Merge "Extract AppFunctionServiceCallback from AppFunctionManagerServiceImpl" into main
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
index 238c028..9eac108 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
@@ -18,6 +18,7 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
+import android.util.Log;
 
 import androidx.test.filters.LargeTest;
 
@@ -47,6 +48,8 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CipherPerfTest {
+    private static final String TAG = "android.libcore.regression.CipherPerfTest";
+
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
     public static Collection getCases() {
@@ -71,6 +74,10 @@
                     }
                     for (int keySize : keySizes) {
                         for (int inputSize : inputSizes) {
+                            Log.i(TAG,
+                                    "param[" + params.size() + "] = " + mode.name() + ", "
+                                            + padding.name() + ", " + keySize + ", " + inputSize
+                                            + ", " + implementation.name());
                             params.add(
                                     new Object[] {
                                         mode, padding, keySize, inputSize, implementation
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 52a761f..31d2ecd 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -34,6 +34,7 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.os.Process;
 import android.system.SystemCleaner;
 import android.util.Log;
 
@@ -638,6 +639,12 @@
      * @hide
      */
     public void enableCleaner() {
+        // JobParameters objects are passed by reference in local Binder
+        // transactions for clients running as SYSTEM. The life cycle of the
+        // JobParameters objects are no longer controlled by the client.
+        if (Process.myUid() == Process.SYSTEM_UID) {
+            return;
+        }
         if (mJobCleanupCallback == null) {
             initCleaner(new JobCleanupCallback(IJobCallback.Stub.asInterface(callback), jobId));
         }
diff --git a/core/api/current.txt b/core/api/current.txt
index 67b3280..4f91361 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6858,43 +6858,43 @@
 
   @FlaggedApi("android.app.api_rich_ongoing") public static class Notification.ProgressStyle extends android.app.Notification.Style {
     ctor public Notification.ProgressStyle();
+    method @NonNull public android.app.Notification.ProgressStyle addProgressPoint(@NonNull android.app.Notification.ProgressStyle.Point);
     method @NonNull public android.app.Notification.ProgressStyle addProgressSegment(@NonNull android.app.Notification.ProgressStyle.Segment);
-    method @NonNull public android.app.Notification.ProgressStyle addProgressStep(@NonNull android.app.Notification.ProgressStyle.Step);
     method public int getProgress();
     method @Nullable public android.graphics.drawable.Icon getProgressEndIcon();
     method public int getProgressMax();
+    method @NonNull public java.util.List<android.app.Notification.ProgressStyle.Point> getProgressPoints();
     method @NonNull public java.util.List<android.app.Notification.ProgressStyle.Segment> getProgressSegments();
     method @Nullable public android.graphics.drawable.Icon getProgressStartIcon();
-    method @NonNull public java.util.List<android.app.Notification.ProgressStyle.Step> getProgressSteps();
     method @Nullable public android.graphics.drawable.Icon getProgressTrackerIcon();
     method public boolean isProgressIndeterminate();
     method public boolean isStyledByProgress();
     method @NonNull public android.app.Notification.ProgressStyle setProgress(int);
     method @NonNull public android.app.Notification.ProgressStyle setProgressEndIcon(@Nullable android.graphics.drawable.Icon);
     method @NonNull public android.app.Notification.ProgressStyle setProgressIndeterminate(boolean);
+    method @NonNull public android.app.Notification.ProgressStyle setProgressPoints(@NonNull java.util.List<android.app.Notification.ProgressStyle.Point>);
     method @NonNull public android.app.Notification.ProgressStyle setProgressSegments(@NonNull java.util.List<android.app.Notification.ProgressStyle.Segment>);
     method @NonNull public android.app.Notification.ProgressStyle setProgressStartIcon(@Nullable android.graphics.drawable.Icon);
-    method @NonNull public android.app.Notification.ProgressStyle setProgressSteps(@NonNull java.util.List<android.app.Notification.ProgressStyle.Step>);
     method @NonNull public android.app.Notification.ProgressStyle setProgressTrackerIcon(@Nullable android.graphics.drawable.Icon);
     method @NonNull public android.app.Notification.ProgressStyle setStyledByProgress(boolean);
   }
 
+  public static final class Notification.ProgressStyle.Point {
+    ctor public Notification.ProgressStyle.Point(int);
+    method @ColorInt public int getColor();
+    method public int getId();
+    method public int getPosition();
+    method @NonNull public android.app.Notification.ProgressStyle.Point setColor(@ColorInt int);
+    method @NonNull public android.app.Notification.ProgressStyle.Point setId(int);
+  }
+
   public static final class Notification.ProgressStyle.Segment {
     ctor public Notification.ProgressStyle.Segment(int);
     method @ColorInt public int getColor();
+    method public int getId();
     method public int getLength();
-    method public int getStableId();
     method @NonNull public android.app.Notification.ProgressStyle.Segment setColor(@ColorInt int);
-    method @NonNull public android.app.Notification.ProgressStyle.Segment setStableId(int);
-  }
-
-  public static final class Notification.ProgressStyle.Step {
-    ctor public Notification.ProgressStyle.Step(int);
-    method @ColorInt public int getColor();
-    method public int getPosition();
-    method public int getStableId();
-    method @NonNull public android.app.Notification.ProgressStyle.Step setColor(@ColorInt int);
-    method @NonNull public android.app.Notification.ProgressStyle.Step setStableId(int);
+    method @NonNull public android.app.Notification.ProgressStyle.Segment setId(int);
   }
 
   public abstract static class Notification.Style {
@@ -8787,7 +8787,7 @@
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @Deprecated @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+    method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
     method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
@@ -8822,13 +8822,12 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
     field public static final String PROPERTY_RETURN_VALUE = "returnValue";
     field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
-    field public static final int RESULT_CANCELLED = 7; // 0x7
+    field public static final int RESULT_CANCELLED = 6; // 0x6
     field public static final int RESULT_DENIED = 1; // 0x1
-    field public static final int RESULT_DISABLED = 6; // 0x6
+    field public static final int RESULT_DISABLED = 5; // 0x5
     field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
     field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
     field public static final int RESULT_OK = 0; // 0x0
-    field public static final int RESULT_TIMED_OUT = 5; // 0x5
   }
 
 }
@@ -12114,6 +12113,7 @@
     method @NonNull public void setResourceValue(@NonNull String, int, @NonNull String, @Nullable String);
     method @NonNull public void setResourceValue(@NonNull String, @NonNull android.os.ParcelFileDescriptor, @Nullable String);
     method @FlaggedApi("android.content.res.asset_file_descriptor_frro") @NonNull public void setResourceValue(@NonNull String, @NonNull android.content.res.AssetFileDescriptor, @Nullable String);
+    method @FlaggedApi("android.content.res.dimension_frro") public void setResourceValue(@NonNull String, float, int, @Nullable String);
     method public void setTargetOverlayable(@Nullable String);
   }
 
@@ -32821,7 +32821,7 @@
     field @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY;
     field @Deprecated public static final String SDK;
     field public static final int SDK_INT;
-    field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int SDK_MINOR_INT;
+    field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int SDK_INT_FULL;
     field public static final String SECURITY_PATCH;
   }
 
@@ -32865,6 +32865,9 @@
     field public static final int VANILLA_ICE_CREAM = 35; // 0x23
   }
 
+  @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL {
+  }
+
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
     ctor public Bundle();
     ctor public Bundle(ClassLoader);
@@ -54956,6 +54959,7 @@
     method public boolean addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public void addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, @Nullable android.os.Handler);
     method public void addAudioDescriptionRequestedChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionRequestedChangeListener);
+    method @FlaggedApi("com.android.graphics.hwui.flags.high_contrast_text_small_text_rect") public void addHighContrastTextStateChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.accessibility.AccessibilityManager.HighContrastTextStateChangeListener);
     method public boolean addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
     method public void addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, @Nullable android.os.Handler);
     method @ColorInt public int getAccessibilityFocusColor();
@@ -54968,12 +54972,14 @@
     method public static boolean isAccessibilityButtonSupported();
     method public boolean isAudioDescriptionRequested();
     method public boolean isEnabled();
+    method @FlaggedApi("com.android.graphics.hwui.flags.high_contrast_text_small_text_rect") public boolean isHighContrastTextEnabled();
     method public boolean isRequestFromAccessibilityTool();
     method public boolean isTouchExplorationEnabled();
     method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
     method public boolean removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
     method public boolean removeAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public boolean removeAudioDescriptionRequestedChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionRequestedChangeListener);
+    method @FlaggedApi("com.android.graphics.hwui.flags.high_contrast_text_small_text_rect") public void removeHighContrastTextStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.HighContrastTextStateChangeListener);
     method public boolean removeTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
     method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     field public static final int FLAG_CONTENT_CONTROLS = 4; // 0x4
@@ -54993,6 +54999,10 @@
     method public void onAudioDescriptionRequestedChanged(boolean);
   }
 
+  @FlaggedApi("com.android.graphics.hwui.flags.high_contrast_text_small_text_rect") public static interface AccessibilityManager.HighContrastTextStateChangeListener {
+    method public void onHighContrastTextStateChanged(boolean);
+  }
+
   public static interface AccessibilityManager.TouchExplorationStateChangeListener {
     method public void onTouchExplorationStateChanged(boolean);
   }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f27dc32..5907af0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -10153,6 +10153,9 @@
         }
 
         p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
+        final int sizePosition = p.dataPosition();
+        // Write size placeholder. With this size we can easily skip it in native.
+        p.writeInt(0);
 
         int numAttributionWithNotesAppOps = notedAppOps.size();
         p.writeInt(numAttributionWithNotesAppOps);
@@ -10169,6 +10172,12 @@
                 }
             }
         }
+
+        final int payloadPosition = p.dataPosition();
+        p.setDataPosition(sizePosition);
+        // Total header size including 4 bytes size itself.
+        p.writeInt(payloadPosition - sizePosition);
+        p.setDataPosition(payloadPosition);
     }
 
     /**
@@ -10182,6 +10191,8 @@
      * @hide
      */
     public static void readAndLogNotedAppops(@NonNull Parcel p) {
+        // Skip size.
+        p.readInt();
         int numAttributionsWithNotedAppOps = p.readInt();
 
         for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index edcdb6c..f34341f 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -34,6 +34,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.WireTypeMismatchException;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -777,7 +778,9 @@
         mStartComponent = other.mStartComponent;
     }
 
-    private ApplicationStartInfo(@NonNull Parcel in) {
+    /** @hide */
+    @VisibleForTesting
+    public ApplicationStartInfo(@NonNull Parcel in) {
         mStartupState = in.readInt();
         mPid = in.readInt();
         mRealUid = in.readInt();
@@ -1061,12 +1064,21 @@
         if (other == null || !(other instanceof ApplicationStartInfo)) {
             return false;
         }
+
         final ApplicationStartInfo o = (ApplicationStartInfo) other;
-        return mPid == o.mPid && mRealUid == o.mRealUid && mPackageUid == o.mPackageUid
-                && mDefiningUid == o.mDefiningUid && mReason == o.mReason
-                && mStartupState == o.mStartupState && mStartType == o.mStartType
-                && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName)
-                && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped
+
+        return mPid == o.mPid
+                && mRealUid == o.mRealUid
+                && mPackageUid == o.mPackageUid
+                && mDefiningUid == o.mDefiningUid
+                && mReason == o.mReason
+                && mStartupState == o.mStartupState
+                && mStartType == o.mStartType
+                && mLaunchMode == o.mLaunchMode
+                && TextUtils.equals(mPackageName, o.mPackageName)
+                && TextUtils.equals(mProcessName, o.mProcessName)
+                && timestampsEquals(o)
+                && mWasForceStopped == o.mWasForceStopped
                 && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs
                 && mStartComponent == o.mStartComponent;
     }
@@ -1074,7 +1086,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState,
-                mStartType, mLaunchMode, mProcessName, mStartupTimestampsNs,
+                mStartType, mLaunchMode, mPackageName, mProcessName, mStartupTimestampsNs,
                 mMonoticCreationTimeMs, mStartComponent);
     }
 
diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl
index cfdb426..1ff7a17 100644
--- a/core/java/android/app/IUserSwitchObserver.aidl
+++ b/core/java/android/app/IUserSwitchObserver.aidl
@@ -19,10 +19,10 @@
 import android.os.IRemoteCallback;
 
 /** {@hide} */
-oneway interface IUserSwitchObserver {
+interface IUserSwitchObserver {
     void onBeforeUserSwitching(int newUserId);
-    void onUserSwitching(int newUserId, IRemoteCallback reply);
-    void onUserSwitchComplete(int newUserId);
-    void onForegroundProfileSwitch(int newProfileId);
-    void onLockedBootComplete(int newUserId);
+    oneway void onUserSwitching(int newUserId, IRemoteCallback reply);
+    oneway void onUserSwitchComplete(int newUserId);
+    oneway void onForegroundProfileSwitch(int newProfileId);
+    oneway void onLockedBootComplete(int newUserId);
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e8b0a36f..0812a13 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1637,16 +1637,16 @@
     public static final String EXTRA_PROGRESS_SEGMENTS = "android.progressSegments";
 
     /**
-     * {@link #extras} key: an arraylist of {@link android.app.Notification.ProgressStyle.Step}
+     * {@link #extras} key: an arraylist of {@link ProgressStyle.Point}
      * bundles provided by a
      * {@link android.app.Notification.ProgressStyle} notification as supplied to
-     * {@link ProgressStyle#setProgressSteps}
-     * or {@link ProgressStyle#addProgressStep(ProgressStyle.Step)}.
+     * {@link ProgressStyle#setProgressPoints}
+     * or {@link ProgressStyle#addProgressPoint(ProgressStyle.Point)}.
      * This extra is a parcelable array list of bundles.
      * @hide
      */
     @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
-    public static final String EXTRA_PROGRESS_STEPS = "android.progressSteps";
+    public static final String EXTRA_PROGRESS_POINTS = "android.progressPoints";
 
     /**
      * {@link #extras} key: whether the progress bar should be styled by its progress as
@@ -11159,7 +11159,7 @@
 
     /**
      * A Notification Style used to to define a notification whose expanded state includes
-     * a highly customizable progress bar with segments, steps, a custom tracker icon,
+     * a highly customizable progress bar with segments, points, a custom tracker icon,
      * and custom icons at the start and end of the progress bar.
      *
      * This style is suggested for use cases where the app is showing a tracker to the
@@ -11185,8 +11185,8 @@
      *       .addProgressSegment(new Segment(552).setColor(Color.YELLOW))
      *       .addProgressSegment(new Segment(253).setColor(Color.YELLOW))
      *       .addProgressSegment(new Segment(94).setColor(Color.BLUE))
-     *       .addProgressStep(new Step(60).setColor(Color.RED))
-     *       .addProgressStep(new Step(560).setColor(Color.YELLOW))
+     *       .addProgressPoint(new Point(60).setColor(Color.RED))
+     *       .addProgressPoint(new Point(560).setColor(Color.YELLOW))
      *   )
      * </pre>
      *
@@ -11199,17 +11199,17 @@
      */
     @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
     public static class ProgressStyle extends Notification.Style {
-        private static final String KEY_ELEMENT_STABLE_ID = "stableId";
+        private static final String KEY_ELEMENT_ID = "id";
         private static final String KEY_ELEMENT_COLOR = "colorInt";
         private static final String KEY_SEGMENT_LENGTH = "length";
-        private static final String KEY_STEP_POSITION = "position";
+        private static final String KEY_POINT_POSITION = "position";
 
         private static final int MAX_PROGRESS_SEGMENT_LIMIT = 15;
-        private static final int MAX_PROGRESS_STEP_LIMIT = 5;
+        private static final int MAX_PROGRESS_STOP_LIMIT = 5;
         private static final int DEFAULT_PROGRESS_MAX = 100;
 
         private List<Segment> mProgressSegments = new ArrayList<>();
-        private List<Step> mProgressSteps = new ArrayList<>();
+        private List<Point> mProgressPoints = new ArrayList<>();
 
         private int mProgress = 0;
 
@@ -11246,7 +11246,7 @@
                 nonIndeterminateCheckResult = !Objects.equals(mProgress, progressStyle.mProgress)
                         || !Objects.equals(mIsStyledByProgress, progressStyle.mIsStyledByProgress)
                         || !Objects.equals(mProgressSegments, progressStyle.mProgressSegments)
-                        || !Objects.equals(mProgressSteps, progressStyle.mProgressSteps)
+                        || !Objects.equals(mProgressPoints, progressStyle.mProgressPoints)
                         || !Objects.equals(mTrackerIcon, progressStyle.mTrackerIcon);
             }
 
@@ -11300,48 +11300,47 @@
         }
 
         /**
-         * Gets the steps that are displayed on the progress bar.
+         * Gets the points that are displayed on the progress bar.
          *.
-         * @see #setProgressSteps
-         * @see #addProgressStep
-         * @see Step
+         * @see #setProgressPoints
+         * @see #addProgressPoint
+         * @see Point
          */
-        public @NonNull List<Step> getProgressSteps() {
-            return mProgressSteps;
+        public @NonNull List<Point> getProgressPoints() {
+            return mProgressPoints;
         }
 
         /**
-         * Replaces all the progress steps.
+         * Replaces all the progress points.
          *
-         * Steps are designated points within a progressbar to visualize
-         * distinct stages or milestones.
-         * For example, you might use steps to mark stops in a multi-stop
-         * navigation journey, where each step represents a destination.
-         * @see Step
+         * Points within a progress bar are used to visualize distinct stages or milestones.
+         * For example, you might use points to mark stops in a multi-stop
+         * navigation journey, where each point represents a destination.
+         * @see Point
          */
-        public @NonNull ProgressStyle setProgressSteps(@NonNull List<Step> steps) {
-            mProgressSteps = new ArrayList<>(steps);
+        public @NonNull ProgressStyle setProgressPoints(@NonNull List<Point> points) {
+            mProgressPoints = new ArrayList<>(points);
             return this;
         }
 
         /**
-         * Adds another step.
+         * Adds another point.
          *
-         * Steps are designated points within a progressbar to visualize
-         * distinct stages or milestones.
-         * For example, you might use steps to mark stops in a multi-stop
-         * navigation journey, where each step represents a destination.
+         * Points within a progress bar are used to visualize distinct stages or milestones.
          *
-         * Steps can be added in any order, as their
+         * For example, you might use points to mark stops in a multi-stop
+         * navigation journey, where each point represents a destination.
+         *
+         * Points can be added in any order, as their
          * position within the progress bar is determined by their individual
-         * {@link Step#getPosition()}.
-         * @see Step
+         * {@link Point#getPosition()}.
+         * @see Point
          */
-        public @NonNull ProgressStyle addProgressStep(@NonNull Step step) {
-            if (mProgressSteps == null) {
-                mProgressSteps = new ArrayList<>();
+        public @NonNull ProgressStyle addProgressPoint(@NonNull Point point) {
+            if (mProgressPoints == null) {
+                mProgressPoints = new ArrayList<>();
             }
-            mProgressSteps.add(step);
+            mProgressPoints.add(point);
 
             return this;
         }
@@ -11414,7 +11413,7 @@
          * When specified, the following fields are ignored:
          * @see #setProgress
          * @see #setProgressSegments
-         * @see #setProgressSteps
+         * @see #setProgressPoints
          * @see #setProgressTrackerIcon
          * @see #setStyledByProgress
          *
@@ -11435,7 +11434,7 @@
         }
 
         /**
-         * Indicates whether the segments and steps will be styled differently
+         * Indicates whether the segments and points will be styled differently
          * based on whether they are behind or ahead of the current progress.
          * When true, segments appearing ahead of the current progress will be given a
          * slightly different appearance to indicate that it is part of the progress bar
@@ -11558,8 +11557,8 @@
             super.addExtras(extras);
             extras.putParcelableArrayList(EXTRA_PROGRESS_SEGMENTS,
                     getProgressSegmentsAsBundleList(mProgressSegments));
-            extras.putParcelableArrayList(EXTRA_PROGRESS_STEPS,
-                    getProgressStepsAsBundleList(mProgressSteps));
+            extras.putParcelableArrayList(EXTRA_PROGRESS_POINTS,
+                    getProgressPointsAsBundleList(mProgressPoints));
 
             extras.putInt(EXTRA_PROGRESS, mProgress);
             extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mIndeterminate);
@@ -11599,8 +11598,8 @@
             mTrackerIcon = extras.getParcelable(EXTRA_PROGRESS_TRACKER_ICON, Icon.class);
             mStartIcon = extras.getParcelable(EXTRA_PROGRESS_START_ICON, Icon.class);
             mEndIcon = extras.getParcelable(EXTRA_PROGRESS_END_ICON, Icon.class);
-            mProgressSteps = getProgressStepsFromBundleList(
-                    extras.getParcelableArrayList(EXTRA_PROGRESS_STEPS, Bundle.class));
+            mProgressPoints = getProgressPointsFromBundleList(
+                    extras.getParcelableArrayList(EXTRA_PROGRESS_POINTS, Bundle.class));
         }
 
         /**
@@ -11613,6 +11612,30 @@
             // actually be included.
             return true;
         }
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeContentView(boolean increasedHeight) {
+            final StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
+                    .hideProgress(true)
+                    .fillTextsFrom(mBuilder);
+
+            return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */);
+        }
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
+            final StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
+                    .hideProgress(true)
+                    .fillTextsFrom(mBuilder);
+
+            return getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), p, null /* result */);
+        }
 
         private static @NonNull ArrayList<Bundle> getProgressSegmentsAsBundleList(
                 @Nullable List<Segment> progressSegments) {
@@ -11626,7 +11649,7 @@
 
                     final Bundle bundle = new Bundle();
                     bundle.putInt(KEY_SEGMENT_LENGTH, segment.getLength());
-                    bundle.putInt(KEY_ELEMENT_STABLE_ID, segment.getStableId());
+                    bundle.putInt(KEY_ELEMENT_ID, segment.getId());
                     bundle.putInt(KEY_ELEMENT_COLOR, segment.getColor());
 
                     segments.add(bundle);
@@ -11647,11 +11670,11 @@
                         continue;
                     }
 
-                    final int stableId = segmentBundle.getInt(KEY_ELEMENT_STABLE_ID);
+                    final int id = segmentBundle.getInt(KEY_ELEMENT_ID);
                     final int color = segmentBundle.getInt(KEY_ELEMENT_COLOR,
                             Notification.COLOR_DEFAULT);
                     final Segment segment = new Segment(length)
-                            .setStableId(stableId).setColor(color);
+                            .setId(id).setColor(color);
 
                     segments.add(segment);
                 }
@@ -11660,48 +11683,48 @@
             return segments;
         }
 
-        private static @NonNull ArrayList<Bundle> getProgressStepsAsBundleList(
-                @Nullable List<Step> progressSteps) {
-            final ArrayList<Bundle> steps = new ArrayList<>();
-            if (progressSteps != null && !progressSteps.isEmpty()) {
-                for (int i = 0; i < progressSteps.size(); i++) {
-                    final Step step = progressSteps.get(i);
-                    if (step.getPosition() < 0) {
+        private static @NonNull ArrayList<Bundle> getProgressPointsAsBundleList(
+                @Nullable List<Point> progressPoints) {
+            final ArrayList<Bundle> points = new ArrayList<>();
+            if (progressPoints != null && !progressPoints.isEmpty()) {
+                for (int i = 0; i < progressPoints.size(); i++) {
+                    final Point point = progressPoints.get(i);
+                    if (point.getPosition() < 0) {
                         continue;
                     }
 
                     final Bundle bundle = new Bundle();
-                    bundle.putInt(KEY_STEP_POSITION, step.getPosition());
-                    bundle.putInt(KEY_ELEMENT_STABLE_ID, step.getStableId());
-                    bundle.putInt(KEY_ELEMENT_COLOR, step.getColor());
+                    bundle.putInt(KEY_POINT_POSITION, point.getPosition());
+                    bundle.putInt(KEY_ELEMENT_ID, point.getId());
+                    bundle.putInt(KEY_ELEMENT_COLOR, point.getColor());
 
-                    steps.add(bundle);
+                    points.add(bundle);
                 }
             }
 
-            return steps;
+            return points;
         }
 
-        private static @NonNull List<Step> getProgressStepsFromBundleList(
-                @Nullable List<Bundle> stepBundleList) {
-            final ArrayList<Step> steps = new ArrayList<>();
+        private static @NonNull List<Point> getProgressPointsFromBundleList(
+                @Nullable List<Bundle> pointBundleList) {
+            final ArrayList<Point> points = new ArrayList<>();
 
-            if (stepBundleList != null && !stepBundleList.isEmpty()) {
-                for (int i = 0; i < stepBundleList.size(); i++) {
-                    final Bundle segmentBundle = stepBundleList.get(i);
-                    final int position = segmentBundle.getInt(KEY_STEP_POSITION);
+            if (pointBundleList != null && !pointBundleList.isEmpty()) {
+                for (int i = 0; i < pointBundleList.size(); i++) {
+                    final Bundle pointBundle = pointBundleList.get(i);
+                    final int position = pointBundle.getInt(KEY_POINT_POSITION);
                     if (position < 0) {
                         continue;
                     }
-                    final int stableId = segmentBundle.getInt(KEY_ELEMENT_STABLE_ID);
-                    final int color = segmentBundle.getInt(KEY_ELEMENT_COLOR,
+                    final int id = pointBundle.getInt(KEY_ELEMENT_ID);
+                    final int color = pointBundle.getInt(KEY_ELEMENT_COLOR,
                             Notification.COLOR_DEFAULT);
-                    final Step step = new Step(position).setStableId(stableId).setColor(color);
-                    steps.add(step);
+                    final Point point = new Point(position).setId(id).setColor(color);
+                    points.add(point);
                 }
             }
 
-            return steps;
+            return points;
         }
 
         /**
@@ -11712,7 +11735,7 @@
          */
         public static final class Segment {
             private int mLength;
-            private int mStableId = 0;
+            private int mId = 0;
             @ColorInt
             private int mColor = Notification.COLOR_DEFAULT;
 
@@ -11735,19 +11758,19 @@
             }
 
             /**
-             * Gets the stable id of this Segment.
+             * Gets the id of this Segment.
              *
-             * @see #setStableId
+             * @see #setId
              */
-            public int getStableId() {
-                return mStableId;
+            public int getId() {
+                return mId;
             }
 
             /**
              * Optional ID used to uniquely identify the element across updates.
              */
-            public @NonNull Segment setStableId(int stableId) {
-                mStableId = stableId;
+            public @NonNull Segment setId(int id) {
+                mId = id;
                 return this;
             }
 
@@ -11776,45 +11799,44 @@
             public boolean equals(Object o) {
                 if (this == o) return true;
                 if (o == null || getClass() != o.getClass()) return false;
-                Segment segment = (Segment) o;
-                return mLength == segment.mLength && mStableId == segment.mStableId
+                final Segment segment = (Segment) o;
+                return mLength == segment.mLength && mId == segment.mId
                         && mColor == segment.mColor;
             }
 
             @Override
             public int hashCode() {
-                return Objects.hash(mLength, mStableId, mColor);
+                return Objects.hash(mLength, mId, mColor);
             }
         }
 
         /**
-         * A step within the progress bar, defining its position and color.
-         * Steps are designated points within a progressbar to visualize
-         * distinct stages or milestones.
-         * For example, you might use steps to mark stops in a multi-stop
-         * navigation journey, where each step represents a destination.
+         * A point within the progress bar, defining its position and color.
+         * Points within a progress bar are used to visualize distinct stages or milestones.
+         * For example, you might use points to mark stops in a multi-stop
+         * navigation journey, where each point represents a destination.
          */
-        public static final class Step {
+        public static final class Point {
 
             private int mPosition;
-            private int mStableId;
+            private int mId;
             @ColorInt
             private int mColor = Notification.COLOR_DEFAULT;
 
             /**
-             * Create a step element.
-             * The position of this step on the progress bar
+             * Create a point element.
+             * The position of this point on the progress bar
              * relative to {@link ProgressStyle#getProgressMax}
              * @param position
              * See {@link #getPosition}
              */
-            public Step(int position) {
+            public Point(int position) {
                 mPosition = position;
             }
 
             /**
-             * Gets the position of this Step.
-             * The position of this step on the progress bar
+             * Gets the position of this Point.
+             * The position of this point on the progress bar
              * relative to {@link ProgressStyle#getProgressMax}.
              */
             public int getPosition() {
@@ -11823,17 +11845,17 @@
 
 
             /**
-             * Optional ID used to uniqurely identify the element across updates.
+             * Optional ID used to uniquely identify the element across updates.
              */
-            public int getStableId() {
-                return mStableId;
+            public int getId() {
+                return mId;
             }
 
             /**
-             * Optional ID used to uniqurely identify the element across updates.
+             * Optional ID used to uniquely identify the element across updates.
              */
-            public @NonNull Step setStableId(int stableId) {
-                mStableId = stableId;
+            public @NonNull Point setId(int id) {
+                mId = id;
                 return this;
             }
 
@@ -11850,7 +11872,7 @@
             /**
              * Optional color of this Segment
              */
-            public @NonNull Step setColor(@ColorInt int color) {
+            public @NonNull Point setColor(@ColorInt int color) {
                 mColor = color;
                 return this;
             }
@@ -11862,14 +11884,14 @@
             public boolean equals(Object o) {
                 if (this == o) return true;
                 if (o == null || getClass() != o.getClass()) return false;
-                Step step = (Step) o;
-                return mPosition == step.mPosition && mStableId == step.mStableId
-                        && mColor == step.mColor;
+                final Point point = (Point) o;
+                return mPosition == point.mPosition && mId == point.mId
+                        && mColor == point.mColor;
             }
 
             @Override
             public int hashCode() {
-                return Objects.hash(mPosition, mStableId, mColor);
+                return Objects.hash(mPosition, mId, mColor);
             }
         }
     }
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 8e41773..7a68a65 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -35,6 +35,7 @@
 import android.os.CancellationSignal;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.util.Log;
 
 import java.util.function.Consumer;
 
@@ -166,9 +167,13 @@
      */
     @MainThread
     @Deprecated
-    public abstract void onExecuteFunction(
+    public void onExecuteFunction(
             @NonNull ExecuteAppFunctionRequest request,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+        Log.w(
+                "AppFunctionService",
+                "Calling deprecated default implementation of onExecuteFunction");
+    }
 
     /**
      * Called by the system to execute a specific app function.
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index 2851e92..a879b1b 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -96,17 +96,14 @@
      */
     public static final int RESULT_INVALID_ARGUMENT = 4;
 
-    /** The operation was timed out. */
-    public static final int RESULT_TIMED_OUT = 5;
-
     /** The caller tried to execute a disabled app function. */
-    public static final int RESULT_DISABLED = 6;
+    public static final int RESULT_DISABLED = 5;
 
     /**
      * The operation was cancelled. Use this error code to report that a cancellation is done after
      * receiving a cancellation signal.
      */
-    public static final int RESULT_CANCELLED = 7;
+    public static final int RESULT_CANCELLED = 6;
 
     /** The result code of the app function execution. */
     @ResultCode private final int mResultCode;
@@ -282,7 +279,6 @@
                 RESULT_APP_UNKNOWN_ERROR,
                 RESULT_INTERNAL_ERROR,
                 RESULT_INVALID_ARGUMENT,
-                RESULT_TIMED_OUT,
                 RESULT_DISABLED,
                 RESULT_CANCELLED
             })
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 8916ce2..6fe0a73 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -84,11 +84,16 @@
     int getDevicePolicy(int policyType);
 
     /**
-    * Returns whether the device has a valid microphone.
-    */
+     * Returns whether the device has a valid microphone.
+     */
     boolean hasCustomAudioInputSupport();
 
     /**
+     * Returns whether this device is allowed to create mirror displays.
+     */
+    boolean canCreateMirrorDisplays();
+
+    /**
      * Closes the virtual device and frees all associated resources.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index e9fa3e1..9eb6d56 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -132,8 +132,16 @@
 }
 
 flag {
-  namespace: "virtual_devices"
-  name: "camera_timestamp_from_surface"
-  description: "Pass the surface timestamp to the capture result"
-  bug: "351341245"
+    namespace: "virtual_devices"
+    name: "camera_timestamp_from_surface"
+    description: "Pass the surface timestamp to the capture result"
+    bug: "351341245"
+}
+
+flag {
+    namespace: "virtual_devices"
+    name: "enable_limited_vdm_role"
+    description: "New VDM role without trusted displays or input"
+    bug: "370657575"
+    is_exported: true
 }
diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java
index 40ffb0f..64e9c33 100644
--- a/core/java/android/content/om/FabricatedOverlay.java
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -476,6 +476,20 @@
         return entry;
     }
 
+    @NonNull
+    private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry(
+            @NonNull String resourceName, float dimensionValue,
+            @TypedValue.ComplexDimensionUnit int dimensionUnit, @Nullable String configuration) {
+        final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+        entry.resourceName = resourceName;
+        entry.dataType = TypedValue.TYPE_DIMENSION;
+        Preconditions.checkArgumentInRange(dimensionUnit,
+                TypedValue.COMPLEX_UNIT_PX, TypedValue.COMPLEX_UNIT_MM, "dimensionUnit");
+        entry.data = TypedValue.createComplexDimension(dimensionValue, dimensionUnit);
+        entry.configuration = configuration;
+        return entry;
+    }
+
     /**
      * Sets the resource value in the fabricated overlay for the integer-like types with the
      * configuration.
@@ -586,4 +600,25 @@
         mOverlay.entries.add(
                 generateFabricatedOverlayInternalEntry(resourceName, value, configuration));
     }
+
+    /**
+     * Sets the resource value in the fabricated overlay for the dimension type with the
+     * configuration.
+     *
+     * @param resourceName name of the target resource to overlay (in the form
+     *     [package]:type/entry)
+     * @param dimensionValue the float representing the dimension value
+     * @param dimensionUnit the integer representing the dimension unit
+     * @param configuration The string representation of the config this overlay is enabled for
+     */
+    @FlaggedApi(android.content.res.Flags.FLAG_DIMENSION_FRRO)
+    public void setResourceValue(
+            @NonNull String resourceName,
+            float dimensionValue,
+            @TypedValue.ComplexDimensionUnit int dimensionUnit,
+            @Nullable String configuration) {
+        ensureValidResourceName(resourceName);
+        mOverlay.entries.add(generateFabricatedOverlayInternalEntry(resourceName, dimensionValue,
+                dimensionUnit, configuration));
+    }
 }
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index fd1a896..d5edc92 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -68,6 +68,13 @@
 }
 
 flag {
+    name: "multiuser_widget"
+    namespace: "multiuser"
+    description: "Implement the Multiuser Widget"
+    bug: "365748524"
+}
+
+flag {
     name: "enable_biometrics_to_unlock_private_space"
     is_exported: true
     namespace: "profile_experiences"
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index a5f8199..0af2f25 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -66,3 +66,11 @@
     # This flag is read at boot time.
     is_fixed_read_only: true
 }
+
+flag {
+    name: "dimension_frro"
+    is_exported: true
+    namespace: "resource_manager"
+    description: "Feature flag for passing a dimension to create an frro"
+    bug: "369672322"
+}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index b11961c..e3fdd26 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -139,6 +139,13 @@
     public static final int DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS = 8;
 
     /**
+     * Dialog dismissal due to the system being unable to retrieve a WindowManager instance required
+     * to show the dialog.
+     * @hide
+     */
+    public static final int DISMISSED_REASON_ERROR_NO_WM = 9;
+
+    /**
      * @hide
      */
     @IntDef({DISMISSED_REASON_BIOMETRIC_CONFIRMED,
@@ -148,7 +155,8 @@
             DISMISSED_REASON_ERROR,
             DISMISSED_REASON_SERVER_REQUESTED,
             DISMISSED_REASON_CREDENTIAL_CONFIRMED,
-            DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS})
+            DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS,
+            DISMISSED_REASON_ERROR_NO_WM})
     @Retention(RetentionPolicy.SOURCE)
     public @interface DismissedReason {}
 
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 7185719..6affd12 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -792,7 +792,6 @@
     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
         try {
             mDm.setVirtualDisplaySurface(token, surface);
-            setVirtualDisplayState(token, surface != null);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -815,14 +814,6 @@
         }
     }
 
-    void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {
-        try {
-            mDm.setVirtualDisplayState(token, isOn);
-        } catch (RemoteException ex) {
-            throw ex.rethrowFromSystemServer();
-        }
-    }
-
     void setVirtualDisplayRotation(IVirtualDisplayCallback token, @Surface.Rotation int rotation) {
         try {
             mDm.setVirtualDisplayRotation(token, rotation);
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index e598097..36e816a 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -455,6 +455,11 @@
     public abstract void onPresentation(int displayId, boolean isShown);
 
     /**
+     * Called upon the usage of stylus.
+     */
+    public abstract void stylusGestureStarted(long eventTime);
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index aa1539f6..b612bca 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -115,9 +115,6 @@
     void releaseVirtualDisplay(in IVirtualDisplayCallback token);
 
     // No permissions required but must be same Uid as the creator.
-    void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn);
-
-    // No permissions required but must be same Uid as the creator.
     void setVirtualDisplayRotation(in IVirtualDisplayCallback token, int rotation);
 
     // Get a stable metric for the device's display size. No permissions required.
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 6cc938f..32b6405 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -112,18 +112,6 @@
     }
 
     /**
-     * Sets the on/off state for a virtual display.
-     *
-     * @param isOn Whether the display should be on or off.
-     * @hide
-     */
-    public void setDisplayState(boolean isOn) {
-        if (mToken != null) {
-            mGlobal.setVirtualDisplayState(mToken, isOn);
-        }
-    }
-
-    /**
      * Sets the rotation of the virtual display.
      *
      * @param rotation the new rotation of the display. May be one of {@link Surface#ROTATION_0},
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index f546910..1fc91ee 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -17,9 +17,11 @@
 package android.net.vcn;
 
 import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS;
 import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.NetworkCapabilities;
@@ -29,6 +31,8 @@
 import android.os.Parcelable;
 import android.telephony.SubscriptionManager;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.Objects;
 
 /**
@@ -47,6 +51,7 @@
  *
  * @hide
  */
+// TODO: Do not store WifiInfo and subscription ID in VcnTransportInfo anymore
 public class VcnTransportInfo implements TransportInfo, Parcelable {
     @Nullable private final WifiInfo mWifiInfo;
     private final int mSubId;
@@ -195,4 +200,42 @@
                     return new VcnTransportInfo[size];
                 }
             };
+
+    /** This class can be used to construct a {@link VcnTransportInfo}. */
+    public static final class Builder {
+        private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
+
+        /** Construct Builder */
+        public Builder() {}
+
+        /**
+         * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout.
+         *
+         * <p>This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs,
+         * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data.
+         *
+         * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN
+         *     Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN
+         *     Gateway.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        public Builder setMinUdpPort4500NatTimeoutSeconds(
+                @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS)
+                        int minUdpPort4500NatTimeoutSeconds) {
+            Preconditions.checkArgument(
+                    minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
+                    "Timeout must be at least 120s");
+
+            mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
+            return Builder.this;
+        }
+
+        /** Build a VcnTransportInfo instance */
+        @NonNull
+        public VcnTransportInfo build() {
+            return new VcnTransportInfo(
+                    null /* wifiInfo */, INVALID_SUBSCRIPTION_ID, mMinUdpPort4500NatTimeoutSeconds);
+        }
+    }
 }
diff --git a/core/java/android/net/vcn/VcnUtils.java b/core/java/android/net/vcn/VcnUtils.java
new file mode 100644
index 0000000..6dc5180
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUtils.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.TransportInfo;
+import android.net.wifi.WifiInfo;
+
+import java.util.List;
+
+/**
+ * Utility class for VCN callers get information from VCN network
+ *
+ * @hide
+ */
+public class VcnUtils {
+    /** Get the WifiInfo of the VCN's underlying WiFi network */
+    @Nullable
+    public static WifiInfo getWifiInfoFromVcnCaps(
+            @NonNull ConnectivityManager connectivityMgr,
+            @NonNull NetworkCapabilities networkCapabilities) {
+        final NetworkCapabilities underlyingCaps =
+                getVcnUnderlyingCaps(connectivityMgr, networkCapabilities);
+
+        if (underlyingCaps == null) {
+            return null;
+        }
+
+        final TransportInfo underlyingTransportInfo = underlyingCaps.getTransportInfo();
+        if (!(underlyingTransportInfo instanceof WifiInfo)) {
+            return null;
+        }
+
+        return (WifiInfo) underlyingTransportInfo;
+    }
+
+    /** Get the subscription ID of the VCN's underlying Cell network */
+    public static int getSubIdFromVcnCaps(
+            @NonNull ConnectivityManager connectivityMgr,
+            @NonNull NetworkCapabilities networkCapabilities) {
+        final NetworkCapabilities underlyingCaps =
+                getVcnUnderlyingCaps(connectivityMgr, networkCapabilities);
+
+        if (underlyingCaps == null) {
+            return INVALID_SUBSCRIPTION_ID;
+        }
+
+        final NetworkSpecifier underlyingNetworkSpecifier = underlyingCaps.getNetworkSpecifier();
+        if (!(underlyingNetworkSpecifier instanceof TelephonyNetworkSpecifier)) {
+            return INVALID_SUBSCRIPTION_ID;
+        }
+
+        return ((TelephonyNetworkSpecifier) underlyingNetworkSpecifier).getSubscriptionId();
+    }
+
+    @Nullable
+    private static NetworkCapabilities getVcnUnderlyingCaps(
+            @NonNull ConnectivityManager connectivityMgr,
+            @NonNull NetworkCapabilities networkCapabilities) {
+        // Return null if it is not a VCN network
+        if (networkCapabilities.getTransportInfo() == null
+                || !(networkCapabilities.getTransportInfo() instanceof VcnTransportInfo)) {
+            return null;
+        }
+
+        // As of Android 16, VCN has one underlying network, and only one. If there are more
+        // than one networks due to future changes in the VCN mainline code, just take the first
+        // network
+        final List<Network> underlyingNws = networkCapabilities.getUnderlyingNetworks();
+        if (underlyingNws == null) {
+            return null;
+        }
+
+        return connectivityMgr.getNetworkCapabilities(underlyingNws.get(0));
+    }
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b9b5295..c41e626 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3088,8 +3088,9 @@
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
             "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
-            "Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa", "Etw",
-            "Esw", "Ewa", "Elw", "Eec", "Esc", "Eds"
+            "Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa",
+            "Etw", "Esw", "Ewa", "Elw", "Esc",
+            "Eds"
     };
 
     @FunctionalInterface
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a8267d1..479aa98 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
@@ -401,33 +402,42 @@
          * device. This value never changes while a device is booted, but it may
          * increase when the hardware manufacturer provides an OTA update.
          * <p>
-         * Together with {@link SDK_MINOR_INT}, this constant defines the
-         * <pre>major.minor</pre> version of Android. <pre>SDK_INT</pre> is
-         * increased and <pre>SDK_MINOR_INT</pre> is set to 0 on new Android
-         * dessert releases. Between these, Android may also release so called
-         * minor releases where <pre>SDK_INT</pre> remains unchanged and
-         * <pre>SDK_MINOR_INT</pre> is increased. Minor releases can add new
-         * APIs, and have stricter guarantees around backwards compatibility
-         * (e.g. no changes gated by <pre>targetSdkVersion</pre>) compared to
-         * major releases.
+         * This constant records the major version of Android. Use {@link
+         * SDK_INT_FULL} if you need to consider the minor version of Android
+         * as well.
          * <p>
          * Possible values are defined in {@link Build.VERSION_CODES}.
+         * @see #SDK_INT_FULL
          */
         public static final int SDK_INT = SystemProperties.getInt(
                 "ro.build.version.sdk", 0);
 
         /**
-         * The minor SDK version of the software currently running on this hardware
-         * device. This value never changes while a device is booted, but it may
-         * increase when the hardware manufacturer provides an OTA update.
+         * The major and minor SDK version of the software currently running on
+         * this hardware device. This value never changes while a device is
+         * booted, but it may increase when the hardware manufacturer provides
+         * an OTA update.
          * <p>
-         * Together with {@link SDK_INT}, this constant defines the
-         * <pre>major.minor</pre> version of Android. See {@link SDK_INT} for
-         * more information.
+         * <code>SDK_INT</code> is increased on new Android dessert releases,
+         * also called major releases. Between these, Android may also release
+         * minor releases where <code>SDK_INT</code> remains unchanged. Minor
+         * releases can add new APIs, and have stricter guarantees around
+         * backwards compatibility (e.g. no changes gated by
+         * <code>targetSdkVersion</code>) compared to major releases.
+         * <p>
+         * <code>SDK_INT_FULL</code> is increased on every release.
+         * <p>
+         * Possible values are defined in {@link
+         * android.os.Build.VERSION_CODES_FULL}.
          */
         @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
-        public static final int SDK_MINOR_INT = SystemProperties.getInt(
-                "ro.build.version.sdk_minor", 0);
+        public static final int SDK_INT_FULL;
+
+        static {
+            SDK_INT_FULL = VERSION_CODES_FULL.SDK_INT_MULTIPLIER
+                * SystemProperties.getInt("ro.build.version.sdk", 0)
+                + SystemProperties.getInt("ro.build.version.sdk_minor", 0);
+        }
 
         /**
          * The SDK version of the software that <em>initially</em> shipped on
@@ -1264,6 +1274,25 @@
     }
 
     /**
+     * Enumeration of the currently known SDK major and minor version codes.
+     * The numbers increase for every release, and are guaranteed to be ordered
+     * by the release date of each release. The actual values should be
+     * considered an implementation detail, and the current encoding scheme may
+     * change in the future.
+     *
+     * @see android.os.Build.VERSION#SDK_INT_FULL
+     */
+    @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+    @SuppressLint("AcronymName")
+    public static class VERSION_CODES_FULL {
+        private VERSION_CODES_FULL() {}
+
+        // Use the last 5 digits for the minor version. This allows the
+        // minor version to be set to CUR_DEVELOPMENT.
+        private static final int SDK_INT_MULTIPLIER = 100000;
+    }
+
+    /**
      * The vendor API for 2024 Q2
      *
      * <p>For Android 14-QPR3 and later, the vendor API level is completely decoupled from the SDK
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index beb9a93..2d3dd1b 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -598,6 +598,11 @@
         final String abi = chooseAbi(angleInfo);
 
         // Build a path that includes installed native libs and APK
+        // TODO (b/370113081): If the native libraries are not found in this path,
+        // the system libraries will be loaded instead.
+        // This can happen if the ANGLE APK is present,
+        // but accidentally packaged without native libraries.
+        // TBD if this should fail instead of falling back to the system version.
         final String paths = angleInfo.nativeLibraryDir
                 + File.pathSeparator
                 + angleInfo.sourceDir
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 2cb86f7..769cbdd 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -92,9 +92,9 @@
     /**
      * Add a new callback to the list.  This callback will remain in the list
      * until a corresponding call to {@link #unregister} or its hosting process
-     * goes away.  If the callback was already registered (determined by
+     * goes away. If the callback was already registered (determined by
      * checking to see if the {@link IInterface#asBinder callback.asBinder()}
-     * object is already in the list), then it will be left as-is.
+     * object is already in the list), then it will be replaced with the new callback.
      * Registrations are not counted; a single call to {@link #unregister}
      * will remove a callback after any number calls to register it.
      *
@@ -106,7 +106,7 @@
      *
      * @param cookie Optional additional data to be associated with this
      * callback.
-     * 
+     *
      * @return Returns true if the callback was successfully added to the list.
      * Returns false if it was not added, either because {@link #kill} had
      * previously been called or the callback's process has gone away.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d82af55..a2c41c1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2092,23 +2092,6 @@
     public static final String ACTION_ZEN_MODE_SETTINGS = "android.settings.ZEN_MODE_SETTINGS";
 
     /**
-     * Activity Action: Show Zen Mode visual effects configuration settings.
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ZEN_MODE_BLOCKED_EFFECTS_SETTINGS =
-            "android.settings.ZEN_MODE_BLOCKED_EFFECTS_SETTINGS";
-
-    /**
-     * Activity Action: Show Zen Mode onboarding activity.
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ZEN_MODE_ONBOARDING = "android.settings.ZEN_MODE_ONBOARDING";
-
-    /**
      * Activity Action: Show Zen Mode (aka Do Not Disturb) priority configuration settings.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -8748,35 +8731,6 @@
         /** @hide */ public static final int ZEN_DURATION_FOREVER = 0;
 
         /**
-         * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
-         * @hide
-         */
-        @Readable
-        public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
-
-        /**
-         * If nonzero, will show the zen update settings suggestion.
-         * @hide
-         */
-        @Readable
-        public static final String SHOW_ZEN_SETTINGS_SUGGESTION = "show_zen_settings_suggestion";
-
-        /**
-         * If nonzero, zen has not been updated to reflect new changes.
-         * @hide
-         */
-        @Readable
-        public static final String ZEN_SETTINGS_UPDATED = "zen_settings_updated";
-
-        /**
-         * If nonzero, zen setting suggestion has been viewed by user
-         * @hide
-         */
-        @Readable
-        public static final String ZEN_SETTINGS_SUGGESTION_VIEWED =
-                "zen_settings_suggestion_viewed";
-
-        /**
          * Whether the in call notification is enabled to play sound during calls.  The value is
          * boolean (1 or 0).
          * @hide
@@ -18072,10 +18026,6 @@
             MOVED_TO_SECURE = new HashSet<>(8);
             MOVED_TO_SECURE.add(Global.INSTALL_NON_MARKET_APPS);
             MOVED_TO_SECURE.add(Global.ZEN_DURATION);
-            MOVED_TO_SECURE.add(Global.SHOW_ZEN_UPGRADE_NOTIFICATION);
-            MOVED_TO_SECURE.add(Global.SHOW_ZEN_SETTINGS_SUGGESTION);
-            MOVED_TO_SECURE.add(Global.ZEN_SETTINGS_UPDATED);
-            MOVED_TO_SECURE.add(Global.ZEN_SETTINGS_SUGGESTION_VIEWED);
             MOVED_TO_SECURE.add(Global.CHARGING_SOUNDS_ENABLED);
             MOVED_TO_SECURE.add(Global.CHARGING_VIBRATION_ENABLED);
             MOVED_TO_SECURE.add(Global.NOTIFICATION_BUBBLES);
@@ -18910,40 +18860,6 @@
         @Readable
         public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
 
-
-        /**
-         * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
-         * @hide
-         * @deprecated - Use {@link android.provider.Settings.Secure#SHOW_ZEN_UPGRADE_NOTIFICATION}
-         */
-        @Deprecated
-        public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
-
-        /**
-         * If nonzero, will show the zen update settings suggestion.
-         * @hide
-         * @deprecated - Use {@link android.provider.Settings.Secure#SHOW_ZEN_SETTINGS_SUGGESTION}
-         */
-        @Deprecated
-        public static final String SHOW_ZEN_SETTINGS_SUGGESTION = "show_zen_settings_suggestion";
-
-        /**
-         * If nonzero, zen has not been updated to reflect new changes.
-         * @deprecated - Use {@link android.provider.Settings.Secure#ZEN_SETTINGS_UPDATED}
-         * @hide
-         */
-        @Deprecated
-        public static final String ZEN_SETTINGS_UPDATED = "zen_settings_updated";
-
-        /**
-         * If nonzero, zen setting suggestion has been viewed by user
-         * @hide
-         * @deprecated - Use {@link android.provider.Settings.Secure#ZEN_SETTINGS_SUGGESTION_VIEWED}
-         */
-        @Deprecated
-        public static final String ZEN_SETTINGS_SUGGESTION_VIEWED =
-                "zen_settings_suggestion_viewed";
-
         /**
          * Backup and restore agent timeout parameters.
          * These parameters are represented by a comma-delimited key-value list.
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index a9c5790..c9f4647 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -25,6 +25,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.LinkedHashMap;
 import java.util.Objects;
 import java.util.Set;
 
@@ -63,6 +64,7 @@
     public static class FieldDiff<T> {
         private final T mFrom;
         private final T mTo;
+        private final BaseDiff mDetailedDiff;
 
         /**
          * Constructor to create a FieldDiff object with the given values.
@@ -72,6 +74,19 @@
         public FieldDiff(@Nullable T from, @Nullable T to) {
             mFrom = from;
             mTo = to;
+            mDetailedDiff = null;
+        }
+
+        /**
+         * Constructor to create a FieldDiff object with the given values, and that has a
+         * detailed BaseDiff.
+         * @param from from (old) value
+         * @param to to (new) value
+         */
+        public FieldDiff(@Nullable T from, @Nullable T to, @Nullable BaseDiff detailedDiff) {
+            mFrom = from;
+            mTo = to;
+            mDetailedDiff = detailedDiff;
         }
 
         /**
@@ -93,6 +108,9 @@
          */
         @Override
         public String toString() {
+            if (mDetailedDiff != null) {
+                return mDetailedDiff.toString();
+            }
             return mFrom + "->" + mTo;
         }
 
@@ -100,6 +118,9 @@
          * Returns whether this represents an actual diff.
          */
         public boolean hasDiff() {
+            if (mDetailedDiff != null) {
+                return mDetailedDiff.hasDiff();
+            }
             // note that Objects.equals handles null values gracefully.
             return !Objects.equals(mFrom, mTo);
         }
@@ -115,7 +136,8 @@
         @ExistenceChange private int mExists = NONE;
 
         // Map from field name to diffs for any standalone fields in the object.
-        private ArrayMap<String, FieldDiff> mFields = new ArrayMap<>();
+        // LinkedHashMap is specifically chosen here to show insertion order when keys are fetched.
+        private LinkedHashMap<String, FieldDiff> mFields = new LinkedHashMap<>();
 
         // Functions for actually diffing objects and string representations have to be implemented
         // by subclasses.
@@ -550,8 +572,16 @@
             if (!Objects.equals(from.enabler, to.enabler)) {
                 addField(FIELD_ENABLER, new FieldDiff<>(from.enabler, to.enabler));
             }
-            if (!Objects.equals(from.zenPolicy, to.zenPolicy)) {
-                addField(FIELD_ZEN_POLICY, new FieldDiff<>(from.zenPolicy, to.zenPolicy));
+            if (android.app.Flags.modesApi()) {
+                PolicyDiff policyDiff = new PolicyDiff(from.zenPolicy, to.zenPolicy);
+                if (policyDiff.hasDiff()) {
+                    addField(FIELD_ZEN_POLICY, new FieldDiff<>(from.zenPolicy, to.zenPolicy,
+                            policyDiff));
+                }
+            } else {
+                if (!Objects.equals(from.zenPolicy, to.zenPolicy)) {
+                    addField(FIELD_ZEN_POLICY, new FieldDiff<>(from.zenPolicy, to.zenPolicy));
+                }
             }
             if (from.modified != to.modified) {
                 addField(FIELD_MODIFIED, new FieldDiff<>(from.modified, to.modified));
@@ -560,9 +590,12 @@
                 addField(FIELD_PKG, new FieldDiff<>(from.pkg, to.pkg));
             }
             if (android.app.Flags.modesApi()) {
-                if (!Objects.equals(from.zenDeviceEffects, to.zenDeviceEffects)) {
+                DeviceEffectsDiff deviceEffectsDiff = new DeviceEffectsDiff(from.zenDeviceEffects,
+                        to.zenDeviceEffects);
+                if (deviceEffectsDiff.hasDiff()) {
                     addField(FIELD_ZEN_DEVICE_EFFECTS,
-                            new FieldDiff<>(from.zenDeviceEffects, to.zenDeviceEffects));
+                            new FieldDiff<>(from.zenDeviceEffects, to.zenDeviceEffects,
+                                    deviceEffectsDiff));
                 }
                 if (!Objects.equals(from.triggerDescription, to.triggerDescription)) {
                     addField(FIELD_TRIGGER_DESCRIPTION,
@@ -630,7 +663,7 @@
 
                 sb.append(key);
                 sb.append(":");
-                sb.append(diff);
+                sb.append(diff.toString());
             }
 
             if (becameActive()) {
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 3599332..ec5b488 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -178,3 +178,13 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "handwriting_unsupported_show_soft_input_fix"
+  namespace: "text"
+  description: "Don't show soft keyboard on stylus input if text field doesn't support handwriting and getShowSoftInputOnFocus() returns false."
+  bug: "363180475"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/tracing/TEST_MAPPING b/core/java/android/tracing/TEST_MAPPING
new file mode 100644
index 0000000..b51d19d
--- /dev/null
+++ b/core/java/android/tracing/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "postsubmit": [
+    {
+      "name": "TracingTests"
+    },
+    {
+      "name": "ProtologPerfTests"
+    }
+  ]
+}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index f132963..c217999 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -19,6 +19,7 @@
 import static com.android.text.flags.Flags.handwritingCursorPosition;
 import static com.android.text.flags.Flags.handwritingTrackDisabled;
 import static com.android.text.flags.Flags.handwritingUnsupportedMessage;
+import static com.android.text.flags.Flags.handwritingUnsupportedShowSoftInputFix;
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
@@ -241,7 +242,11 @@
                             if (!candidateView.hasFocus()) {
                                 requestFocusWithoutReveal(candidateView);
                             }
-                            mImm.showSoftInput(candidateView, 0);
+                            if (!handwritingUnsupportedShowSoftInputFix()
+                                    || (candidateView instanceof TextView tv
+                                            && tv.getShowSoftInputOnFocus())) {
+                                mImm.showSoftInput(candidateView, 0);
+                            }
                             mState.mHandled = true;
                             mState.mShouldInitHandwriting = false;
                             motionEvent.setAction((motionEvent.getAction()
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 40a75fd..8fb17c7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -238,7 +238,7 @@
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
-import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener;
+import android.view.accessibility.AccessibilityManager.HighContrastTextStateChangeListener;
 import android.view.accessibility.AccessibilityNodeIdManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -1800,8 +1800,8 @@
         }
         mAccessibilityManager.addAccessibilityStateChangeListener(
                 mAccessibilityInteractionConnectionManager, mHandler);
-        mAccessibilityManager.addHighTextContrastStateChangeListener(
-                mHighContrastTextManager, mHandler);
+        mAccessibilityManager.addHighContrastTextStateChangeListener(
+                mExecutor, mHighContrastTextManager);
         DisplayManagerGlobal
                 .getInstance()
                 .registerDisplayListener(
@@ -1838,7 +1838,7 @@
     private void unregisterListeners() {
         mAccessibilityManager.removeAccessibilityStateChangeListener(
                 mAccessibilityInteractionConnectionManager);
-        mAccessibilityManager.removeHighTextContrastStateChangeListener(
+        mAccessibilityManager.removeHighContrastTextStateChangeListener(
                 mHighContrastTextManager);
         DisplayManagerGlobal
                 .getInstance()
@@ -11907,12 +11907,12 @@
         }
     }
 
-    final class HighContrastTextManager implements HighTextContrastChangeListener {
+    final class HighContrastTextManager implements HighContrastTextStateChangeListener {
         HighContrastTextManager() {
-            ThreadedRenderer.setHighContrastText(mAccessibilityManager.isHighTextContrastEnabled());
+            ThreadedRenderer.setHighContrastText(mAccessibilityManager.isHighContrastTextEnabled());
         }
         @Override
-        public void onHighTextContrastStateChanged(boolean enabled) {
+        public void onHighContrastTextStateChanged(boolean enabled) {
             ThreadedRenderer.setHighContrastText(enabled);
 
             destroyAndInvalidate();
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0582afe..381006c 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1345,7 +1345,7 @@
     }
 
     /**
-     * <p>Sets the desired about of HDR headroom to be used when rendering as a ratio of
+     * <p>Sets the desired amount of HDR headroom to be used when rendering as a ratio of
      * targetHdrPeakBrightnessInNits / targetSdrWhitePointInNits. Only applies when
      * {@link #setColorMode(int)} is {@link ActivityInfo#COLOR_MODE_HDR}</p>
      *
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 2b7cf42..fd57aec 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -253,7 +253,7 @@
     boolean mIsTouchExplorationEnabled;
 
     @UnsupportedAppUsage(trackingBug = 123768939L)
-    boolean mIsHighTextContrastEnabled;
+    boolean mIsHighContrastTextEnabled;
 
     boolean mIsAudioDescriptionByDefaultRequested;
 
@@ -276,8 +276,8 @@
     private final ArrayMap<TouchExplorationStateChangeListener, Handler>
             mTouchExplorationStateChangeListeners = new ArrayMap<>();
 
-    private final ArrayMap<HighTextContrastChangeListener, Handler>
-            mHighTextContrastStateChangeListeners = new ArrayMap<>();
+    private final ArrayMap<HighContrastTextStateChangeListener, Executor>
+            mHighContrastTextStateChangeListeners = new ArrayMap<>();
 
     private final ArrayMap<AccessibilityServicesStateChangeListener, Executor>
             mServicesStateChangeListeners = new ArrayMap<>();
@@ -356,21 +356,20 @@
     }
 
     /**
-     * Listener for the system high text contrast state. To listen for changes to
-     * the high text contrast state on the device, implement this interface and
+     * Listener for the system high contrast text state. To listen for changes to
+     * the high contrast text state on the device, implement this interface and
      * register it with the system by calling
-     * {@link #addHighTextContrastStateChangeListener}.
-     *
-     * @hide
+     * {@link #addHighContrastTextStateChangeListener}.
      */
-    public interface HighTextContrastChangeListener {
+    @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public interface HighContrastTextStateChangeListener {
 
         /**
-         * Called when the high text contrast enabled state changes.
+         * Called when the high contrast text enabled state changes.
          *
-         * @param enabled Whether high text contrast is enabled.
+         * @param enabled Whether high contrast text is enabled.
          */
-        void onHighTextContrastStateChanged(boolean enabled);
+        void onHighContrastTextStateChanged(boolean enabled);
     }
 
     /**
@@ -655,24 +654,23 @@
     }
 
     /**
-     * Returns if the high text contrast in the system is enabled.
+     * Returns if high contrast text in the system is enabled.
      * <p>
      * <strong>Note:</strong> You need to query this only if you application is
      * doing its own rendering and does not rely on the platform rendering pipeline.
      * </p>
      *
-     * @return True if high text contrast is enabled, false otherwise.
+     * @return True if high contrast text is enabled, false otherwise.
      *
-     * @hide
      */
-    @UnsupportedAppUsage
-    public boolean isHighTextContrastEnabled() {
+    @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public boolean isHighContrastTextEnabled() {
         synchronized (mLock) {
             IAccessibilityManager service = getServiceLocked();
             if (service == null) {
                 return false;
             }
-            return mIsHighTextContrastEnabled;
+            return mIsHighContrastTextEnabled;
         }
     }
 
@@ -1303,32 +1301,32 @@
     }
 
     /**
-     * Registers a {@link HighTextContrastChangeListener} for changes in
-     * the global high text contrast state of the system.
+     * Registers a {@link HighContrastTextStateChangeListener} for changes in
+     * the global high contrast text state of the system.
      *
-     * @param listener The listener.
-     *
-     * @hide
+     * @param executor a executor to call the listener from
+     * @param listener The listener to be called
      */
-    public void addHighTextContrastStateChangeListener(
-            @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {
+    @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public void addHighContrastTextStateChangeListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull HighContrastTextStateChangeListener listener
+    ) {
         synchronized (mLock) {
-            mHighTextContrastStateChangeListeners
-                    .put(listener, (handler == null) ? mHandler : handler);
+            mHighContrastTextStateChangeListeners.put(listener, executor);
         }
     }
 
     /**
-     * Unregisters a {@link HighTextContrastChangeListener}.
+     * Unregisters a {@link HighContrastTextStateChangeListener}.
      *
      * @param listener The listener.
-     *
-     * @hide
      */
-    public void removeHighTextContrastStateChangeListener(
-            @NonNull HighTextContrastChangeListener listener) {
+    @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public void removeHighContrastTextStateChangeListener(
+            @NonNull HighContrastTextStateChangeListener listener) {
         synchronized (mLock) {
-            mHighTextContrastStateChangeListeners.remove(listener);
+            mHighContrastTextStateChangeListeners.remove(listener);
         }
     }
 
@@ -1505,13 +1503,13 @@
 
         final boolean wasEnabled = isEnabled();
         final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
-        final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
+        final boolean wasHighTextContrastEnabled = mIsHighContrastTextEnabled;
         final boolean wasAudioDescriptionByDefaultRequested = mIsAudioDescriptionByDefaultRequested;
 
         // Ensure listeners get current state from isZzzEnabled() calls.
         mIsEnabled = enabled;
         mIsTouchExplorationEnabled = touchExplorationEnabled;
-        mIsHighTextContrastEnabled = highTextContrastEnabled;
+        mIsHighContrastTextEnabled = highTextContrastEnabled;
         mIsAudioDescriptionByDefaultRequested = audioDescriptionEnabled;
 
         if (wasEnabled != isEnabled()) {
@@ -1523,7 +1521,7 @@
         }
 
         if (wasHighTextContrastEnabled != highTextContrastEnabled) {
-            notifyHighTextContrastStateChanged();
+            notifyHighContrastTextStateChanged();
         }
 
         if (wasAudioDescriptionByDefaultRequested
@@ -2397,24 +2395,24 @@
     }
 
     /**
-     * Notifies the registered {@link HighTextContrastChangeListener}s.
+     * Notifies the registered {@link HighContrastTextStateChangeListener}s.
      */
-    private void notifyHighTextContrastStateChanged() {
+    private void notifyHighContrastTextStateChanged() {
         final boolean isHighTextContrastEnabled;
-        final ArrayMap<HighTextContrastChangeListener, Handler> listeners;
+        final ArrayMap<HighContrastTextStateChangeListener, Executor> listeners;
         synchronized (mLock) {
-            if (mHighTextContrastStateChangeListeners.isEmpty()) {
+            if (mHighContrastTextStateChangeListeners.isEmpty()) {
                 return;
             }
-            isHighTextContrastEnabled = mIsHighTextContrastEnabled;
-            listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners);
+            isHighTextContrastEnabled = mIsHighContrastTextEnabled;
+            listeners = new ArrayMap<>(mHighContrastTextStateChangeListeners);
         }
 
         final int numListeners = listeners.size();
         for (int i = 0; i < numListeners; i++) {
-            final HighTextContrastChangeListener listener = listeners.keyAt(i);
-            listeners.valueAt(i).post(() ->
-                    listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
+            final HighContrastTextStateChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).execute(() ->
+                    listener.onHighContrastTextStateChanged(isHighTextContrastEnabled));
         }
     }
 
diff --git a/core/java/android/view/autofill/AutofillStateFingerprint.java b/core/java/android/view/autofill/AutofillStateFingerprint.java
index 2db4285..7f3858e 100644
--- a/core/java/android/view/autofill/AutofillStateFingerprint.java
+++ b/core/java/android/view/autofill/AutofillStateFingerprint.java
@@ -97,7 +97,6 @@
             if (sDebug) {
                 Log.d(TAG, "Autofillable views count prior to auth:" + autofillableViews.size());
             }
-//            ArrayList<Integer> hashes = getFingerprintIds(autofillableViews);
 
             ArrayMap<Integer, View> hashes = getFingerprintIds(autofillableViews);
             for (Map.Entry<Integer, View> entry : hashes.entrySet()) {
@@ -123,7 +122,6 @@
                 if (view != null) {
                     int id = getEphemeralFingerprintId(view, 0 /* position irrelevant */);
                     AutofillId autofillId = view.getAutofillId();
-                    autofillId.setSessionId(mSessionId);
                     mHashToAutofillIdMap.put(id, autofillId);
                 } else {
                     if (sDebug) {
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index fbc30ed..b22aa22 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -162,6 +162,13 @@
 }
 
 flag {
+    name: "enable_a11y_metrics"
+    namespace: "lse_desktop_experience"
+    description: "Whether to enable log collection for a11y actions in desktop windowing mode"
+    bug: "341319597"
+}
+
+flag {
     name: "enable_caption_compat_inset_force_consumption"
     namespace: "lse_desktop_experience"
     description: "Enables force-consumption of caption bar insets for immersive apps in freeform"
@@ -306,3 +313,10 @@
     description: "Allow entering desktop mode by default on freeform displays"
     bug: "361419732"
 }
+
+flag {
+    name: "enable_desktop_app_launch_alttab_transitions"
+    namespace: "lse_desktop_experience"
+    description: "Enables custom transitions for alt-tab app launches in Desktop Mode."
+    bug: "370735595"
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index fef5e83..4aebde5 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -26,6 +26,7 @@
 import android.os.RemoteException;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -37,38 +38,40 @@
      * @deprecated Legacy system channel, which is no longer used,
      */
     @Deprecated public static String VIRTUAL_KEYBOARD  = "VIRTUAL_KEYBOARD";
-    public static String PHYSICAL_KEYBOARD = "PHYSICAL_KEYBOARD";
-    public static String SECURITY = "SECURITY";
-    public static String CAR_MODE = "CAR_MODE";
-    public static String ACCOUNT = "ACCOUNT";
-    public static String DEVELOPER = "DEVELOPER";
-    public static String DEVELOPER_IMPORTANT = "DEVELOPER_IMPORTANT";
-    public static String UPDATES = "UPDATES";
-    public static String NETWORK_STATUS = "NETWORK_STATUS";
-    public static String NETWORK_ALERTS = "NETWORK_ALERTS";
-    public static String NETWORK_AVAILABLE = "NETWORK_AVAILABLE";
-    public static String VPN = "VPN";
+    public static final String PHYSICAL_KEYBOARD = "PHYSICAL_KEYBOARD";
+    public static final String SECURITY = "SECURITY";
+    public static final String CAR_MODE = "CAR_MODE";
+    public static final String ACCOUNT = "ACCOUNT";
+    public static final String DEVELOPER = "DEVELOPER";
+    public static final String DEVELOPER_IMPORTANT = "DEVELOPER_IMPORTANT";
+    public static final String UPDATES = "UPDATES";
+    public static final String NETWORK_STATUS = "NETWORK_STATUS";
+    public static final String NETWORK_ALERTS = "NETWORK_ALERTS";
+    public static final String NETWORK_AVAILABLE = "NETWORK_AVAILABLE";
+    public static final String VPN = "VPN";
     /**
      * @deprecated Legacy device admin channel with low importance which is no longer used,
      *  Use the high importance {@link #DEVICE_ADMIN} channel instead.
      */
-    @Deprecated public static String DEVICE_ADMIN_DEPRECATED = "DEVICE_ADMIN";
-    public static String DEVICE_ADMIN = "DEVICE_ADMIN_ALERTS";
-    public static String ALERTS = "ALERTS";
-    public static String RETAIL_MODE = "RETAIL_MODE";
-    public static String USB = "USB";
-    public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
-    public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
+    @Deprecated public static final String DEVICE_ADMIN_DEPRECATED = "DEVICE_ADMIN";
+    public static final String DEVICE_ADMIN = "DEVICE_ADMIN_ALERTS";
+    public static final String ALERTS = "ALERTS";
+    public static final String RETAIL_MODE = "RETAIL_MODE";
+    public static final String USB = "USB";
+    public static final String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
+    public static final String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
     /**
      * @deprecated Legacy system changes channel with low importance which is no longer used,
      *  Use the default importance {@link #SYSTEM_CHANGES} channel instead.
      */
-    @Deprecated public static String SYSTEM_CHANGES_DEPRECATED = "SYSTEM_CHANGES";
-    public static String SYSTEM_CHANGES = "SYSTEM_CHANGES_ALERTS";
-    public static String DO_NOT_DISTURB = "DO_NOT_DISTURB";
-    public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION";
-    public static String ACCESSIBILITY_SECURITY_POLICY = "ACCESSIBILITY_SECURITY_POLICY";
-    public static String ABUSIVE_BACKGROUND_APPS = "ABUSIVE_BACKGROUND_APPS";
+    @Deprecated public static final String SYSTEM_CHANGES_DEPRECATED = "SYSTEM_CHANGES";
+    public static final String SYSTEM_CHANGES = "SYSTEM_CHANGES_ALERTS";
+    public static final String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION";
+    public static final String ACCESSIBILITY_SECURITY_POLICY = "ACCESSIBILITY_SECURITY_POLICY";
+    public static final String ABUSIVE_BACKGROUND_APPS = "ABUSIVE_BACKGROUND_APPS";
+
+    @VisibleForTesting
+    static final String OBSOLETE_DO_NOT_DISTURB = "DO_NOT_DISTURB";
 
     public static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -193,11 +196,6 @@
                 .build());
         channelsList.add(systemChanges);
 
-        NotificationChannel dndChanges = new NotificationChannel(DO_NOT_DISTURB,
-                context.getString(R.string.notification_channel_do_not_disturb),
-                NotificationManager.IMPORTANCE_LOW);
-        channelsList.add(dndChanges);
-
         final NotificationChannel newFeaturePrompt = new NotificationChannel(
                 ACCESSIBILITY_MAGNIFICATION,
                 context.getString(R.string.notification_channel_accessibility_magnification),
@@ -218,6 +216,9 @@
         channelsList.add(abusiveBackgroundAppsChannel);
 
         nm.createNotificationChannels(channelsList);
+
+        // Delete channels created by previous Android versions that are no longer used.
+        nm.deleteNotificationChannel(OBSOLETE_DO_NOT_DISTURB);
     }
 
     private static String getDeviceAdminNotificationChannelName(Context context) {
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 4d0cd27..f3dc896 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -56,6 +56,7 @@
 import android.tracing.perfetto.Producer;
 import android.tracing.perfetto.TracingContext;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.LongArray;
 import android.util.Slog;
@@ -351,6 +352,10 @@
     }
 
     private void registerGroupsLocally(@NonNull IProtoLogGroup[] protoLogGroups) {
+        // Verify we don't have id collisions, if we do we want to know as soon as possible and
+        // we might want to manually specify an id for the group with a collision
+        verifyNoCollisionsOrDuplicates(protoLogGroups);
+
         final var groupsLoggingToLogcat = new ArrayList<String>();
         for (IProtoLogGroup protoLogGroup : protoLogGroups) {
             mLogGroups.put(protoLogGroup.name(), protoLogGroup);
@@ -369,6 +374,19 @@
         }
     }
 
+    private void verifyNoCollisionsOrDuplicates(@NonNull IProtoLogGroup[] protoLogGroups) {
+        final var groupId = new ArraySet<Integer>();
+
+        for (IProtoLogGroup protoLogGroup : protoLogGroups) {
+            if (groupId.contains(protoLogGroup.getId())) {
+                throw new RuntimeException(
+                        "Group with same id (" + protoLogGroup.getId() + ") registered twice. "
+                                + "Potential duplicate or hash id collision.");
+            }
+            groupId.add(protoLogGroup.getId());
+        }
+    }
+
     /**
      * Responds to a shell command.
      */
diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java
index adf03fe..60213b1 100644
--- a/core/java/com/android/internal/protolog/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/ProtoLog.java
@@ -22,8 +22,8 @@
 import com.android.internal.protolog.common.IProtoLogGroup;
 import com.android.internal.protolog.common.LogLevel;
 
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 
 /**
  * ProtoLog API - exposes static logging methods. Usage of this API is similar
@@ -73,7 +73,7 @@
                 if (sProtoLogInstance != null) {
                     // The ProtoLog instance has already been initialized in this process
                     final var alreadyRegisteredGroups = sProtoLogInstance.getRegisteredGroups();
-                    final var allGroups = new ArrayList<>(alreadyRegisteredGroups);
+                    final var allGroups = new HashSet<>(alreadyRegisteredGroups);
                     allGroups.addAll(Arrays.stream(groups).toList());
                     groups = allGroups.toArray(new IProtoLogGroup[0]);
                 }
diff --git a/core/java/com/android/internal/protolog/TEST_MAPPING b/core/java/com/android/internal/protolog/TEST_MAPPING
index 37d57ee..b51d19d 100644
--- a/core/java/com/android/internal/protolog/TEST_MAPPING
+++ b/core/java/com/android/internal/protolog/TEST_MAPPING
@@ -1,6 +1,9 @@
 {
   "postsubmit": [
     {
+      "name": "TracingTests"
+    },
+    {
       "name": "ProtologPerfTests"
     }
   ]
diff --git a/core/jni/jni_wrappers.h b/core/jni/jni_wrappers.h
index 21b5b13..e3e17ee 100644
--- a/core/jni/jni_wrappers.h
+++ b/core/jni/jni_wrappers.h
@@ -79,13 +79,14 @@
     jniMethodFormat = value;
 }
 
-// Potentially translates the given JNINativeMethods if setJniMethodFormat has been set.
-// Has no effect otherwise
-inline const JNINativeMethod* maybeRenameJniMethods(const JNINativeMethod* gMethods,
-                                                    int numMethods) {
+// Register the native methods, potenially applying the jniMethodFormat if it has been set.
+static inline int jniRegisterMaybeRenamedNativeMethods(JNIEnv* env, const char* className,
+                                                       const JNINativeMethod* gMethods,
+                                                       int numMethods) {
     if (jniMethodFormat.empty()) {
-        return gMethods;
+        return jniRegisterNativeMethods(env, className, gMethods, numMethods);
     }
+
     // Make a copy of gMethods with reformatted method names.
     JNINativeMethod* modifiedMethods = new JNINativeMethod[numMethods];
     LOG_ALWAYS_FATAL_IF(!modifiedMethods, "Failed to allocate a copy of the JNI methods");
@@ -103,13 +104,17 @@
         std::strcpy(modifiedNameChars, modifiedName.c_str());
         modifiedMethods[i].name = modifiedNameChars;
     }
-    return modifiedMethods;
+    int res = jniRegisterNativeMethods(env, className, modifiedMethods, numMethods);
+    for (int i = 0; i < numMethods; i++) {
+        delete[] modifiedMethods[i].name;
+    }
+    delete[] modifiedMethods;
+    return res;
 }
 
 static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
                                        const JNINativeMethod* gMethods, int numMethods) {
-    const JNINativeMethod* modifiedMethods = maybeRenameJniMethods(gMethods, numMethods);
-    int res = jniRegisterNativeMethods(env, className, modifiedMethods, numMethods);
+    int res = jniRegisterMaybeRenamedNativeMethods(env, className, gMethods, numMethods);
     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
     return res;
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 71ae22f..1a3a30d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1250,6 +1250,7 @@
          a watch, setting this config is no-op.
             0 - Nothing
             1 - Switch to the recent app
+            2 - Launch the default fitness app
     -->
     <integer name="config_doublePressOnStemPrimaryBehavior">0</integer>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d634210..7aca535 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5818,16 +5818,6 @@
 
     <!-- Title for the notification channel notifying user of settings system changes. [CHAR LIMIT=NONE] -->
     <string name="notification_channel_system_changes">System changes</string>
-    <!-- Title for the notification channel notifying user of do not disturb system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
-    <string name="notification_channel_do_not_disturb">Do Not Disturb</string>
-    <!-- Title of notification indicating do not disturb visual interruption settings have changed when upgrading to P -->
-    <string name="zen_upgrade_notification_visd_title">New: Do Not Disturb is hiding notifications</string>
-    <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
-    <string name="zen_upgrade_notification_visd_content">Tap to learn more and change.</string>
-    <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
-    <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
-    <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
-    <string name="zen_upgrade_notification_content">Tap to check what\'s blocked.</string>
 
     <!-- Notification permission informational notification text -->
     <!-- Title for notification inviting users to review their notification settings [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0ccef91..d5298ac 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3937,7 +3937,6 @@
   <java-symbol type="string" name="notification_channel_usb" />
   <java-symbol type="string" name="notification_channel_heavy_weight_app" />
   <java-symbol type="string" name="notification_channel_system_changes" />
-  <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="notification_channel_accessibility_magnification" />
   <java-symbol type="string" name="notification_channel_accessibility_security_policy" />
   <java-symbol type="string" name="notification_channel_display" />
@@ -4164,11 +4163,6 @@
   <!-- For Wear devices -->
   <java-symbol type="array" name="config_wearActivityModeRadios" />
 
-  <java-symbol type="string" name="zen_upgrade_notification_title" />
-  <java-symbol type="string" name="zen_upgrade_notification_content" />
-  <java-symbol type="string" name="zen_upgrade_notification_visd_title" />
-  <java-symbol type="string" name="zen_upgrade_notification_visd_content" />
-
   <java-symbol type="string" name="review_notification_settings_title" />
   <java-symbol type="string" name="review_notification_settings_text" />
   <java-symbol type="string" name="review_notification_settings_remind_me_action" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 9821d43..56e18e6 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -250,7 +250,7 @@
         "androidx.test.rules",
         "androidx.test.ext.junit",
         "androidx.test.uiautomator_uiautomator",
-        "compatibility-device-util-axt",
+        "compatibility-device-util-axt-ravenwood",
         "flag-junit",
         "platform-test-annotations",
         "flag-junit",
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e9b137c..48e2620 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -2301,13 +2301,13 @@
 
     @Test
     @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
-    public void progressStyle_onProgressStepChange_visiblyDifferent() {
+    public void progressStyle_onProgressPointChange_visiblyDifferent() {
         final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
                 .setStyle(new Notification.ProgressStyle()
-                        .addProgressStep(new Notification.ProgressStyle.Step(10)));
+                        .addProgressPoint(new Notification.ProgressStyle.Point(10)));
         final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
                 .setStyle(new Notification.ProgressStyle()
-                        .addProgressStep(new Notification.ProgressStyle.Step(12)));
+                        .addProgressPoint(new Notification.ProgressStyle.Point(12)));
 
         assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
                 .isTrue();
@@ -2315,13 +2315,13 @@
 
     @Test
     @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
-    public void indeterminateProgressStyle_onProgressStepChange_visiblyNotDifferent() {
+    public void indeterminateProgressStyle_onProgressPointChange_visiblyNotDifferent() {
         final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
                 .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true)
-                        .addProgressStep(new Notification.ProgressStyle.Step(10)));
+                        .addProgressPoint(new Notification.ProgressStyle.Point(10)));
         final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
                 .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true)
-                        .addProgressStep(new Notification.ProgressStyle.Step(12)));
+                        .addProgressPoint(new Notification.ProgressStyle.Point(12)));
 
         assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
                 .isFalse();
diff --git a/core/tests/coretests/src/com/android/internal/notification/SystemNotificationChannelsTest.java b/core/tests/coretests/src/com/android/internal/notification/SystemNotificationChannelsTest.java
new file mode 100644
index 0000000..0bf406c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/notification/SystemNotificationChannelsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.notification;
+
+import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
+import static com.android.internal.notification.SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION;
+import static com.android.internal.notification.SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY;
+import static com.android.internal.notification.SystemNotificationChannels.ACCOUNT;
+import static com.android.internal.notification.SystemNotificationChannels.ALERTS;
+import static com.android.internal.notification.SystemNotificationChannels.CAR_MODE;
+import static com.android.internal.notification.SystemNotificationChannels.DEVELOPER;
+import static com.android.internal.notification.SystemNotificationChannels.DEVELOPER_IMPORTANT;
+import static com.android.internal.notification.SystemNotificationChannels.DEVICE_ADMIN;
+import static com.android.internal.notification.SystemNotificationChannels.FOREGROUND_SERVICE;
+import static com.android.internal.notification.SystemNotificationChannels.HEAVY_WEIGHT_APP;
+import static com.android.internal.notification.SystemNotificationChannels.NETWORK_ALERTS;
+import static com.android.internal.notification.SystemNotificationChannels.NETWORK_AVAILABLE;
+import static com.android.internal.notification.SystemNotificationChannels.NETWORK_STATUS;
+import static com.android.internal.notification.SystemNotificationChannels.OBSOLETE_DO_NOT_DISTURB;
+import static com.android.internal.notification.SystemNotificationChannels.PHYSICAL_KEYBOARD;
+import static com.android.internal.notification.SystemNotificationChannels.RETAIL_MODE;
+import static com.android.internal.notification.SystemNotificationChannels.SECURITY;
+import static com.android.internal.notification.SystemNotificationChannels.SYSTEM_CHANGES;
+import static com.android.internal.notification.SystemNotificationChannels.UPDATES;
+import static com.android.internal.notification.SystemNotificationChannels.USB;
+import static com.android.internal.notification.SystemNotificationChannels.VPN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.testing.TestableContext;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SystemNotificationChannelsTest {
+
+    @Rule public TestableContext mContext = new TestableContext(
+            ApplicationProvider.getApplicationContext());
+
+    @Mock private NotificationManager mNm;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext.addMockSystemService(NotificationManager.class, mNm);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void createAll_createsExpectedChannels() {
+        ArgumentCaptor<List<NotificationChannel>> createdChannelsCaptor =
+                ArgumentCaptor.forClass(List.class);
+
+        SystemNotificationChannels.createAll(mContext);
+
+        verify(mNm).createNotificationChannels(createdChannelsCaptor.capture());
+        List<NotificationChannel> createdChannels = createdChannelsCaptor.getValue();
+        assertThat(createdChannels.stream().map(NotificationChannel::getId).toList())
+                .containsExactly(PHYSICAL_KEYBOARD, SECURITY, CAR_MODE, ACCOUNT, DEVELOPER,
+                        DEVELOPER_IMPORTANT, UPDATES, NETWORK_STATUS, NETWORK_ALERTS,
+                        NETWORK_AVAILABLE, VPN, DEVICE_ADMIN, ALERTS, RETAIL_MODE, USB,
+                        FOREGROUND_SERVICE, HEAVY_WEIGHT_APP, SYSTEM_CHANGES,
+                        ACCESSIBILITY_MAGNIFICATION, ACCESSIBILITY_SECURITY_POLICY,
+                        ABUSIVE_BACKGROUND_APPS);
+    }
+
+    @Test
+    public void createAll_deletesObsoleteChannels() {
+        ArgumentCaptor<String> deletedChannelCaptor = ArgumentCaptor.forClass(String.class);
+
+        SystemNotificationChannels.createAll(mContext);
+
+        verify(mNm, atLeastOnce()).deleteNotificationChannel(deletedChannelCaptor.capture());
+        List<String> deletedChannels = deletedChannelCaptor.getAllValues();
+        assertThat(deletedChannels).containsExactly(OBSOLETE_DO_NOT_DISTURB);
+    }
+}
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index cdc8a9e..7cf49ab 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -61,7 +61,7 @@
         "androidx.annotation_annotation",
         "androidx.test.rules",
         "frameworks-base-testutils",
-        "servicestests-utils",
+        "servicestests-utils-ravenwood",
     ],
     srcs: [
         "src/android/util/IRemoteMemoryIntArray.aidl",
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 0b3e545..28c2ca3 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -155,7 +155,7 @@
     /**
      * Indicates whether this Canvas is drawing high contrast text.
      *
-     * @see android.view.accessibility.AccessibilityManager#isHighTextContrastEnabled()
+     * @see android.view.accessibility.AccessibilityManager#isHighContrastTextEnabled()
      * @return True if high contrast text is enabled, false otherwise.
      *
      * @hide
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index a796ecc..e493ed1 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -197,6 +197,7 @@
 android_library {
     name: "WindowManager-Shell",
     srcs: [
+        "src/com/android/wm/shell/EventLogTags.logtags",
         ":wm_shell_protolog_src",
         // TODO(b/168581922) protologtool do not support kotlin(*.kt)
         ":wm_shell-sources-kt",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/EventLogTags.logtags b/libs/WindowManager/Shell/src/com/android/wm/shell/EventLogTags.logtags
new file mode 100644
index 0000000..db960d1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/EventLogTags.logtags
@@ -0,0 +1,11 @@
+# See system/logging/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.wm.shell
+
+# Do not change these names without updating the checkin_events setting in
+# google3/googledata/wireless/android/provisioning/gservices.config !!
+#
+
+38500 wm_shell_enter_desktop_mode (EnterReason|1|5),(SessionId|1|5)
+38501 wm_shell_exit_desktop_mode (ExitReason|1|5),(SessionId|1|5)
+38502 wm_shell_desktop_mode_task_update (TaskEvent|1|5),(InstanceId|1|5),(uid|1|5),(TaskHeight|1),(TaskWidth|1),(TaskX|1),(TaskY|1),(SessionId|1|5),(MinimiseReason|1|5),(UnminimiseReason|1|5),(VisibleTaskCount|1)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index af4a0c5..03b7c8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -2001,6 +2001,10 @@
             // in bubble bar mode, let the request to show the expanded view come from launcher.
             // only collapse here if we're collapsing.
             if (mLayerView != null && !isExpanded) {
+                if (mBubblePositioner.isImeVisible()) {
+                    // If we're collapsing, hide the IME
+                    hideCurrentInputMethod();
+                }
                 mLayerView.collapse();
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index ec235a5..2a90017 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -353,6 +353,11 @@
         if (isDragging != mIsDragging) {
             mIsDragging = isDragging;
             updateSamplingState();
+
+            if (isDragging && mPositioner.isImeVisible()) {
+                // Hide the IME when dragging begins
+                mManager.hideCurrentInputMethod();
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 8ce3884..7293956 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -851,10 +851,11 @@
     static DesktopWindowingEducationTooltipController
             provideDesktopWindowingEducationTooltipController(
             Context context,
-            AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory
+            AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory,
+            DisplayController displayController
     ) {
         return new DesktopWindowingEducationTooltipController(context,
-                additionalSystemViewContainerFactory);
+                additionalSystemViewContainerFactory, displayController);
     }
 
     @OptIn(markerClass = ExperimentalCoroutinesApi.class)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 02cbe01..5a277316f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -19,6 +19,8 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
 import com.android.internal.util.FrameworkStatsLog
+import com.android.window.flags.Flags
+import com.android.wm.shell.EventLogTags
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 
 /** Event logger for logging desktop mode session events */
@@ -41,6 +43,7 @@
             /* exitReason */ 0,
             /* session_id */ sessionId
         )
+        EventLogTags.writeWmShellEnterDesktopMode(enterReason.reason, sessionId)
     }
 
     /**
@@ -61,6 +64,7 @@
             /* exitReason */ exitReason.reason,
             /* session_id */ sessionId
         )
+        EventLogTags.writeWmShellExitDesktopMode(exitReason.reason, sessionId)
     }
 
     /**
@@ -76,7 +80,8 @@
         )
         logTaskUpdate(
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED,
-            sessionId, taskUpdate)
+            sessionId, taskUpdate
+        )
     }
 
     /**
@@ -92,7 +97,8 @@
         )
         logTaskUpdate(
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED,
-            sessionId, taskUpdate)
+            sessionId, taskUpdate
+        )
     }
 
     /**
@@ -108,7 +114,46 @@
         )
         logTaskUpdate(
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
-            sessionId, taskUpdate)
+            sessionId, taskUpdate
+        )
+    }
+
+    /**
+     * Logs that a task resize event is starting with [taskSizeUpdate] within a
+     * Desktop mode [sessionId].
+     */
+    fun logTaskResizingStarted(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+        if (!Flags.enableResizingMetrics()) return
+
+        ProtoLog.v(
+            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s",
+            sessionId,
+            taskSizeUpdate.instanceId
+        )
+        logTaskSizeUpdated(
+            FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE,
+            sessionId, taskSizeUpdate
+        )
+    }
+
+    /**
+     * Logs that a task resize event is ending with [taskSizeUpdate] within a
+     * Desktop mode [sessionId].
+     */
+    fun logTaskResizingEnded(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+        if (!Flags.enableResizingMetrics()) return
+
+        ProtoLog.v(
+            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s",
+            sessionId,
+            taskSizeUpdate.instanceId
+        )
+        logTaskSizeUpdated(
+            FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE,
+            sessionId, taskSizeUpdate
+        )
     }
 
     private fun logTaskUpdate(taskEvent: Int, sessionId: Int, taskUpdate: TaskUpdate) {
@@ -135,6 +180,56 @@
             /* visible_task_count */
             taskUpdate.visibleTaskCount
         )
+        EventLogTags.writeWmShellDesktopModeTaskUpdate(
+            /* task_event */
+            taskEvent,
+            /* instance_id */
+            taskUpdate.instanceId,
+            /* uid */
+            taskUpdate.uid,
+            /* task_height */
+            taskUpdate.taskHeight,
+            /* task_width */
+            taskUpdate.taskWidth,
+            /* task_x */
+            taskUpdate.taskX,
+            /* task_y */
+            taskUpdate.taskY,
+            /* session_id */
+            sessionId,
+            taskUpdate.minimizeReason?.reason ?: UNSET_MINIMIZE_REASON,
+            taskUpdate.unminimizeReason?.reason ?: UNSET_UNMINIMIZE_REASON,
+            /* visible_task_count */
+            taskUpdate.visibleTaskCount
+        )
+    }
+
+    private fun logTaskSizeUpdated(
+        resizingStage: Int,
+        sessionId: Int,
+        taskSizeUpdate: TaskSizeUpdate
+    ) {
+        FrameworkStatsLog.write(
+            DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID,
+            /* resize_trigger */
+            taskSizeUpdate.resizeTrigger?.trigger ?: ResizeTrigger.UNKNOWN_RESIZE_TRIGGER.trigger,
+            /* resizing_stage */
+            resizingStage,
+            /* input_method */
+            taskSizeUpdate.inputMethod?.method ?: InputMethod.UNKNOWN_INPUT_METHOD.method,
+            /* desktop_mode_session_id */
+            sessionId,
+            /* instance_id */
+            taskSizeUpdate.instanceId,
+            /* uid */
+            taskSizeUpdate.uid,
+            /* task_height */
+            taskSizeUpdate.taskHeight,
+            /* task_width */
+            taskSizeUpdate.taskWidth,
+            /* display_area */
+            taskSizeUpdate.displayArea
+        )
     }
 
     companion object {
@@ -163,13 +258,35 @@
             val visibleTaskCount: Int,
         )
 
+        /**
+         * Describes a task size update (resizing, snapping or maximizing to
+         * stable bounds).
+         *
+         * @property resizeTrigger the trigger for task resize
+         * @property inputMethod the input method for resizing this task
+         * @property instanceId instance id of the task
+         * @property uid uid of the app associated with the task
+         * @property taskHeight height of the task in dp
+         * @property taskWidth width of the task in dp
+         * @property displayArea the display size of the screen in dp
+         */
+        data class TaskSizeUpdate(
+            val resizeTrigger: ResizeTrigger? = null,
+            val inputMethod: InputMethod? = null,
+            val instanceId: Int,
+            val uid: Int,
+            val taskHeight: Int,
+            val taskWidth: Int,
+            val displayArea: Int,
+        )
+
         // Default value used when the task was not minimized.
         @VisibleForTesting
         const val UNSET_MINIMIZE_REASON =
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__UNSET_MINIMIZE
 
         /** The reason a task was minimized. */
-        enum class MinimizeReason (val reason: Int) {
+        enum class MinimizeReason(val reason: Int) {
             TASK_LIMIT(
                 FrameworkStatsLog
                     .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_TASK_LIMIT
@@ -186,7 +303,7 @@
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNSET_UNMINIMIZE
 
         /** The reason a task was unminimized. */
-        enum class UnminimizeReason (val reason: Int) {
+        enum class UnminimizeReason(val reason: Int) {
             UNKNOWN(
                 FrameworkStatsLog
                     .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_UNKNOWN
@@ -250,8 +367,88 @@
             SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF)
         }
 
+        /**
+         * Enum ResizeTrigger mapped to the ResizeTrigger definition in
+         * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
+         */
+        enum class ResizeTrigger(val trigger: Int) {
+            UNKNOWN_RESIZE_TRIGGER(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER
+            ),
+            CORNER(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER
+            ),
+            EDGE(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__EDGE_RESIZE_TRIGGER
+            ),
+            TILING_DIVIDER(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__TILING_DIVIDER_RESIZE_TRIGGER
+            ),
+            MAXIMIZE_BUTTON(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__MAXIMIZE_BUTTON_RESIZE_TRIGGER
+            ),
+            DOUBLE_TAP_APP_HEADER(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__DOUBLE_TAP_APP_HEADER_RESIZE_TRIGGER
+            ),
+            DRAG_LEFT(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__DRAG_LEFT_RESIZE_TRIGGER
+            ),
+            DRAG_RIGHT(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__DRAG_RIGHT_RESIZE_TRIGGER
+            ),
+            SNAP_LEFT_MENU(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__SNAP_LEFT_MENU_RESIZE_TRIGGER
+            ),
+            SNAP_RIGHT_MENU(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__SNAP_RIGHT_MENU_RESIZE_TRIGGER
+            ),
+        }
+
+        /**
+         * Enum InputMethod mapped to the InputMethod definition in
+         * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
+         */
+        enum class InputMethod(val method: Int) {
+            UNKNOWN_INPUT_METHOD(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD
+            ),
+            TOUCH(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__TOUCH_INPUT_METHOD
+            ),
+            STYLUS(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__STYLUS_INPUT_METHOD
+            ),
+            MOUSE(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__MOUSE_INPUT_METHOD
+            ),
+            TOUCHPAD(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__TOUCHPAD_INPUT_METHOD
+            ),
+            KEYBOARD(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__KEYBOARD_INPUT_METHOD
+            ),
+        }
+
         private const val DESKTOP_MODE_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED
         private const val DESKTOP_MODE_TASK_UPDATE_ATOM_ID =
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE
+        private const val DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID =
+            FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b8bb73b..fcd2f8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -406,7 +406,7 @@
         interactionJankMonitor.begin(taskSurface, context, handler,
             CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         dragToDesktopTransitionHandler.startDragToDesktopTransition(
-            taskInfo.taskId,
+            taskInfo,
             dragToDesktopValueAnimator
         )
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 7e0741f..37ad0c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -24,6 +24,7 @@
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
+import android.window.flags.DesktopModeFlags
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
 import com.android.internal.jank.InteractionJankMonitor
@@ -161,6 +162,8 @@
     @VisibleForTesting
     inner class LeftoverMinimizedTasksRemover : DesktopModeTaskRepository.ActiveTasksListener {
         override fun onActiveTasksChanged(displayId: Int) {
+            // If back navigation is enabled, we shouldn't remove the leftover tasks
+            if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
             val wct = WindowContainerTransaction()
             removeLeftoverMinimizedTasks(displayId, wct)
             shellTaskOrganizer.applyTransaction(wct)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 4796c4d..b20c9fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -29,6 +29,7 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
@@ -67,9 +68,29 @@
     ) {
         // TODO: b/332682201 Update repository state
         updateWallpaperToken(info)
-
         if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
             handleBackNavigation(info)
+            removeTaskIfNeeded(info)
+        }
+    }
+
+    private fun removeTaskIfNeeded(info: TransitionInfo) {
+        // Since we are no longer removing all the tasks [onTaskVanished], we need to remove them by
+        // checking the transitions.
+        if (!TransitionUtil.isOpeningType(info.type)) return
+        // Remove a task from the repository if the app is launched outside of desktop.
+        for (change in info.changes) {
+            val taskInfo = change.taskInfo
+            if (taskInfo == null || taskInfo.taskId == -1) continue
+
+            if (desktopModeTaskRepository.isActiveTask(taskInfo.taskId)
+                && taskInfo.windowingMode != WINDOWING_MODE_FREEFORM
+            ) {
+                desktopModeTaskRepository.removeFreeformTask(
+                    taskInfo.displayId,
+                    taskInfo.taskId
+                )
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 2bc01b2..8e264b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -109,8 +109,8 @@
      * after one of the "end" or "cancel" transitions is merged into this transition.
      */
     fun startDragToDesktopTransition(
-        taskId: Int,
-        dragToDesktopAnimator: MoveToDesktopAnimator,
+        taskInfo: RunningTaskInfo,
+        dragToDesktopAnimator: MoveToDesktopAnimator
     ) {
         if (inProgress) {
             ProtoLog.v(
@@ -137,23 +137,26 @@
             )
         val wct = WindowContainerTransaction()
         wct.sendPendingIntent(pendingIntent, launchHomeIntent, Bundle())
+        // The home launch done above will result in an attempt to move the task to pip if
+        // applicable, resulting in a broken state. Prevent that here.
+        wct.setDoNotPip(taskInfo.token)
         val startTransitionToken =
             transitions.startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
 
         transitionState =
-            if (isSplitTask(taskId)) {
+            if (isSplitTask(taskInfo.taskId)) {
                 val otherTask =
-                    getOtherSplitTask(taskId)
+                    getOtherSplitTask(taskInfo.taskId)
                         ?: throw IllegalStateException("Expected split task to have a counterpart.")
                 TransitionState.FromSplit(
-                    draggedTaskId = taskId,
+                    draggedTaskId = taskInfo.taskId,
                     dragAnimator = dragToDesktopAnimator,
                     startTransitionToken = startTransitionToken,
                     otherSplitTask = otherTask
                 )
             } else {
                 TransitionState.FromFullscreen(
-                    draggedTaskId = taskId,
+                    draggedTaskId = taskInfo.taskId,
                     dragAnimator = dragToDesktopAnimator,
                     startTransitionToken = startTransitionToken
                 )
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 1b9bf2a..dc0bc78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -304,54 +304,28 @@
         if (pipChange == null) {
             return false;
         }
-        WindowContainerToken pipTaskToken = pipChange.getContainer();
         SurfaceControl pipLeash = pipChange.getLeash();
+        Preconditions.checkNotNull(pipLeash, "Leash is null for swipe-up transition.");
 
-        if (pipTaskToken == null || pipLeash == null) {
-            return false;
-        }
-
-        SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
-        PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
-
-        Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds();
-        Rect destinationBounds = pipChange.getEndAbsBounds();
-
-        float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat();
-
-        // We fake the source rect hint when the one prvided by the app is invalid for
-        // the animation with an app icon overlay.
-        Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint()
-                : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio);
-
-        WindowContainerTransaction finishWct = new WindowContainerTransaction();
-        SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-
-        final float scale = (float) destinationBounds.width() / animationSrcRectHint.width();
-        startTransaction.setWindowCrop(pipLeash, animationSrcRectHint);
-        startTransaction.setPosition(pipLeash,
-                destinationBounds.left - animationSrcRectHint.left * scale,
-                destinationBounds.top - animationSrcRectHint.top * scale);
-        startTransaction.setScale(pipLeash, scale, scale);
-
-        if (overlayLeash != null) {
+        final Rect destinationBounds = pipChange.getEndAbsBounds();
+        final SurfaceControl swipePipToHomeOverlay = mPipTransitionState.getSwipePipToHomeOverlay();
+        if (swipePipToHomeOverlay != null) {
             final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize(
                     mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds);
-
-            // Overlay needs to be adjusted once a new draw comes in resetting surface transform.
-            tx.setScale(overlayLeash, 1f, 1f);
-            tx.setPosition(overlayLeash, (destinationBounds.width() - overlaySize) / 2f,
-                    (destinationBounds.height() - overlaySize) / 2f);
+            // It is possible we reparent the PIP activity to a new PIP task (in multi-activity
+            // apps), so we should also reparent the overlay to the final PIP task.
+            startTransaction.reparent(swipePipToHomeOverlay, pipLeash)
+                    .setLayer(swipePipToHomeOverlay, Integer.MAX_VALUE)
+                    .setScale(swipePipToHomeOverlay, 1f, 1f)
+                    .setPosition(swipePipToHomeOverlay,
+                            (destinationBounds.width() - overlaySize) / 2f,
+                            (destinationBounds.height() - overlaySize) / 2f);
         }
+
+        startTransaction.merge(finishTransaction);
         startTransaction.apply();
-
-        tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
-                        this::onClientDrawAtTransitionEnd);
-        finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
-
-        // Note that finishWct should be free of any actual WM state changes; we are using
-        // it for syncing with the client draw after delayed configuration changes are dispatched.
-        finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct);
+        finishCallback.onTransitionFinished(null /* finishWct */);
+        onClientDrawAtTransitionEnd();
         return true;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 03ff1aa..c9c0873 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Color;
+import android.graphics.Point;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -46,6 +47,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
@@ -421,6 +423,16 @@
                 if (mostRecentFreeformTaskIndex == Integer.MAX_VALUE) {
                     mostRecentFreeformTaskIndex = recentTasks.size();
                 }
+                // If task has their app bounds set to null which happens after reboot, set the
+                // app bounds to persisted lastFullscreenBounds. Also set the position in parent
+                // to the top left of the bounds.
+                if (Flags.enableDesktopWindowingPersistence()
+                        && taskInfo.configuration.windowConfiguration.getAppBounds() == null) {
+                    taskInfo.configuration.windowConfiguration.setAppBounds(
+                            taskInfo.lastNonFullscreenBounds);
+                    taskInfo.positionInParent = new Point(taskInfo.lastNonFullscreenBounds.left,
+                            taskInfo.lastNonFullscreenBounds.top);
+                }
                 freeformTasks.add(taskInfo);
                 if (mDesktopModeTaskRepository.get().isMinimizedTask(taskInfo.taskId)) {
                     minimizedFreeformTasks.add(taskInfo.taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 5daa3ee..ada1e0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -824,6 +824,7 @@
                         WindowInsets.Type.systemBars() & ~WindowInsets.Type.captionBar(),
                         false /* ignoreVisibility */);
                 relayoutParams.mCaptionTopPadding = systemBarInsets.top;
+                relayoutParams.mIsInsetSource = false;
             }
             // Report occluding elements as bounding rects to the insets system so that apps can
             // draw in the empty space in the center:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 000beba1..f8aed41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -346,7 +346,7 @@
 
     private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
             RelayoutResult<T> outResult, Rect taskBounds) {
-        if (!mIsCaptionVisible) {
+        if (!mIsCaptionVisible || !params.mIsInsetSource) {
             if (mWindowDecorationInsets != null) {
                 mWindowDecorationInsets.remove(wct);
                 mWindowDecorationInsets = null;
@@ -724,6 +724,7 @@
         int mCaptionWidthId;
         final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>();
         int mInputFeatures;
+        boolean mIsInsetSource = true;
         @InsetsSource.Flags int mInsetSourceFlags;
 
         int mShadowRadiusId;
@@ -743,6 +744,7 @@
             mCaptionWidthId = Resources.ID_NULL;
             mOccludingCaptionElements.clear();
             mInputFeatures = 0;
+            mIsInsetSource = true;
             mInsetSourceFlags = 0;
 
             mShadowRadiusId = Resources.ID_NULL;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
index 98413ee..a9a16bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
@@ -30,9 +30,13 @@
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerTransaction
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.SpringForce
 import com.android.wm.shell.R
+import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener
+import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.shared.animation.PhysicsAnimator
 import com.android.wm.shell.windowdecor.WindowManagerWrapper
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -44,7 +48,8 @@
 class DesktopWindowingEducationTooltipController(
     private val context: Context,
     private val additionalSystemViewContainerFactory: AdditionalSystemViewContainer.Factory,
-) {
+    private val displayController: DisplayController,
+) : OnDisplayChangingListener {
   // TODO: b/369384567 - Set tooltip color scheme to match LT/DT of app theme
   private var tooltipView: View? = null
   private var animator: PhysicsAnimator<View>? = null
@@ -53,6 +58,20 @@
   }
   private var popupWindow: AdditionalSystemViewContainer? = null
 
+  override fun onDisplayChange(
+      displayId: Int,
+      fromRotation: Int,
+      toRotation: Int,
+      newDisplayAreaInfo: DisplayAreaInfo?,
+      t: WindowContainerTransaction?
+  ) {
+    // Exit if the rotation hasn't changed or is changed by 180 degrees. [fromRotation] and
+    // [toRotation] can be one of the [@Surface.Rotation] values.
+    if ((fromRotation % 2 == toRotation % 2)) return
+    hideEducationTooltip()
+    // TODO: b/370820018 - Update tooltip position on orientation change instead of dismissing
+  }
+
   /**
    * Shows education tooltip.
    *
@@ -64,6 +83,7 @@
     tooltipView = createEducationTooltipView(tooltipViewConfig, taskId)
     animator = createAnimator()
     animateShowTooltipTransition()
+    displayController.addDisplayChangingController(this)
   }
 
   /** Hide the current education view if visible */
@@ -145,6 +165,7 @@
     animator = null
     popupWindow?.releaseView()
     popupWindow = null
+    displayController.removeDisplayChangingController(this)
   }
 
   private fun createTooltipPopupWindow(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index ca97229..d7a132d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -16,13 +16,22 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.window.flags.Flags
+import com.android.wm.shell.EventLogTags
+import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskSizeUpdate
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON
@@ -34,14 +43,19 @@
 /**
  * Tests for [DesktopModeEventLogger].
  */
-class DesktopModeEventLoggerTest {
+class DesktopModeEventLoggerTest : ShellTestCase() {
 
     private val desktopModeEventLogger = DesktopModeEventLogger()
 
     @JvmField
-    @Rule
+    @Rule(order = 0)
     val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
-            .mockStatic(FrameworkStatsLog::class.java).build()!!
+            .mockStatic(FrameworkStatsLog::class.java)
+            .mockStatic(EventLogTags::class.java).build()!!
+
+    @JvmField
+    @Rule(order = 1)
+    val setFlagsRule = SetFlagsRule()
 
     @Test
     fun logSessionEnter_enterReason() = runBlocking {
@@ -60,6 +74,11 @@
                 eq(SESSION_ID)
             )
         }
+        verify {
+            EventLogTags.writeWmShellEnterDesktopMode(
+                eq(EnterReason.UNKNOWN_ENTER.reason),
+                eq(SESSION_ID))
+        }
     }
 
     @Test
@@ -79,6 +98,11 @@
                 eq(SESSION_ID)
             )
         }
+        verify {
+            EventLogTags.writeWmShellExitDesktopMode(
+                eq(ExitReason.UNKNOWN_EXIT.reason),
+                eq(SESSION_ID))
+        }
     }
 
     @Test
@@ -108,6 +132,22 @@
                 /* visible_task_count */
                 eq(TASK_COUNT))
         }
+
+        verify {
+            EventLogTags.writeWmShellDesktopModeTaskUpdate(
+                eq(FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
+                eq(TASK_UPDATE.instanceId),
+                eq(TASK_UPDATE.uid),
+                eq(TASK_UPDATE.taskHeight),
+                eq(TASK_UPDATE.taskWidth),
+                eq(TASK_UPDATE.taskX),
+                eq(TASK_UPDATE.taskY),
+                eq(SESSION_ID),
+                eq(UNSET_MINIMIZE_REASON),
+                eq(UNSET_UNMINIMIZE_REASON),
+                eq(TASK_COUNT))
+        }
     }
 
     @Test
@@ -137,6 +177,22 @@
                 /* visible_task_count */
                 eq(TASK_COUNT))
         }
+
+        verify {
+            EventLogTags.writeWmShellDesktopModeTaskUpdate(
+                eq(FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
+                eq(TASK_UPDATE.instanceId),
+                eq(TASK_UPDATE.uid),
+                eq(TASK_UPDATE.taskHeight),
+                eq(TASK_UPDATE.taskWidth),
+                eq(TASK_UPDATE.taskX),
+                eq(TASK_UPDATE.taskY),
+                eq(SESSION_ID),
+                eq(UNSET_MINIMIZE_REASON),
+                eq(UNSET_UNMINIMIZE_REASON),
+                eq(TASK_COUNT))
+        }
     }
 
     @Test
@@ -167,6 +223,22 @@
                 /* visible_task_count */
                 eq(TASK_COUNT))
         }
+
+        verify {
+            EventLogTags.writeWmShellDesktopModeTaskUpdate(
+                eq(FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(TASK_UPDATE.instanceId),
+                eq(TASK_UPDATE.uid),
+                eq(TASK_UPDATE.taskHeight),
+                eq(TASK_UPDATE.taskWidth),
+                eq(TASK_UPDATE.taskX),
+                eq(TASK_UPDATE.taskY),
+                eq(SESSION_ID),
+                eq(UNSET_MINIMIZE_REASON),
+                eq(UNSET_UNMINIMIZE_REASON),
+                eq(TASK_COUNT))
+        }
     }
 
     @Test
@@ -200,6 +272,22 @@
                 /* visible_task_count */
                 eq(TASK_COUNT))
         }
+
+        verify {
+            EventLogTags.writeWmShellDesktopModeTaskUpdate(
+                eq(FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(TASK_UPDATE.instanceId),
+                eq(TASK_UPDATE.uid),
+                eq(TASK_UPDATE.taskHeight),
+                eq(TASK_UPDATE.taskWidth),
+                eq(TASK_UPDATE.taskX),
+                eq(TASK_UPDATE.taskY),
+                eq(SESSION_ID),
+                eq(MinimizeReason.TASK_LIMIT.reason),
+                eq(UNSET_UNMINIMIZE_REASON),
+                eq(TASK_COUNT))
+        }
     }
 
     @Test
@@ -233,9 +321,83 @@
                 /* visible_task_count */
                 eq(TASK_COUNT))
         }
+
+        verify {
+            EventLogTags.writeWmShellDesktopModeTaskUpdate(
+                eq(FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(TASK_UPDATE.instanceId),
+                eq(TASK_UPDATE.uid),
+                eq(TASK_UPDATE.taskHeight),
+                eq(TASK_UPDATE.taskWidth),
+                eq(TASK_UPDATE.taskX),
+                eq(TASK_UPDATE.taskY),
+                eq(SESSION_ID),
+                eq(UNSET_MINIMIZE_REASON),
+                eq(UnminimizeReason.TASKBAR_TAP.reason),
+                eq(TASK_COUNT))
+        }
     }
 
-    companion object {
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
+    fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() = runBlocking {
+        desktopModeEventLogger.logTaskResizingStarted(sessionId = SESSION_ID, createTaskSizeUpdate())
+
+        verify {
+            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+                /* resize_trigger */
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
+                /* resizing_stage */
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE),
+                /* input_method */
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
+                /* desktop_mode_session_id */
+                eq(SESSION_ID),
+                /* instance_id */
+                eq(TASK_SIZE_UPDATE.instanceId),
+                /* uid */
+                eq(TASK_SIZE_UPDATE.uid),
+                /* task_height */
+                eq(TASK_SIZE_UPDATE.taskHeight),
+                /* task_width */
+                eq(TASK_SIZE_UPDATE.taskWidth),
+                /* display_area */
+                eq(TASK_SIZE_UPDATE.displayArea),
+            )
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
+    fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() = runBlocking {
+        desktopModeEventLogger.logTaskResizingEnded(sessionId = SESSION_ID, createTaskSizeUpdate())
+
+        verify {
+            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+                /* resize_trigger */
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
+                /* resizing_stage */
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE),
+                /* input_method */
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
+                /* desktop_mode_session_id */
+                eq(SESSION_ID),
+                /* instance_id */
+                eq(TASK_SIZE_UPDATE.instanceId),
+                /* uid */
+                eq(TASK_SIZE_UPDATE.uid),
+                /* task_height */
+                eq(TASK_SIZE_UPDATE.taskHeight),
+                /* task_width */
+                eq(TASK_SIZE_UPDATE.taskWidth),
+                /* display_area */
+                eq(TASK_SIZE_UPDATE.displayArea),
+            )
+        }
+    }
+
+    private companion object {
         private const val SESSION_ID = 1
         private const val TASK_ID = 1
         private const val TASK_UID = 1
@@ -244,16 +406,40 @@
         private const val TASK_HEIGHT = 100
         private const val TASK_WIDTH = 100
         private const val TASK_COUNT = 1
+        private const val DISPLAY_AREA = 1000
 
         private val TASK_UPDATE = TaskUpdate(
             TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y,
             visibleTaskCount = TASK_COUNT,
         )
 
+        private val TASK_SIZE_UPDATE = TaskSizeUpdate(
+            resizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
+            inputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
+            TASK_ID,
+            TASK_UID,
+            TASK_HEIGHT,
+            TASK_WIDTH,
+            DISPLAY_AREA,
+        )
+
+        private fun createTaskSizeUpdate(
+            resizeTrigger: ResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
+            inputMethod: InputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
+        ) = TaskSizeUpdate(
+            resizeTrigger,
+            inputMethod,
+            TASK_ID,
+            TASK_UID,
+            TASK_HEIGHT,
+            TASK_WIDTH,
+            DISPLAY_AREA,
+        )
+
         private fun createTaskUpdate(
             minimizeReason: MinimizeReason? = null,
             unminimizeReason: UnminimizeReason? = null,
         ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
             unminimizeReason, TASK_COUNT)
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index bc5ae97..bdcb459 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -19,6 +19,8 @@
 import android.app.ActivityManager.RunningTaskInfo
 import android.os.Binder
 import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
@@ -33,6 +35,7 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.ShellExecutor
@@ -243,6 +246,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
     fun removeLeftoverMinimizedTasks_activeNonMinimizedTasksStillAround_doesNothing() {
         desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
         desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
@@ -256,6 +260,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
     fun removeLeftoverMinimizedTasks_noMinimizedTasks_doesNothing() {
         val wct = WindowContainerTransaction()
         desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
@@ -265,6 +270,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
     fun removeLeftoverMinimizedTasks_onlyMinimizedTasksLeft_removesAllMinimizedTasks() {
         val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
         val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -283,6 +289,20 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun removeLeftoverMinimizedTasks_onlyMinimizedTasksLeft_backNavEnabled_doesNothing() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
+        desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+
+        val wct = WindowContainerTransaction()
+        desktopTasksLimiter.leftoverMinimizedTasksRemover.onActiveTasksChanged(DEFAULT_DISPLAY)
+
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
     fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() {
         (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index c989d16..42fcc83 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -18,11 +18,13 @@
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
 import android.platform.test.annotations.EnableFlags
 import android.view.Display.DEFAULT_DISPLAY
+import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.IWindowContainerToken
 import android.window.TransitionInfo
@@ -110,6 +112,24 @@
         verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId)
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun removeTasks_onTaskFullscreenLaunch_taskRemovedFromRepo() {
+        val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)
+        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
+        whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+        transitionObserver.onTransitionReady(
+            transition = mock(),
+            info = createOpenTransition(task),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId)
+        verify(taskRepository).removeFreeformTask(task.displayId, task.taskId)
+    }
+
     private fun createBackNavigationTransition(
         task: RunningTaskInfo?
     ): TransitionInfo {
@@ -125,11 +145,26 @@
         }
     }
 
-    private fun createTaskInfo(id: Int) =
+    private fun createOpenTransition(
+        task: RunningTaskInfo?
+    ): TransitionInfo {
+        return TransitionInfo(TRANSIT_OPEN, 0 /* flags */).apply {
+            addChange(
+                Change(mock(), mock()).apply {
+                    mode = TRANSIT_OPEN
+                    parent = null
+                    taskInfo = task
+                    flags = flags
+                }
+            )
+        }
+    }
+
+    private fun createTaskInfo(id: Int, windowingMode: Int = WINDOWING_MODE_FREEFORM) =
         RunningTaskInfo().apply {
             taskId = id
             displayId = DEFAULT_DISPLAY
-            configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+            configuration.windowConfiguration.windowingMode = windowingMode
             token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
             baseIntent = Intent().apply {
                 component = ComponentName("package", "component.name")
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index d9387d2..230f7e6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -581,7 +581,7 @@
                 )
             )
             .thenReturn(token)
-        handler.startDragToDesktopTransition(task.taskId, dragAnimator)
+        handler.startDragToDesktopTransition(task, dragAnimator)
         return token
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 753d4cd..0364b51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
 
 import static org.junit.Assert.assertEquals;
@@ -50,6 +51,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.platform.test.annotations.DisableFlags;
@@ -441,6 +443,40 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    public void testGetRecentTasks_hasDesktopTasks_persistenceEnabled_freeformTaskHaveBoundsSet() {
+        ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+        ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+
+        t1.lastNonFullscreenBounds = new Rect(100, 200, 300, 400);
+        t2.lastNonFullscreenBounds = new Rect(150, 250, 350, 450);
+        setRawList(t1, t2);
+
+        when(mDesktopModeTaskRepository.isActiveTask(1)).thenReturn(true);
+        when(mDesktopModeTaskRepository.isActiveTask(2)).thenReturn(true);
+
+        ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+                MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+
+        assertEquals(1, recentTasks.size());
+        GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
+
+        // Check bounds
+        assertEquals(t1.lastNonFullscreenBounds, freeformGroup.getTaskInfoList().get(
+                0).configuration.windowConfiguration.getAppBounds());
+        assertEquals(t2.lastNonFullscreenBounds, freeformGroup.getTaskInfoList().get(
+                1).configuration.windowConfiguration.getAppBounds());
+
+        // Check position in parent
+        assertEquals(new Point(t1.lastNonFullscreenBounds.left,
+                        t1.lastNonFullscreenBounds.top),
+                freeformGroup.getTaskInfoList().get(0).positionInParent);
+        assertEquals(new Point(t2.lastNonFullscreenBounds.left,
+                        t2.lastNonFullscreenBounds.top),
+                freeformGroup.getTaskInfoList().get(1).positionInParent);
+    }
+
+    @Test
     public void testRemovedTaskRemovesSplit() {
         ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
         ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -623,6 +659,7 @@
     private ActivityManager.RecentTaskInfo makeTaskInfo(int taskId) {
         ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
         info.taskId = taskId;
+        info.lastNonFullscreenBounds = new Rect();
         return info;
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 3e7f3bd..ed752fd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -619,6 +619,27 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    public void updateRelayoutParams_header_notAnInsetsSourceInFullyImmersive() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false,
+                /* isStatusBarVisible */ true,
+                /* isKeyguardVisibleAndOccluded */ false,
+                /* inFullImmersiveMode */ true,
+                new InsetsState());
+
+        assertThat(relayoutParams.mIsInsetSource).isFalse();
+    }
+
+    @Test
     @DisableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
     public void updateRelayoutParams_header_statusBarInvisible_captionVisible() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 94cabc4..54dd15ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -650,6 +650,57 @@
     }
 
     @Test
+    public void testRelayout_notAnInsetsSource_doesNotAddInsets() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true)
+                .setBounds(new Rect(0, 0, 1000, 1000))
+                .build();
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+        mRelayoutParams.mIsInsetSource = false;
+        windowDecor.relayout(taskInfo);
+
+        // Never added.
+        verify(mMockWindowContainerTransaction, never()).addInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(captionBar()), any(), any(), anyInt());
+        verify(mMockWindowContainerTransaction, never()).addInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(mandatorySystemGestures()), any(), any(), anyInt());
+    }
+
+    @Test
+    public void testRelayout_notAnInsetsSource_hadInsetsBefore_removesInsets() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true)
+                .setBounds(new Rect(0, 0, 1000, 1000))
+                .build();
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+        mRelayoutParams.mIsCaptionVisible = true;
+        mRelayoutParams.mIsInsetSource = true;
+        windowDecor.relayout(taskInfo);
+
+        mRelayoutParams.mIsCaptionVisible = true;
+        mRelayoutParams.mIsInsetSource = false;
+        windowDecor.relayout(taskInfo);
+
+        // Insets should be removed.
+        verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(captionBar()));
+        verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+                eq(0) /* index */, eq(mandatorySystemGestures()));
+    }
+
+    @Test
     public void testClose_withExistingInsets_insetsRemoved() {
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
index 5594981..6749776 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
@@ -24,12 +24,16 @@
 import android.testing.TestableLooper
 import android.testing.TestableResources
 import android.view.MotionEvent
+import android.view.Surface.ROTATION_180
+import android.view.Surface.ROTATION_90
 import android.view.View
 import android.view.WindowManager
 import android.widget.TextView
+import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.R
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipArrowDirection
 import com.google.common.truth.Truth.assertThat
@@ -42,9 +46,11 @@
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -52,6 +58,8 @@
 class DesktopWindowingEducationTooltipControllerTest : ShellTestCase() {
   @Mock private lateinit var mockWindowManager: WindowManager
   @Mock private lateinit var mockViewContainerFactory: AdditionalSystemViewContainer.Factory
+  @Mock private lateinit var mockDisplayController: DisplayController
+  @Mock private lateinit var mockPopupWindow: AdditionalSystemViewContainer
   private lateinit var testableResources: TestableResources
   private lateinit var testableContext: TestableContext
   private lateinit var tooltipController: DesktopWindowingEducationTooltipController
@@ -69,7 +77,8 @@
         Context.LAYOUT_INFLATER_SERVICE, context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
     testableContext.addMockSystemService(WindowManager::class.java, mockWindowManager)
     tooltipController =
-        DesktopWindowingEducationTooltipController(testableContext, mockViewContainerFactory)
+        DesktopWindowingEducationTooltipController(
+            testableContext, mockViewContainerFactory, mockDisplayController)
   }
 
   @Test
@@ -218,6 +227,25 @@
     verify(mockLambda).invoke()
   }
 
+  @Test
+  fun showEducationTooltip_displayRotationChanged_hidesTooltip() {
+    whenever(
+            mockViewContainerFactory.create(any(), any(), any(), any(), any(), any(), any(), any()))
+        .thenReturn(mockPopupWindow)
+    val tooltipViewConfig = createTooltipConfig()
+
+    tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123)
+    tooltipController.onDisplayChange(
+        /* displayId= */ 123,
+        /* fromRotation= */ ROTATION_90,
+        /* toRotation= */ ROTATION_180,
+        /* newDisplayAreaInfo= */ null,
+        WindowContainerTransaction())
+
+    verify(mockPopupWindow, times(1)).releaseView()
+    verify(mockDisplayController, atLeastOnce()).removeDisplayChangingController(any())
+  }
+
   private fun createTooltipConfig(
       @LayoutRes tooltipViewLayout: Int = R.layout.desktop_windowing_education_top_arrow_tooltip,
       tooltipViewGlobalCoordinates: Point = Point(0, 0),
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index bb0fc41..bc269fe 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -16,7 +16,7 @@
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
-    method @Deprecated @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @Deprecated @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
     field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
@@ -45,12 +45,12 @@
     method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
     field public static final String PROPERTY_RETURN_VALUE = "returnValue";
     field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
+    field public static final int RESULT_CANCELLED = 6; // 0x6
     field public static final int RESULT_DENIED = 1; // 0x1
-    field public static final int RESULT_DISABLED = 6; // 0x6
+    field public static final int RESULT_DISABLED = 5; // 0x5
     field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
     field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
     field public static final int RESULT_OK = 0; // 0x0
-    field public static final int RESULT_TIMED_OUT = 5; // 0x5
   }
 
 }
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
index 6023c97..6e91de6 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
@@ -26,6 +26,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.CancellationSignal;
+import android.util.Log;
 
 import java.util.function.Consumer;
 
@@ -143,7 +144,11 @@
      */
     @MainThread
     @Deprecated
-    public abstract void onExecuteFunction(
+    public void onExecuteFunction(
             @NonNull ExecuteAppFunctionRequest request,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+        Log.w(
+                "AppFunctionService",
+                "Calling deprecated default implementation of onExecuteFunction");
+    }
 }
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
index c7ce95b..d87fec79 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
@@ -73,11 +73,14 @@
      */
     public static final int RESULT_INVALID_ARGUMENT = 4;
 
-    /** The operation was timed out. */
-    public static final int RESULT_TIMED_OUT = 5;
-
     /** The caller tried to execute a disabled app function. */
-    public static final int RESULT_DISABLED = 6;
+    public static final int RESULT_DISABLED = 5;
+
+    /**
+     * The operation was cancelled. Use this error code to report that a cancellation is done after
+     * receiving a cancellation signal.
+     */
+    public static final int RESULT_CANCELLED = 6;
 
     /** The result code of the app function execution. */
     @ResultCode private final int mResultCode;
@@ -236,7 +239,6 @@
                     RESULT_APP_UNKNOWN_ERROR,
                     RESULT_INTERNAL_ERROR,
                     RESULT_INVALID_ARGUMENT,
-                    RESULT_TIMED_OUT,
                     RESULT_DISABLED
             })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index 27add35..4305196 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -141,6 +141,13 @@
         return;
     }
 
+    if (!RenderThread::isCurrent()) {
+        // releaseQueueOwnership needs to run on RenderThread to prevent multithread calling
+        // setBackendTextureState will operate skia resource cache which need single owner
+        RenderThread::getInstance().queue().post([this, context]() { releaseQueueOwnership(context); });
+        return;
+    }
+
     LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
     if (mBackendTexture.isValid()) {
         // Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout.
diff --git a/libs/hwui/Gainmap.cpp b/libs/hwui/Gainmap.cpp
index 30f401e..ea955e2 100644
--- a/libs/hwui/Gainmap.cpp
+++ b/libs/hwui/Gainmap.cpp
@@ -15,12 +15,37 @@
  */
 #include "Gainmap.h"
 
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColorFilter.h>
+#include <SkImagePriv.h>
+#include <SkPaint.h>
+
+#include "HardwareBitmapUploader.h"
+
 namespace android::uirenderer {
 
 sp<Gainmap> Gainmap::allocateHardwareGainmap(const sp<Gainmap>& srcGainmap) {
     auto gainmap = sp<Gainmap>::make();
     gainmap->info = srcGainmap->info;
-    const SkBitmap skSrcBitmap = srcGainmap->bitmap->getSkBitmap();
+    SkBitmap skSrcBitmap = srcGainmap->bitmap->getSkBitmap();
+    if (skSrcBitmap.info().colorType() == kAlpha_8_SkColorType &&
+        !HardwareBitmapUploader::hasAlpha8Support()) {
+        // The regular Bitmap::allocateHardwareBitmap will do a conversion that preserves channels,
+        // so alpha8 maps to the alpha channel of rgba. However, for gainmaps we will interpret
+        // the data of an rgba buffer differently as we'll only look at the rgb channels
+        // So we need to map alpha8 to rgbx_8888 essentially
+        SkBitmap bitmap;
+        bitmap.allocPixels(skSrcBitmap.info().makeColorType(kN32_SkColorType));
+        SkCanvas canvas(bitmap);
+        SkPaint paint;
+        const float alphaToOpaque[] = {0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+                                       0, 0, 0, 1, 0, 0, 0, 0, 0, 255};
+        paint.setColorFilter(SkColorFilters::Matrix(alphaToOpaque, SkColorFilters::Clamp::kNo));
+        canvas.drawImage(SkMakeImageFromRasterBitmap(skSrcBitmap, kNever_SkCopyPixelsMode), 0, 0,
+                         SkSamplingOptions{}, &paint);
+        skSrcBitmap = bitmap;
+    }
     sk_sp<Bitmap> skBitmap(Bitmap::allocateHardwareBitmap(skSrcBitmap));
     if (!skBitmap.get()) {
         return nullptr;
diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h
index 78db54a..91db134 100644
--- a/libs/hwui/jni/graphics_jni_helpers.h
+++ b/libs/hwui/jni/graphics_jni_helpers.h
@@ -80,9 +80,52 @@
     return static_cast<T>(res);
 }
 
+//  Inline variable that specifies the method binding format.
+//  The expected format is 'XX${method}XX', where ${method} represents the original method name.
+//  This variable is shared across all translation units. This is treated as a global variable as
+//  per C++ 17.
+inline std::string jniMethodFormat;
+
+inline static void setJniMethodFormat(std::string value) {
+    jniMethodFormat = value;
+}
+
+// Register the native methods, potenially applying the jniMethodFormat if it has been set.
+static inline int jniRegisterMaybeRenamedNativeMethods(JNIEnv* env, const char* className,
+                                                       const JNINativeMethod* gMethods,
+                                                       int numMethods) {
+    if (jniMethodFormat.empty()) {
+        return jniRegisterNativeMethods(env, className, gMethods, numMethods);
+    }
+
+    // Make a copy of gMethods with reformatted method names.
+    JNINativeMethod* modifiedMethods = new JNINativeMethod[numMethods];
+    LOG_ALWAYS_FATAL_IF(!modifiedMethods, "Failed to allocate a copy of the JNI methods");
+
+    size_t methodNamePos = jniMethodFormat.find("${method}");
+    LOG_ALWAYS_FATAL_IF(methodNamePos == std::string::npos,
+                        "Invalid jniMethodFormat: could not find '${method}' in pattern");
+
+    for (int i = 0; i < numMethods; i++) {
+        modifiedMethods[i] = gMethods[i];
+        std::string modifiedName = jniMethodFormat;
+        modifiedName.replace(methodNamePos, 9, gMethods[i].name);
+        char* modifiedNameChars = new char[modifiedName.length() + 1];
+        LOG_ALWAYS_FATAL_IF(!modifiedNameChars, "Failed to allocate the new method name");
+        std::strcpy(modifiedNameChars, modifiedName.c_str());
+        modifiedMethods[i].name = modifiedNameChars;
+    }
+    int res = jniRegisterNativeMethods(env, className, modifiedMethods, numMethods);
+    for (int i = 0; i < numMethods; i++) {
+        delete[] modifiedMethods[i].name;
+    }
+    delete[] modifiedMethods;
+    return res;
+}
+
 static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
                                        const JNINativeMethod* gMethods, int numMethods) {
-    int res = jniRegisterNativeMethods(env, className, gMethods, numMethods);
+    int res = jniRegisterMaybeRenamedNativeMethods(env, className, gMethods, numMethods);
     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
     return res;
 }
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index e7cb76c..96b7c13 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -223,6 +223,7 @@
     field public static final String CATEGORY_PAYMENT = "payment";
     field public static final String EXTRA_CATEGORY = "category";
     field public static final String EXTRA_SERVICE_COMPONENT = "component";
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT = 3; // 0x3
     field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; // 0x0
     field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; // 0x1
     field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2; // 0x2
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 94231b0..4428ade 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -58,12 +58,16 @@
   @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean hasUserEnabledNfc();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAutoChangeEnabled();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagPresent();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overwriteRoutingTable(int, int, int);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void pausePolling(int);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void resumePolling();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAutoChangeEnabled(boolean);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOnMode(int);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void triggerInitialization();
@@ -90,7 +94,11 @@
     method public void onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
     method public void onEnableFinished(int);
     method public void onEnableStarted();
+    method public void onGetOemAppSearchIntent(@NonNull java.util.List<java.lang.String>, @NonNull java.util.function.Consumer<android.content.Intent>);
     method public void onHceEventReceived(int);
+    method public void onLaunchHceAppChooserActivity(@NonNull String, @NonNull java.util.List<android.nfc.cardemulation.ApduServiceInfo>, @NonNull android.content.ComponentName, @NonNull String);
+    method public void onLaunchHceTapAgainDialog(@NonNull android.nfc.cardemulation.ApduServiceInfo, @NonNull String);
+    method public void onNdefMessage(@NonNull android.nfc.Tag, @NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>);
     method public void onReaderOptionChanged(boolean);
     method public void onRfDiscoveryStarted(boolean);
@@ -101,6 +109,12 @@
     method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>);
   }
 
+  @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingStatus {
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultIsoDepRoute();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultOffHostRoute();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultRoute();
+  }
+
 }
 
 package android.nfc.cardemulation {
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 19b9e0f..1eae3c6 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -51,4 +51,8 @@
     void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg);
     void recoverRoutingTable(int userHandle);
     boolean isEuiccSupported();
+    void setAutoChangeStatus(boolean state);
+    boolean isAutoChangeEnabled();
+    List<String> getRoutingStatus();
+    void overwriteRoutingTable(int userHandle, String emptyAid, String protocol, String tech);
 }
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index e49ef7e..48c7ee6 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -15,9 +15,14 @@
  */
 package android.nfc;
 
+import android.content.ComponentName;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.NdefMessage;
 import android.nfc.Tag;
 import android.os.ResultReceiver;
 
+import java.util.List;
+
 /**
  * @hide
  */
@@ -41,4 +46,8 @@
    void onCardEmulationActivated(boolean isActivated);
    void onRfFieldActivated(boolean isActivated);
    void onRfDiscoveryStarted(boolean isDiscoveryStarted);
+   void onGetOemAppSearchIntent(in List<String> firstPackage, in ResultReceiver intentConsumer);
+   void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent);
+   void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category);
+   void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category);
 }
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 6d5c069..fb63b5c 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -16,6 +16,12 @@
 
 package android.nfc;
 
+import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_DH;
+import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE;
+import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC;
+import static android.nfc.cardemulation.CardEmulation.routeIntToString;
+
+import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -23,8 +29,14 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.util.Log;
@@ -306,6 +318,60 @@
         * @param isDiscoveryStarted true, if RF discovery started, else RF state is Idle.
         */
         void onRfDiscoveryStarted(boolean isDiscoveryStarted);
+
+        /**
+         * Gets the intent to find the OEM package in the OEM App market. If the consumer returns
+         * {@code null} or a timeout occurs, the intent from the first available package will be
+         * used instead.
+         *
+         * @param packages the OEM packages name stored in the tag
+         * @param intentConsumer The {@link Consumer} to be completed.
+         *                       The {@link Consumer#accept(Object)} should be called with
+         *                       the Intent required.
+         *
+         */
+        void onGetOemAppSearchIntent(@NonNull List<String> packages,
+                                     @NonNull Consumer<Intent> intentConsumer);
+
+        /**
+         * Checks if the NDEF message contains any specific OEM package executable content
+         *
+         * @param tag        the {@link android.nfc.Tag Tag}
+         * @param message NDEF Message to read from tag
+         * @param hasOemExecutableContent The {@link Consumer} to be completed. If there is
+         *                                OEM package executable content, the
+         *                                {@link Consumer#accept(Object)} should be called with
+         *                                {@link Boolean#TRUE}, otherwise call with
+         *                                {@link Boolean#FALSE}.
+         */
+        void onNdefMessage(@NonNull Tag tag, @NonNull NdefMessage message,
+                           @NonNull Consumer<Boolean> hasOemExecutableContent);
+
+        /**
+         * Callback to indicate the app chooser activity should be launched for handling CE
+         * transaction. This is invoked for example when there are more than 1 app installed that
+         * can handle the HCE transaction. OEMs can launch the Activity based
+         * on their requirement.
+         *
+         * @param selectedAid the selected AID from APDU
+         * @param services {@link ApduServiceInfo} of the service triggering the activity
+         * @param failedComponent the component failed to be resolved
+         * @param category the category of the service
+         */
+        void onLaunchHceAppChooserActivity(@NonNull String selectedAid,
+                                           @NonNull List<ApduServiceInfo> services,
+                                           @NonNull ComponentName failedComponent,
+                                           @NonNull String category);
+
+        /**
+         * Callback to indicate tap again dialog should be launched for handling HCE transaction.
+         * This is invoked for example when a CE service needs the device to unlocked before
+         * handling the transaction. OEMs can launch the Activity based on their requirement.
+         *
+         * @param service {@link ApduServiceInfo} of the service triggering the dialog
+         * @param category the category of the service
+         */
+        void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category);
     }
 
 
@@ -523,6 +589,85 @@
         NfcAdapter.callService(() -> NfcAdapter.sService.resumePolling());
     }
 
+    /**
+     * Set whether to enable auto routing change or not (enabled by default).
+     * If disabled, routing targets are limited to a single off-host destination.
+     *
+     * @param state status of auto routing change, true if enable, otherwise false
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void setAutoChangeEnabled(boolean state) {
+        NfcAdapter.callService(() ->
+                NfcAdapter.sCardEmulationService.setAutoChangeStatus(state));
+    }
+
+    /**
+     * Check if auto routing change is enabled or not.
+     *
+     * @return true if enabled, otherwise false
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public boolean isAutoChangeEnabled() {
+        return NfcAdapter.callServiceReturn(() ->
+                NfcAdapter.sCardEmulationService.isAutoChangeEnabled(), false);
+    }
+
+    /**
+     * Get current routing status
+     *
+     * @return {@link RoutingStatus} indicating the default route, default ISO-DEP
+     * route and default off-host route.
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public RoutingStatus getRoutingStatus() {
+        List<String> status = NfcAdapter.callServiceReturn(() ->
+                NfcAdapter.sCardEmulationService.getRoutingStatus(), new ArrayList<>());
+        return new RoutingStatus(routeStringToInt(status.get(0)),
+                routeStringToInt(status.get(1)),
+                routeStringToInt(status.get(2)));
+    }
+
+    /**
+     * Overwrites NFC controller routing table, which includes Protocol Route, Technology Route,
+     * and Empty AID Route.
+     *
+     * The parameter set to
+     * {@link ProtocolAndTechnologyRoute#PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET}
+     * can be used to keep current values for that entry. At least one route should be overridden
+     * when calling this API, otherwise throw {@link IllegalArgumentException}.
+     *
+     * @param protocol ISO-DEP route destination, where the possible inputs are defined in
+     *                 {@link ProtocolAndTechnologyRoute}.
+     * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs
+     *                   are defined in
+     *                   {@link ProtocolAndTechnologyRoute}
+     * @param emptyAid Zero-length AID route destination, where the possible inputs are defined in
+     *                 {@link ProtocolAndTechnologyRoute}
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    public void overwriteRoutingTable(
+            @CardEmulation.ProtocolAndTechnologyRoute int protocol,
+            @CardEmulation.ProtocolAndTechnologyRoute int technology,
+            @CardEmulation.ProtocolAndTechnologyRoute int emptyAid) {
+
+        String protocolRoute = routeIntToString(protocol);
+        String technologyRoute = routeIntToString(technology);
+        String emptyAidRoute = routeIntToString(emptyAid);
+
+        NfcAdapter.callService(() ->
+                NfcAdapter.sCardEmulationService.overwriteRoutingTable(
+                        mContext.getUser().getIdentifier(),
+                        emptyAidRoute,
+                        protocolRoute,
+                        technologyRoute
+                ));
+    }
+
     private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
 
         @Override
@@ -562,25 +707,25 @@
         public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException {
             mCallbackMap.forEach((cb, ex) ->
                     handleVoidCallback(
-                        new ReceiverWrapper(isSkipped), cb::onApplyRouting, ex));
+                        new ReceiverWrapper<>(isSkipped), cb::onApplyRouting, ex));
         }
         @Override
         public void onNdefRead(ResultReceiver isSkipped) throws RemoteException {
             mCallbackMap.forEach((cb, ex) ->
                     handleVoidCallback(
-                        new ReceiverWrapper(isSkipped), cb::onNdefRead, ex));
+                        new ReceiverWrapper<>(isSkipped), cb::onNdefRead, ex));
         }
         @Override
         public void onEnable(ResultReceiver isAllowed) throws RemoteException {
             mCallbackMap.forEach((cb, ex) ->
                     handleVoidCallback(
-                        new ReceiverWrapper(isAllowed), cb::onEnable, ex));
+                        new ReceiverWrapper<>(isAllowed), cb::onEnable, ex));
         }
         @Override
         public void onDisable(ResultReceiver isAllowed) throws RemoteException {
             mCallbackMap.forEach((cb, ex) ->
                     handleVoidCallback(
-                        new ReceiverWrapper(isAllowed), cb::onDisable, ex));
+                        new ReceiverWrapper<>(isAllowed), cb::onDisable, ex));
         }
         @Override
         public void onBootStarted() throws RemoteException {
@@ -616,7 +761,7 @@
         public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException {
             mCallbackMap.forEach((cb, ex) ->
                     handleVoidCallback(
-                        new ReceiverWrapper(isSkipped), cb::onTagDispatch, ex));
+                        new ReceiverWrapper<>(isSkipped), cb::onTagDispatch, ex));
         }
         @Override
         public void onRoutingChanged() throws RemoteException {
@@ -635,6 +780,59 @@
                     handleVoidCallback(enabled, cb::onReaderOptionChanged, ex));
         }
 
+        @Override
+        public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer)
+                throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoid2ArgCallback(packages, new ReceiverWrapper<>(intentConsumer),
+                            cb::onGetOemAppSearchIntent, ex));
+        }
+
+        @Override
+        public void onNdefMessage(Tag tag, NdefMessage message,
+                                  ResultReceiver hasOemExecutableContent) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) -> {
+                synchronized (mLock) {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        ex.execute(() -> cb.onNdefMessage(
+                                tag, message, new ReceiverWrapper<>(hasOemExecutableContent)));
+                    } catch (RuntimeException exception) {
+                        throw exception;
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onLaunchHceAppChooserActivity(String selectedAid,
+                                                  List<ApduServiceInfo> services,
+                                                  ComponentName failedComponent, String category)
+                throws RemoteException {
+            mCallbackMap.forEach((cb, ex) -> {
+                synchronized (mLock) {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        ex.execute(() -> cb.onLaunchHceAppChooserActivity(
+                                selectedAid, services, failedComponent, category));
+                    } catch (RuntimeException exception) {
+                        throw exception;
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onLaunchHceTapAgainActivity(ApduServiceInfo service, String category)
+                throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoid2ArgCallback(service, category, cb::onLaunchHceTapAgainDialog, ex));
+        }
+
         private <T> void handleVoidCallback(
                 T input, Consumer<T> callbackMethod, Executor executor) {
             synchronized (mLock) {
@@ -718,7 +916,16 @@
         }
     }
 
-    private class ReceiverWrapper implements Consumer<Boolean> {
+    private @CardEmulation.ProtocolAndTechnologyRoute int routeStringToInt(String route) {
+        return switch (route) {
+            case "DH" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_DH;
+            case "eSE" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE;
+            case "SIM" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC;
+            default -> throw new IllegalStateException("Unexpected value: " + route);
+        };
+    }
+
+    private class ReceiverWrapper<T> implements Consumer<T> {
         private final ResultReceiver mResultReceiver;
 
         ReceiverWrapper(ResultReceiver resultReceiver) {
@@ -726,12 +933,19 @@
         }
 
         @Override
-        public void accept(Boolean result) {
-            mResultReceiver.send(result ? 1 : 0, null);
+        public void accept(T result) {
+            if (result instanceof Boolean) {
+                mResultReceiver.send((Boolean) result ? 1 : 0, null);
+            } else if (result instanceof Intent) {
+                Bundle bundle = new Bundle();
+                bundle.putParcelable("intent", (Intent) result);
+                mResultReceiver.send(0, bundle);
+            }
+
         }
 
         @Override
-        public Consumer<Boolean> andThen(Consumer<? super Boolean> after) {
+        public Consumer<T> andThen(Consumer<? super T> after) {
             return Consumer.super.andThen(after);
         }
     }
diff --git a/nfc/java/android/nfc/RoutingStatus.java b/nfc/java/android/nfc/RoutingStatus.java
new file mode 100644
index 0000000..4a1b1f3
--- /dev/null
+++ b/nfc/java/android/nfc/RoutingStatus.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.nfc.cardemulation.CardEmulation;
+
+/**
+ * A class indicating default route, ISO-DEP route and off-host route.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+public class RoutingStatus {
+    private final @CardEmulation.ProtocolAndTechnologyRoute int mDefaultRoute;
+    private final @CardEmulation.ProtocolAndTechnologyRoute int mDefaultIsoDepRoute;
+    private final @CardEmulation.ProtocolAndTechnologyRoute int mDefaultOffHostRoute;
+
+    RoutingStatus(@CardEmulation.ProtocolAndTechnologyRoute int mDefaultRoute,
+                  @CardEmulation.ProtocolAndTechnologyRoute int mDefaultIsoDepRoute,
+                  @CardEmulation.ProtocolAndTechnologyRoute int mDefaultOffHostRoute) {
+        this.mDefaultRoute = mDefaultRoute;
+        this.mDefaultIsoDepRoute = mDefaultIsoDepRoute;
+        this.mDefaultOffHostRoute = mDefaultOffHostRoute;
+    }
+
+    /**
+     * Getter of the default route.
+     * @return an integer defined in
+     * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute}
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @CardEmulation.ProtocolAndTechnologyRoute
+    public int getDefaultRoute() {
+        return mDefaultRoute;
+    }
+
+    /**
+     * Getter of the default ISO-DEP route.
+     * @return an integer defined in
+     * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute}
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @CardEmulation.ProtocolAndTechnologyRoute
+    public int getDefaultIsoDepRoute() {
+        return mDefaultIsoDepRoute;
+    }
+
+    /**
+     * Getter of the default off-host route.
+     * @return an integer defined in
+     * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute}
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @CardEmulation.ProtocolAndTechnologyRoute
+    public int getDefaultOffHostRoute() {
+        return mDefaultOffHostRoute;
+    }
+
+}
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 4be082c..d8f04c5 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -168,6 +168,12 @@
     public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2;
 
     /**
+     * Route to the default value in config file.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT = 3;
+
+    /**
      * Route unset.
      */
     @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
@@ -895,45 +901,47 @@
                     PROTOCOL_AND_TECHNOLOGY_ROUTE_DH,
                     PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE,
                     PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC,
-                    PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET
+                    PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET,
+                    PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtocolAndTechnologyRoute {}
 
-     /**
-      * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
-      * while this Activity is in the foreground.
-      *
-      * The parameter set to {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET}
-      * can be used to keep current values for that entry. Either
-      * Protocol Route or Technology Route should be override when calling this API, otherwise
-      * throw {@link IllegalArgumentException}.
-      * <p>
-      * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
-      * <pre>
-      * protected void onResume() {
-      *     mNfcAdapter.overrideRoutingTable(
-      *         this, {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE}, null);
-      * }</pre>
-      * </p>
-      * Also activities must call {@link #recoverRoutingTable(Activity)}
-      * when it goes to the background. Only the package of the
-      * currently preferred service (the service set as preferred by the current foreground
-      * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the
-      * current Default Wallet Role Holder {@link RoleManager#ROLE_WALLET}),
-      * otherwise a call to this method will fail and throw {@link SecurityException}.
-      * @param activity The Activity that requests NFC controller routing table to be changed.
-      * @param protocol ISO-DEP route destination, where the possible inputs are defined
-      *                 in {@link ProtocolAndTechnologyRoute}.
-      * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs
-      *                   are defined in {@link ProtocolAndTechnologyRoute}
-      * @throws SecurityException if the caller is not the preferred NFC service
-      * @throws IllegalArgumentException if the activity is not resumed or the caller is not in the
-      * foreground.
-      * <p>
-      * This is a high risk API and only included to support mainline effort
-      * @hide
-      */
+    /**
+     * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
+     * while this Activity is in the foreground.
+     *
+     * The parameter set to {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET}
+     * can be used to keep current values for that entry. Either
+     * Protocol Route or Technology Route should be override when calling this API, otherwise
+     * throw {@link IllegalArgumentException}.
+     * <p>
+     * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
+     * <pre>
+     * protected void onResume() {
+     *     mNfcAdapter.overrideRoutingTable(
+     *         this, {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE},
+     *         {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET});
+     * }</pre>
+     * </p>
+     * Also activities must call {@link #recoverRoutingTable(Activity)}
+     * when it goes to the background. Only the package of the
+     * currently preferred service (the service set as preferred by the current foreground
+     * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the
+     * current Default Wallet Role Holder {@link RoleManager#ROLE_WALLET}),
+     * otherwise a call to this method will fail and throw {@link SecurityException}.
+     * @param activity The Activity that requests NFC controller routing table to be changed.
+     * @param protocol ISO-DEP route destination, where the possible inputs are defined
+     *                 in {@link ProtocolAndTechnologyRoute}.
+     * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs
+     *                   are defined in {@link ProtocolAndTechnologyRoute}
+     * @throws SecurityException if the caller is not the preferred NFC service
+     * @throws IllegalArgumentException if the activity is not resumed or the caller is not in the
+     * foreground.
+     * <p>
+     * This is a high risk API and only included to support mainline effort
+     * @hide
+     */
     @SystemApi
     @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
     public void overrideRoutingTable(
@@ -942,26 +950,14 @@
         if (!activity.isResumed()) {
             throw new IllegalArgumentException("Activity must be resumed.");
         }
-        String protocolRoute = switch (protocol) {
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH";
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "ESE";
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "UICC";
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null;
-            default -> throw new IllegalStateException("Unexpected value: " + protocol);
-        };
-        String technologyRoute = switch (technology) {
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH";
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "ESE";
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "UICC";
-            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null;
-            default -> throw new IllegalStateException("Unexpected value: " + protocol);
-        };
+        String protocolRoute = routeIntToString(protocol);
+        String technologyRoute = routeIntToString(technology);
         callService(() ->
                 sService.overrideRoutingTable(
-                    mContext.getUser().getIdentifier(),
-                    protocolRoute,
-                    technologyRoute,
-                    mContext.getPackageName()));
+                        mContext.getUser().getIdentifier(),
+                        protocolRoute,
+                        technologyRoute,
+                        mContext.getPackageName()));
     }
 
     /**
@@ -1068,4 +1064,16 @@
         }
         return defaultReturn;
     }
+
+    /** @hide */
+    public static String routeIntToString(@ProtocolAndTechnologyRoute int route) {
+        return switch (route) {
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "eSE";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "SIM";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null;
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT -> "default";
+            default -> throw new IllegalStateException("Unexpected value: " + route);
+        };
+    }
 }
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index a57d6eb..b266912 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -50,13 +50,13 @@
     <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
 
     <!-- Confirmation for associating an application with a companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
-    <string name="title_app_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="device_type" example="phone">%2$s</xliff:g>\u2019s apps to &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
+    <string name="title_app_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="device_type" example="phone">%2$s</xliff:g>\u2019s apps and system features to &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
 
     <!-- Summary for associating an application with a companion device of APP_STREAMING profile [CHAR LIMIT=NONE] -->
-    <string name="summary_app_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s visible or played on the <xliff:g id="device_type" example="phone">%2$s</xliff:g>, including audio, photos, passwords, and messages.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream apps to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
+    <string name="summary_app_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s visible or played on your <xliff:g id="device_type" example="phone">%2$s</xliff:g>, including audio, photos, payment info, passwords, and messages.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream apps to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
 
     <!-- Description of the helper dialog for APP_STREAMING profile. [CHAR LIMIT=NONE] -->
-    <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to display and stream apps between your devices</string>
+    <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream apps and system features from your <xliff:g id="device_type" example="phone">%3$s</xliff:g></string>
 
     <!-- ================= DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ================= -->
 
@@ -80,13 +80,13 @@
     <!-- ================= DEVICE_PROFILE_NEARBY_DEVICE_STREAMING ================= -->
 
     <!-- Confirmation for associating an application with a companion device of NEARBY_DEVICE_STREAMING profile (type) [CHAR LIMIT=NONE] -->
-    <string name="title_nearby_device_streaming">Allow &lt;strong&gt;<xliff:g id="device_name" example="NearbyStreamer">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="device_type" example="phone">%2$s</xliff:g>\u2019s apps and system features to &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
+    <string name="title_nearby_device_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="device_type" example="phone">%2$s</xliff:g>\u2019s apps to &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
 
     <!-- Summary for associating an application with a companion device of NEARBY_DEVICE_STREAMING profile [CHAR LIMIT=NONE] -->
-    <string name="summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s visible or played on your <xliff:g id="device_type" example="phone">%2$s</xliff:g>, including audio, photos, payment info, passwords, and messages.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream apps and system features to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
+    <string name="summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s visible or played on <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>, including audio, photos, payment info, passwords, and messages.&lt;br/>&lt;br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream apps to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string>
 
     <!-- Description of the helper dialog for NEARBY_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
-    <string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="NearbyStreamerApp">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_name" example="NearbyDevice">%2$s</xliff:g> to stream apps and other system features between your devices</string>
+    <string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="device_name" example="Chromebook">%2$s</xliff:g> to stream apps from your <xliff:g id="device_type" example="phone">%3$s</xliff:g></string>
 
     <!-- ================= null profile ================= -->
 
diff --git a/packages/SettingsLib/ButtonPreference/res/values-v35/attrs_expressive.xml b/packages/SettingsLib/ButtonPreference/res/values-v35/attrs_expressive.xml
deleted file mode 100644
index a1761e5..0000000
--- a/packages/SettingsLib/ButtonPreference/res/values-v35/attrs_expressive.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2024 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <declare-styleable name="ButtonPreference">
-        <attr name="buttonType" format="enum">
-            <enum name="filled" value="0"/>
-            <enum name="tonal" value="1"/>
-            <enum name="outline" value="2"/>
-        </attr>
-        <attr name="buttonSize" format="enum">
-            <enum name="normal" value="0"/>
-            <enum name="large" value="1"/>
-            <enum name="extra" value="2"/>
-        </attr>
-    </declare-styleable>
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values/attrs.xml b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml
index 9c1e503f..970eeb2 100644
--- a/packages/SettingsLib/ButtonPreference/res/values/attrs.xml
+++ b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml
@@ -18,12 +18,12 @@
 <resources>
     <declare-styleable name="ButtonPreference">
         <attr name="android:gravity" />
-        <attr name="buttonType" format="enum">
+        <attr name="buttonPreferenceType" format="enum">
             <enum name="filled" value="0"/>
             <enum name="tonal" value="1"/>
             <enum name="outline" value="2"/>
         </attr>
-        <attr name="buttonSize" format="enum">
+        <attr name="buttonPreferenceSize" format="enum">
             <enum name="normal" value="0"/>
             <enum name="large" value="1"/>
             <enum name="extra" value="2"/>
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
index 0041eb2..979ff96 100644
--- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -137,8 +137,8 @@
             mGravity = a.getInt(R.styleable.ButtonPreference_android_gravity, Gravity.START);
 
             if (SettingsThemeHelper.isExpressiveTheme(context)) {
-                int type = a.getInt(R.styleable.ButtonPreference_buttonType, 0);
-                int size = a.getInt(R.styleable.ButtonPreference_buttonSize, 0);
+                int type = a.getInt(R.styleable.ButtonPreference_buttonPreferenceType, 0);
+                int size = a.getInt(R.styleable.ButtonPreference_buttonPreferenceSize, 0);
                 resId = ButtonStyle.getLayoutId(type, size);
             }
             a.recycle();
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 5e31da4..4dc8424 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -168,10 +168,6 @@
         Settings.Secure.SHOW_NOTIFICATION_SNOOZE,
         Settings.Secure.NOTIFICATION_HISTORY_ENABLED,
         Settings.Secure.ZEN_DURATION,
-        Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION,
-        Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION,
-        Settings.Secure.ZEN_SETTINGS_UPDATED,
-        Settings.Secure.ZEN_SETTINGS_SUGGESTION_VIEWED,
         Settings.Secure.CHARGING_SOUNDS_ENABLED,
         Settings.Secure.CHARGING_VIBRATION_ENABLED,
         Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index f6e1057..0773bd7 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -186,7 +186,7 @@
         VALIDATORS.put(
                 Global.STEM_PRIMARY_BUTTON_SHORT_PRESS, new InclusiveIntegerRangeValidator(0, 1));
         VALIDATORS.put(
-                Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS, new InclusiveIntegerRangeValidator(0, 1));
+                Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS, new InclusiveIntegerRangeValidator(0, 2));
         VALIDATORS.put(
                 Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS, new InclusiveIntegerRangeValidator(0, 1));
         VALIDATORS.put(
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b3f7374..688676d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -247,10 +247,6 @@
         VALIDATORS.put(Secure.SHOW_NOTIFICATION_SNOOZE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.NOTIFICATION_HISTORY_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ZEN_DURATION, ANY_INTEGER_VALIDATOR);
-        VALIDATORS.put(Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.SHOW_ZEN_SETTINGS_SUGGESTION, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.ZEN_SETTINGS_UPDATED, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.ZEN_SETTINGS_SUGGESTION_VIEWED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 3c24f5c..2034f36 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2734,18 +2734,6 @@
                 Settings.Secure.ZEN_DURATION,
                 SecureSettingsProto.Zen.DURATION);
         dumpSetting(s, p,
-                Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION,
-                SecureSettingsProto.Zen.SHOW_ZEN_UPGRADE_NOTIFICATION);
-        dumpSetting(s, p,
-                Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION,
-                SecureSettingsProto.Zen.SHOW_ZEN_SETTINGS_SUGGESTION);
-        dumpSetting(s, p,
-                Settings.Secure.ZEN_SETTINGS_UPDATED,
-                SecureSettingsProto.Zen.SETTINGS_UPDATED);
-        dumpSetting(s, p,
-                Settings.Secure.ZEN_SETTINGS_SUGGESTION_VIEWED,
-                SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED);
-        dumpSetting(s, p,
                 Settings.Secure.CHARGE_OPTIMIZATION_MODE,
                 SecureSettingsProto.CHARGE_OPTIMIZATION_MODE);
         p.end(zenToken);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 749ad0a..a8af43f5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4771,9 +4771,9 @@
                 }
 
                 if (currentVersion == 169) {
-                    // Version 169: Set the default value for Secure Settings ZEN_DURATION,
-                    // SHOW_ZEN_SETTINGS_SUGGESTION, ZEN_SETTINGS_UPDATE and
-                    // ZEN_SETTINGS_SUGGESTION_VIEWED
+                    // Version 169: Set the default value for Secure Settings ZEN_DURATION.
+                    // Also used to update SHOW_ZEN_SETTINGS_SUGGESTION, ZEN_SETTINGS_UPDATE and
+                    // ZEN_SETTINGS_SUGGESTION_VIEWED, but those properties are gone now.
 
                     final SettingsState globalSettings = getGlobalSettingsLocked();
                     final Setting globalZenDuration = globalSettings.getSettingLocked(
@@ -4801,33 +4801,6 @@
                                 SettingsState.SYSTEM_PACKAGE_NAME);
                     }
 
-                    // SHOW_ZEN_SETTINGS_SUGGESTION
-                    final Setting currentShowZenSettingSuggestion = secureSettings.getSettingLocked(
-                            Secure.SHOW_ZEN_SETTINGS_SUGGESTION);
-                    if (currentShowZenSettingSuggestion.isNull()) {
-                        secureSettings.insertSettingOverrideableByRestoreLocked(
-                                Secure.SHOW_ZEN_SETTINGS_SUGGESTION, "1",
-                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
-                    // ZEN_SETTINGS_UPDATED
-                    final Setting currentUpdatedSetting = secureSettings.getSettingLocked(
-                            Secure.ZEN_SETTINGS_UPDATED);
-                    if (currentUpdatedSetting.isNull()) {
-                        secureSettings.insertSettingOverrideableByRestoreLocked(
-                                Secure.ZEN_SETTINGS_UPDATED, "0",
-                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
-                    // ZEN_SETTINGS_SUGGESTION_VIEWED
-                    final Setting currentSettingSuggestionViewed = secureSettings.getSettingLocked(
-                            Secure.ZEN_SETTINGS_SUGGESTION_VIEWED);
-                    if (currentSettingSuggestionViewed.isNull()) {
-                        secureSettings.insertSettingOverrideableByRestoreLocked(
-                                Secure.ZEN_SETTINGS_SUGGESTION_VIEWED, "0",
-                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
                     currentVersion = 170;
                 }
 
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index d98b2da5..6c3ba68 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -637,6 +637,13 @@
 }
 
 flag {
+    name: "screenshot_policy_split_and_desktop_mode"
+    namespace: "systemui"
+    description: "Improves screenshot policy handling for split screen and desktop mode."
+    bug: "365597999"
+}
+
+flag {
    name: "run_fingerprint_detect_on_dismissible_keyguard"
    namespace: "systemui"
    description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
@@ -1074,6 +1081,16 @@
 }
 
 flag {
+    name: "dream_overlay_updated_font"
+    namespace: "systemui"
+    description: "Flag to enable updated font settings for dream overlay"
+    bug: "349656117"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
   name: "app_clips_backlinks"
   namespace: "systemui"
   description: "Enables Backlinks improvement feature in App Clips"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index ac58ab5..9d5252e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.scene.ui.composable
 
+import androidx.compose.animation.core.spring
 import androidx.compose.foundation.gestures.Orientation
 import com.android.compose.animation.scene.ProgressConverter
 import com.android.compose.animation.scene.TransitionKey
@@ -44,6 +45,7 @@
 
     // Overscroll progress starts linearly with some resistance (3f) and slowly approaches 0.2f
     defaultOverscrollProgressConverter = ProgressConverter.tanh(maxProgress = 0.2f, tilt = 3f)
+    defaultSwipeSpec = spring(stiffness = 300f, dampingRatio = 0.8f, visibilityThreshold = 0.5f)
 
     // Scene transitions
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 5ddc284..dc3135d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -42,8 +42,10 @@
 import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
 import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverModifierNode
 import androidx.compose.ui.node.PointerInputModifierNode
 import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.node.observeReads
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.Velocity
@@ -77,6 +79,7 @@
 @Stable
 internal fun Modifier.multiPointerDraggable(
     orientation: Orientation,
+    enabled: () -> Boolean,
     startDragImmediately: (startedPosition: Offset) -> Boolean,
     onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
     onFirstPointerDown: () -> Unit = {},
@@ -86,6 +89,7 @@
     this.then(
         MultiPointerDraggableElement(
             orientation,
+            enabled,
             startDragImmediately,
             onDragStarted,
             onFirstPointerDown,
@@ -96,6 +100,7 @@
 
 private data class MultiPointerDraggableElement(
     private val orientation: Orientation,
+    private val enabled: () -> Boolean,
     private val startDragImmediately: (startedPosition: Offset) -> Boolean,
     private val onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
@@ -106,6 +111,7 @@
     override fun create(): MultiPointerDraggableNode =
         MultiPointerDraggableNode(
             orientation = orientation,
+            enabled = enabled,
             startDragImmediately = startDragImmediately,
             onDragStarted = onDragStarted,
             onFirstPointerDown = onFirstPointerDown,
@@ -115,6 +121,7 @@
 
     override fun update(node: MultiPointerDraggableNode) {
         node.orientation = orientation
+        node.enabled = enabled
         node.startDragImmediately = startDragImmediately
         node.onDragStarted = onDragStarted
         node.onFirstPointerDown = onFirstPointerDown
@@ -124,6 +131,7 @@
 
 internal class MultiPointerDraggableNode(
     orientation: Orientation,
+    enabled: () -> Boolean,
     var startDragImmediately: (startedPosition: Offset) -> Boolean,
     var onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
@@ -134,10 +142,21 @@
     DelegatingNode(),
     PointerInputModifierNode,
     CompositionLocalConsumerModifierNode,
+    ObserverModifierNode,
     SpaceVectorConverter {
     private val pointerTracker = delegate(SuspendingPointerInputModifierNode { pointerTracker() })
     private val pointerInput = delegate(SuspendingPointerInputModifierNode { pointerInput() })
     private val velocityTracker = VelocityTracker()
+    private var previousEnabled: Boolean = false
+
+    var enabled: () -> Boolean = enabled
+        set(value) {
+            // Reset the pointer input whenever enabled changed.
+            if (value != field) {
+                field = value
+                pointerInput.resetPointerInputHandler()
+            }
+        }
 
     private var converter = SpaceVectorConverter(orientation)
 
@@ -159,6 +178,21 @@
             }
         }
 
+    override fun onAttach() {
+        previousEnabled = enabled()
+        onObservedReadsChanged()
+    }
+
+    override fun onObservedReadsChanged() {
+        observeReads {
+            val newEnabled = enabled()
+            if (newEnabled != previousEnabled) {
+                pointerInput.resetPointerInputHandler()
+            }
+            previousEnabled = newEnabled
+        }
+    }
+
     override fun onCancelPointerInput() {
         pointerTracker.onCancelPointerInput()
         pointerInput.onCancelPointerInput()
@@ -220,7 +254,9 @@
                         velocityTracker.resetTracking()
                         velocityTracker.addPointerInputChange(firstPointerDown)
                         startedPosition = firstPointerDown.position
-                        onFirstPointerDown()
+                        if (enabled()) {
+                            onFirstPointerDown()
+                        }
                     }
 
                     // Changes with at least one pointer
@@ -259,6 +295,10 @@
     }
 
     private suspend fun PointerInputScope.pointerInput() {
+        if (!enabled()) {
+            return
+        }
+
         val currentContext = currentCoroutineContext()
         awaitPointerEventScope {
             while (currentContext.isActive) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index b358faf..879dc54 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -152,6 +152,7 @@
         internal val DefaultSwipeSpec =
             spring(
                 stiffness = Spring.StiffnessMediumLow,
+                dampingRatio = Spring.DampingRatioLowBouncy,
                 visibilityThreshold = OffsetVisibilityThreshold,
             )
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index d201be9..98d4aaa 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -41,28 +41,7 @@
     draggableHandler: DraggableHandlerImpl,
     swipeDetector: SwipeDetector,
 ): Modifier {
-    return if (draggableHandler.enabled()) {
-        this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
-    } else {
-        this
-    }
-}
-
-private fun DraggableHandlerImpl.enabled(): Boolean {
-    return isDrivingTransition || contentForSwipes().shouldEnableSwipes(orientation)
-}
-
-private fun DraggableHandlerImpl.contentForSwipes(): Content {
-    return layoutImpl.contentForUserActions()
-}
-
-/** Whether swipe should be enabled in the given [orientation]. */
-private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
-    if (userActions.isEmpty()) {
-        return false
-    }
-
-    return userActions.keys.any { it is Swipe.Resolved && it.direction.orientation == orientation }
+    return this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
 }
 
 private data class SwipeToSceneElement(
@@ -85,6 +64,7 @@
         delegate(
             MultiPointerDraggableNode(
                 orientation = draggableHandler.orientation,
+                enabled = ::enabled,
                 startDragImmediately = ::startDragImmediately,
                 onDragStarted = draggableHandler::onDragStarted,
                 onFirstPointerDown = ::onFirstPointerDown,
@@ -144,6 +124,22 @@
 
     override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput()
 
+    private fun enabled(): Boolean {
+        return draggableHandler.isDrivingTransition ||
+            contentForSwipes().shouldEnableSwipes(multiPointerDraggableNode.orientation)
+    }
+
+    private fun contentForSwipes(): Content {
+        return draggableHandler.layoutImpl.contentForUserActions()
+    }
+
+    /** Whether swipe should be enabled in the given [orientation]. */
+    private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
+        return userActions.keys.any {
+            it is Swipe.Resolved && it.direction.orientation == orientation
+        }
+    }
+
     private fun startDragImmediately(startedPosition: Offset): Boolean {
         // Immediately start the drag if the user can't swipe in the other direction and the gesture
         // handler can intercept it.
@@ -156,7 +152,7 @@
                 Orientation.Vertical -> Orientation.Horizontal
                 Orientation.Horizontal -> Orientation.Vertical
             }
-        return draggableHandler.contentForSwipes().shouldEnableSwipes(oppositeOrientation)
+        return contentForSwipes().shouldEnableSwipes(oppositeOrientation)
     }
 }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index a6ebb0e..a3641e6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -34,71 +34,76 @@
  * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop]
  * after [onStart].
  *
- * @sample com.android.compose.animation.scene.rememberSwipeToSceneNestedScrollConnection
+ * @sample LargeTopAppBarNestedScrollConnection
+ * @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection
  */
 class PriorityNestedScrollConnection(
-    private val canStartPreScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
-    private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
-    private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean,
+    orientation: Orientation,
+    private val canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
+    private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
+    private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
     private val canContinueScroll: (source: NestedScrollSource) -> Boolean,
     private val canScrollOnFling: Boolean,
-    private val onStart: (offsetAvailable: Offset) -> Unit,
-    private val onScroll: (offsetAvailable: Offset) -> Offset,
-    private val onStop: (velocityAvailable: Velocity) -> SuspendedValue<Velocity>,
-) : NestedScrollConnection {
+    private val onStart: (offsetAvailable: Float) -> Unit,
+    private val onScroll: (offsetAvailable: Float) -> Float,
+    private val onStop: (velocityAvailable: Float) -> SuspendedValue<Float>,
+) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
 
     /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
     private var isPriorityMode = false
 
-    private var offsetScrolledBeforePriorityMode = Offset.Zero
+    private var offsetScrolledBeforePriorityMode = 0f
 
     override fun onPostScroll(
         consumed: Offset,
         available: Offset,
         source: NestedScrollSource,
     ): Offset {
+        val availableFloat = available.toFloat()
         // The offset before the start takes into account the up and down movements, starting from
         // the beginning or from the last fling gesture.
-        val offsetBeforeStart = offsetScrolledBeforePriorityMode - available
+        val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat
 
         if (
             isPriorityMode ||
                 (source == NestedScrollSource.SideEffect && !canScrollOnFling) ||
-                !canStartPostScroll(available, offsetBeforeStart)
+                !canStartPostScroll(availableFloat, offsetBeforeStart)
         ) {
             // The priority mode cannot start so we won't consume the available offset.
             return Offset.Zero
         }
 
-        return onPriorityStart(available)
+        return onPriorityStart(availableFloat).toOffset()
     }
 
     override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
         if (!isPriorityMode) {
             if (source == NestedScrollSource.UserInput || canScrollOnFling) {
-                if (canStartPreScroll(available, offsetScrolledBeforePriorityMode)) {
-                    return onPriorityStart(available)
+                val availableFloat = available.toFloat()
+                if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode)) {
+                    return onPriorityStart(availableFloat).toOffset()
                 }
                 // We want to track the amount of offset consumed before entering priority mode
-                offsetScrolledBeforePriorityMode += available
+                offsetScrolledBeforePriorityMode += availableFloat
             }
 
             return Offset.Zero
         }
 
+        val availableFloat = available.toFloat()
         if (!canContinueScroll(source)) {
             // Step 3a: We have lost priority and we no longer need to intercept scroll events.
-            onPriorityStop(velocity = Velocity.Zero)
+            onPriorityStop(velocity = 0f)
 
-            // We've just reset offsetScrolledBeforePriorityMode to Offset.Zero
+            // We've just reset offsetScrolledBeforePriorityMode to 0f
             // We want to track the amount of offset consumed before entering priority mode
-            offsetScrolledBeforePriorityMode += available
+            offsetScrolledBeforePriorityMode += availableFloat
 
             return Offset.Zero
         }
 
         // Step 2: We have the priority and can consume the scroll events.
-        return onScroll(available)
+        return onScroll(availableFloat).toOffset()
     }
 
     override suspend fun onPreFling(available: Velocity): Velocity {
@@ -108,15 +113,16 @@
         }
         // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed
         // of the fling gesture.
-        return onPriorityStop(velocity = available).invoke()
+        return onPriorityStop(velocity = available.toFloat()).invoke().toVelocity()
     }
 
     override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+        val availableFloat = available.toFloat()
         if (isPriorityMode) {
-            return onPriorityStop(velocity = available).invoke()
+            return onPriorityStop(velocity = availableFloat).invoke().toVelocity()
         }
 
-        if (!canStartPostFling(available)) {
+        if (!canStartPostFling(availableFloat)) {
             return Velocity.Zero
         }
 
@@ -124,11 +130,11 @@
         // given the available velocity.
         // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
         // overscroll behavior on the Scene level.
-        val smallOffset = Offset(available.x.sign, available.y.sign)
-        onPriorityStart(available = smallOffset)
+        val smallOffset = availableFloat.sign
+        onPriorityStart(availableOffset = smallOffset)
 
         // This is the last event of a scroll gesture.
-        return onPriorityStop(available).invoke()
+        return onPriorityStop(availableFloat).invoke().toVelocity()
     }
 
     /**
@@ -138,10 +144,10 @@
      */
     fun reset() {
         // Step 3c: To ensure that an onStop is always called for every onStart.
-        onPriorityStop(velocity = Velocity.Zero)
+        onPriorityStop(velocity = 0f)
     }
 
-    private fun onPriorityStart(available: Offset): Offset {
+    private fun onPriorityStart(availableOffset: Float): Float {
         if (isPriorityMode) {
             error("This should never happen, onPriorityStart() was called when isPriorityMode")
         }
@@ -152,17 +158,17 @@
 
         // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
         // lifted (step 3b), or this object has been destroyed (step 3c).
-        onStart(available)
+        onStart(availableOffset)
 
-        return onScroll(available)
+        return onScroll(availableOffset)
     }
 
-    private fun onPriorityStop(velocity: Velocity): SuspendedValue<Velocity> {
+    private fun onPriorityStop(velocity: Float): SuspendedValue<Float> {
         // We can restart tracking the consumed offsets from scratch.
-        offsetScrolledBeforePriorityMode = Offset.Zero
+        offsetScrolledBeforePriorityMode = 0f
 
         if (!isPriorityMode) {
-            return { Velocity.Zero }
+            return { 0f }
         }
 
         isPriorityMode = false
@@ -170,38 +176,3 @@
         return onStop(velocity)
     }
 }
-
-fun PriorityNestedScrollConnection(
-    orientation: Orientation,
-    canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
-    canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
-    canStartPostFling: (velocityAvailable: Float) -> Boolean,
-    canContinueScroll: (source: NestedScrollSource) -> Boolean,
-    canScrollOnFling: Boolean,
-    onStart: (offsetAvailable: Float) -> Unit,
-    onScroll: (offsetAvailable: Float) -> Float,
-    onStop: (velocityAvailable: Float) -> SuspendedValue<Float>,
-) =
-    with(SpaceVectorConverter(orientation)) {
-        PriorityNestedScrollConnection(
-            canStartPreScroll = { offsetAvailable: Offset, offsetBeforeStart: Offset ->
-                canStartPreScroll(offsetAvailable.toFloat(), offsetBeforeStart.toFloat())
-            },
-            canStartPostScroll = { offsetAvailable: Offset, offsetBeforeStart: Offset ->
-                canStartPostScroll(offsetAvailable.toFloat(), offsetBeforeStart.toFloat())
-            },
-            canStartPostFling = { velocityAvailable: Velocity ->
-                canStartPostFling(velocityAvailable.toFloat())
-            },
-            canContinueScroll = canContinueScroll,
-            canScrollOnFling = canScrollOnFling,
-            onStart = { offsetAvailable -> onStart(offsetAvailable.toFloat()) },
-            onScroll = { offsetAvailable: Offset ->
-                onScroll(offsetAvailable.toFloat()).toOffset()
-            },
-            onStop = { velocityAvailable: Velocity ->
-                val consumedVelocity = onStop(velocityAvailable.toFloat())
-                suspend { consumedVelocity.invoke().toVelocity() }
-            },
-        )
-    }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index dd4f99f..302f207 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -1241,7 +1241,8 @@
     fun overscroll_releaseBetween0And100Percent_up() = runGestureTest {
         // Make scene B overscrollable.
         layoutState.transitions = transitions {
-            from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            defaultSwipeSpec = spring(dampingRatio = Spring.DampingRatioNoBouncy)
+            from(SceneA, to = SceneB) {}
             overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
         }
 
@@ -1272,7 +1273,8 @@
     fun overscroll_releaseBetween0And100Percent_down() = runGestureTest {
         // Make scene C overscrollable.
         layoutState.transitions = transitions {
-            from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            defaultSwipeSpec = spring(dampingRatio = Spring.DampingRatioNoBouncy)
+            from(SceneA, to = SceneC) {}
             overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
         }
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index c8f6e6d..493f3a1 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -45,7 +45,6 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.compose.modifiers.thenIf
 import com.android.compose.nestedscroll.SuspendedValue
 import com.google.common.truth.Truth.assertThat
 import kotlin.properties.Delegates
@@ -95,20 +94,19 @@
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
                     .nestedScrollDispatcher()
-                    .thenIf(enabled) {
-                        Modifier.multiPointerDraggable(
-                            orientation = Orientation.Vertical,
-                            startDragImmediately = { false },
-                            onDragStarted = { _, _, _ ->
-                                started = true
-                                SimpleDragController(
-                                    onDrag = { dragged = true },
-                                    onStop = { stopped = true },
-                                )
-                            },
-                            dispatcher = defaultDispatcher,
-                        )
-                    }
+                    .multiPointerDraggable(
+                        orientation = Orientation.Vertical,
+                        enabled = { enabled },
+                        startDragImmediately = { false },
+                        onDragStarted = { _, _, _ ->
+                            started = true
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
+                        },
+                        dispatcher = defaultDispatcher,
+                    )
             )
         }
 
@@ -166,6 +164,7 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
+                        enabled = { true },
                         // We want to start a drag gesture immediately
                         startDragImmediately = { true },
                         onDragStarted = { _, _, _ ->
@@ -239,6 +238,7 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
+                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
@@ -358,6 +358,7 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
+                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
@@ -463,6 +464,7 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
+                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             verticalStarted = true
@@ -475,6 +477,7 @@
                     )
                     .multiPointerDraggable(
                         orientation = Orientation.Horizontal,
+                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             horizontalStarted = true
@@ -567,6 +570,7 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
+                        enabled = { true },
                         startDragImmediately = { false },
                         swipeDetector =
                             object : SwipeDetector {
@@ -668,6 +672,7 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
+                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             SimpleDragController(
@@ -739,6 +744,7 @@
                     .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
+                        enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             SimpleDragController(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index ce64628..25e8713 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -22,15 +22,11 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.size
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
@@ -40,11 +36,8 @@
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
-import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
-import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.unit.Density
@@ -851,29 +844,4 @@
         assertThat(transition.progress).isEqualTo(1f)
         assertThat(availableOnPostScroll).isEqualTo(ovescrollPx)
     }
-
-    @Test
-    fun sceneWithoutSwipesDoesNotConsumeGestures() {
-        val buttonTag = "button"
-
-        rule.setContent {
-            Box {
-                var count by remember { mutableStateOf(0) }
-                Button(onClick = { count++ }, Modifier.testTag(buttonTag).align(Alignment.Center)) {
-                    Text("Count: $count")
-                }
-
-                SceneTransitionLayout(remember { MutableSceneTransitionLayoutState(SceneA) }) {
-                    scene(SceneA) { Box(Modifier.fillMaxSize()) }
-                }
-            }
-        }
-
-        rule.onNodeWithTag(buttonTag).assertTextEquals("Count: 0")
-
-        // Click on the root at its center, where the button is located. Clicks should go through
-        // the STL and reach the button given that there is no swipes for the current scene.
-        repeat(3) { rule.onRoot().performClick() }
-        rule.onNodeWithTag(buttonTag).assertTextEquals("Count: 3")
-    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index bde7699..badc43b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -18,8 +18,9 @@
 
 package com.android.compose.nestedscroll
 
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
@@ -35,13 +36,14 @@
     private var canStartPostFling = false
     private var canContinueScroll = false
     private var isStarted = false
-    private var lastScroll: Offset? = null
-    private var returnOnScroll = Offset.Zero
-    private var lastStop: Velocity? = null
-    private var returnOnStop = Velocity.Zero
+    private var lastScroll: Float? = null
+    private var returnOnScroll = 0f
+    private var lastStop: Float? = null
+    private var returnOnStop = 0f
 
     private val scrollConnection =
         PriorityNestedScrollConnection(
+            orientation = Orientation.Vertical,
             canStartPreScroll = { _, _ -> canStartPreScroll },
             canStartPostScroll = { _, _ -> canStartPostScroll },
             canStartPostFling = { canStartPostFling },
@@ -58,11 +60,6 @@
             },
         )
 
-    private val offset1 = Offset(1f, 1f)
-    private val offset2 = Offset(2f, 2f)
-    private val velocity1 = Velocity(1f, 1f)
-    private val velocity2 = Velocity(2f, 2f)
-
     @Test
     fun step1_priorityModeShouldStartOnlyOnPreScroll() = runTest {
         canStartPreScroll = true
@@ -70,7 +67,7 @@
         scrollConnection.onPostScroll(
             consumed = Offset.Zero,
             available = Offset.Zero,
-            source = NestedScrollSource.Drag,
+            source = UserInput,
         )
         assertThat(isStarted).isEqualTo(false)
 
@@ -80,7 +77,7 @@
         scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
         assertThat(isStarted).isEqualTo(false)
 
-        scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag)
+        scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
         assertThat(isStarted).isEqualTo(true)
     }
 
@@ -89,7 +86,7 @@
         scrollConnection.onPostScroll(
             consumed = Offset.Zero,
             available = Offset.Zero,
-            source = NestedScrollSource.Drag,
+            source = UserInput,
         )
     }
 
@@ -97,7 +94,7 @@
     fun step1_priorityModeShouldStartOnlyOnPostScroll() = runTest {
         canStartPostScroll = true
 
-        scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag)
+        scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
         assertThat(isStarted).isEqualTo(false)
 
         scrollConnection.onPreFling(available = Velocity.Zero)
@@ -115,7 +112,7 @@
         scrollConnection.onPostScroll(
             consumed = Offset.Zero,
             available = Offset.Zero,
-            source = NestedScrollSource.Drag,
+            source = UserInput,
         )
         assertThat(isStarted).isEqualTo(false)
 
@@ -128,12 +125,12 @@
         canStartPostScroll = true
 
         scrollConnection.onPostScroll(
-            consumed = offset1,
-            available = offset2,
-            source = NestedScrollSource.Drag,
+            consumed = Offset(1f, 1f),
+            available = Offset(2f, 2f),
+            source = UserInput,
         )
 
-        assertThat(lastScroll).isEqualTo(offset2)
+        assertThat(lastScroll).isEqualTo(2f)
     }
 
     @Test
@@ -141,13 +138,13 @@
         startPriorityModePostScroll()
         canContinueScroll = true
 
-        scrollConnection.onPreScroll(available = offset1, source = NestedScrollSource.Drag)
-        assertThat(lastScroll).isEqualTo(offset1)
+        scrollConnection.onPreScroll(available = Offset(1f, 1f), source = UserInput)
+        assertThat(lastScroll).isEqualTo(1f)
 
         canContinueScroll = false
-        scrollConnection.onPreScroll(available = offset2, source = NestedScrollSource.Drag)
-        assertThat(lastScroll).isNotEqualTo(offset2)
-        assertThat(lastScroll).isEqualTo(offset1)
+        scrollConnection.onPreScroll(available = Offset(2f, 2f), source = UserInput)
+        assertThat(lastScroll).isNotEqualTo(2f)
+        assertThat(lastScroll).isEqualTo(1f)
     }
 
     @Test
@@ -155,7 +152,7 @@
         startPriorityModePostScroll()
         canContinueScroll = false
 
-        scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag)
+        scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
 
         assertThat(lastStop).isNotNull()
     }
@@ -184,22 +181,22 @@
     fun receive_onPostFling() = runTest {
         canStartPostFling = true
 
-        scrollConnection.onPostFling(consumed = velocity1, available = velocity2)
+        scrollConnection.onPostFling(consumed = Velocity(1f, 1f), available = Velocity(2f, 2f))
 
-        assertThat(lastStop).isEqualTo(velocity2)
+        assertThat(lastStop).isEqualTo(2f)
     }
 
     @Test
     fun step1_priorityModeShouldStartOnlyOnPostFling() = runTest {
         canStartPostFling = true
 
-        scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag)
+        scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
         assertThat(isStarted).isEqualTo(false)
 
         scrollConnection.onPostScroll(
             consumed = Offset.Zero,
             available = Offset.Zero,
-            source = NestedScrollSource.Drag,
+            source = UserInput,
         )
         assertThat(isStarted).isEqualTo(false)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSpecTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSpecTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSpecTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSpecTest.kt
index 791a26e..6f43c20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSpecTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSpecTest.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.accessibility
 
-import android.testing.AndroidTestingRunner
 import android.util.Size
 import androidx.test.filters.SmallTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.WindowMagnificationSettings.MagnificationSize
 import com.google.common.truth.Truth.assertThat
@@ -26,7 +26,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class WindowMagnificationFrameSpecTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 65825b2..2dcbdc8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.view.Display.INVALID_DISPLAY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -68,10 +69,12 @@
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.WindowManager;
@@ -210,6 +213,7 @@
                 .thenReturn(true);
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
                 .thenReturn(true);
+        when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(false);
 
         when(mDialog1.getOpPackageName()).thenReturn("Dialog1");
         when(mDialog2.getOpPackageName()).thenReturn("Dialog2");
@@ -462,7 +466,7 @@
     @Test
     public void testShowInvoked_whenSystemRequested() {
         showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
-        verify(mDialog1).show(any());
+        verify(mDialog1).show(mWindowManager);
     }
 
     @Test
@@ -679,7 +683,7 @@
         // 2) Client cancels authentication
 
         showDialog(new int[0] /* sensorIds */, true /* credentialAllowed */);
-        verify(mDialog1).show(any());
+        verify(mDialog1).show(mWindowManager);
 
         final byte[] credentialAttestation = generateRandomHAT();
 
@@ -695,7 +699,7 @@
     @Test
     public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() {
         showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
-        verify(mDialog1).show(any());
+        verify(mDialog1).show(mWindowManager);
 
         showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
 
@@ -703,7 +707,7 @@
         verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
 
         // Second dialog should be shown without animation
-        verify(mDialog2).show(any());
+        verify(mDialog2).show(mWindowManager);
     }
 
     @Test
@@ -990,13 +994,97 @@
         verify(mDialog1, never()).show(any());
     }
 
+    @Test
+    public void testShowDialog_visibleBackgroundUser() {
+        int backgroundUserId = 1001;
+        int backgroundDisplayId = 1001;
+        when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(true);
+        WindowManager wm = mockBackgroundUser(backgroundUserId, backgroundDisplayId,
+                true /* isVisible */, true /* hasUserManager */, true /* hasDisplay */);
+
+        showDialog(new int[]{1} /* sensorIds */, backgroundUserId /* userId */,
+                false /* credentialAllowed */);
+
+        verify(mDialog1).show(wm);
+    }
+
+    @Test
+    public void testShowDialog_invisibleBackgroundUser_defaultWM() {
+        int backgroundUserId = 1001;
+        when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(true);
+        mockBackgroundUser(backgroundUserId, INVALID_DISPLAY,
+                false /* isVisible */, true /* hasUserManager */, true /* hasDisplay */);
+
+        showDialog(new int[]{1} /* sensorIds */, backgroundUserId /* userId */,
+                false /* credentialAllowed */);
+
+        verify(mDialog1).show(mWindowManager);
+    }
+
+    @Test
+    public void testShowDialog_visibleBackgroundUser_noUserManager_dismissError()
+            throws RemoteException {
+        int backgroundUserId = 1001;
+        int backgroundDisplayId = 1001;
+        when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(true);
+        mockBackgroundUser(backgroundUserId, backgroundDisplayId,
+                true /* isVisible */, false /* hasUserManager */, true /* hasDisplay */);
+
+        showDialog(new int[]{1} /* sensorIds */, backgroundUserId /* userId */,
+                false /* credentialAllowed */);
+
+        verify(mDialog1, never()).show(any());
+        verify(mReceiver).onDialogDismissed(
+                eq(BiometricPrompt.DISMISSED_REASON_ERROR_NO_WM),
+                eq(null) /* credentialAttestation */);
+    }
+
+    @Test
+    public void testShowDialog_visibleBackgroundUser_invalidDisplayId_dismissError()
+            throws RemoteException {
+        int backgroundUserId = 1001;
+        when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(true);
+        mockBackgroundUser(backgroundUserId, INVALID_DISPLAY,
+                true /* isVisible */, true /* hasUserManager */, false /* hasDisplay */);
+
+        showDialog(new int[]{1} /* sensorIds */, backgroundUserId /* userId */,
+                false /* credentialAllowed */);
+
+        verify(mDialog1, never()).show(any());
+        verify(mReceiver).onDialogDismissed(
+                eq(BiometricPrompt.DISMISSED_REASON_ERROR_NO_WM),
+                eq(null) /* credentialAttestation */);
+    }
+
+    @Test
+    public void testShowDialog_visibleBackgroundUser_invalidDisplay_dismissError()
+            throws RemoteException {
+        int backgroundUserId = 1001;
+        int backgroundDisplayId = 1001;
+        when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(true);
+        mockBackgroundUser(backgroundUserId, backgroundDisplayId,
+                true /* isVisible */, true /* hasUserManager */, false /* hasDisplay */);
+
+        showDialog(new int[]{1} /* sensorIds */, backgroundUserId /* userId */,
+                false /* credentialAllowed */);
+
+        verify(mDialog1, never()).show(any());
+        verify(mReceiver).onDialogDismissed(
+                eq(BiometricPrompt.DISMISSED_REASON_ERROR_NO_WM),
+                eq(null) /* credentialAttestation */);
+    }
+
     private void showDialog(int[] sensorIds, boolean credentialAllowed) {
+        showDialog(sensorIds, 0 /* userId */, credentialAllowed);
+    }
+
+    private void showDialog(int[] sensorIds, int userId, boolean credentialAllowed) {
         mAuthController.showAuthenticationDialog(createTestPromptInfo(),
                 mReceiver /* receiver */,
                 sensorIds,
                 credentialAllowed,
                 true /* requireConfirmation */,
-                0 /* userId */,
+                userId /* userId */,
                 0 /* operationId */,
                 "testPackage",
                 REQUEST_ID);
@@ -1059,6 +1147,40 @@
         assertTrue(mAuthController.isFaceAuthEnrolled(userId));
     }
 
+    /**
+     * Create mocks related to visible background users.
+     *
+     * @param userId the user id of the background user to mock
+     * @param displayId display id of the background user
+     * @param isVisible whether the background user is a visible background user or not
+     * @param hasUserManager simulate whether the background user's context will return a mock
+     *                       UserManager instance or null
+     * @param hasDisplay simulate whether the background user's context will return a mock Display
+     *                   instance or null
+     * @return mock WindowManager instance associated with the background user's display context
+     */
+    private WindowManager mockBackgroundUser(int userId, int displayId, boolean isVisible,
+            boolean hasUserManager, boolean hasDisplay) {
+        Context mockUserContext = mock(Context.class);
+        Context mockDisplayContext = mock(Context.class);
+        UserManager mockUserManager = mock(UserManager.class);
+        Display mockDisplay = mock(Display.class);
+        WindowManager mockDisplayWM = mock(WindowManager.class);
+        doReturn(mockUserContext).when(mContextSpy).createContextAsUser(eq(UserHandle.of(userId)),
+                anyInt());
+        if (hasUserManager) {
+            when(mockUserContext.getSystemService(UserManager.class)).thenReturn(mockUserManager);
+        }
+        when(mockUserManager.isUserVisible()).thenReturn(isVisible);
+        when(mockUserManager.getMainDisplayIdAssignedToUser()).thenReturn(displayId);
+        if (hasDisplay) {
+            when(mDisplayManager.getDisplay(displayId)).thenReturn(mockDisplay);
+        }
+        doReturn(mockDisplayContext).when(mContextSpy).createDisplayContext(mockDisplay);
+        when(mockDisplayContext.getSystemService(WindowManager.class)).thenReturn(mockDisplayWM);
+        return mockDisplayWM;
+    }
+
     private final class TestableAuthController extends AuthController {
         private int mBuildCount = 0;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable
index 0d369a3..97f2e56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable
@@ -67,7 +67,6 @@
 import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
 import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.scene.ui.composable.SceneContainer
-import com.android.systemui.settings.displayTracker
 import com.android.systemui.testKosmos
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.awaitCancellation
@@ -127,7 +126,7 @@
 
     private val sceneContainerViewModel by lazy {
         kosmos.sceneContainerViewModelFactory
-            .create(view, kosmos.displayTracker.defaultDisplayId, {})
+            .create(view) {}
             .apply { setTransitionState(transitionState) }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
index d4d966a..2312bbd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.nano.CommunalHubState
+import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.lifecycle.InstantTaskExecutorRule
 import com.google.common.truth.Truth.assertThat
@@ -102,7 +103,7 @@
                     widgetId = widgetId,
                     provider = provider,
                     rank = rank,
-                    userSerialNumber = userSerialNumber
+                    userSerialNumber = userSerialNumber,
                 )
             }
             assertThat(widgets())
@@ -110,7 +111,7 @@
                     communalItemRankEntry1,
                     communalWidgetItemEntry1,
                     communalItemRankEntry2,
-                    communalWidgetItemEntry2
+                    communalWidgetItemEntry2,
                 )
         }
 
@@ -129,7 +130,7 @@
                 communalWidgetDao.addWidget(
                     widgetId = widgetId,
                     provider = provider,
-                    userSerialNumber = userSerialNumber
+                    userSerialNumber = userSerialNumber,
                 )
             }
 
@@ -165,7 +166,7 @@
                     communalItemRankEntry1,
                     communalWidgetItemEntry1,
                     communalItemRankEntry2,
-                    communalWidgetItemEntry2
+                    communalWidgetItemEntry2,
                 )
 
             communalWidgetDao.deleteWidgetById(communalWidgetItemEntry1.widgetId)
@@ -251,6 +252,7 @@
                     componentName = "pk_name/cls_name_4",
                     itemId = 4L,
                     userSerialNumber = 0,
+                    spanY = 3,
                 )
             assertThat(widgets())
                 .containsExactly(
@@ -267,6 +269,68 @@
         }
 
     @Test
+    fun addWidget_withDifferentSpanY_readsCorrectValuesInDb() =
+        testScope.runTest {
+            val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+            // Add widgets with different spanY values
+            communalWidgetDao.addWidget(
+                widgetId = 1,
+                provider = ComponentName("pkg_name", "cls_name_1"),
+                rank = 0,
+                userSerialNumber = 0,
+                spanY = CommunalContentSize.FULL.span,
+            )
+            communalWidgetDao.addWidget(
+                widgetId = 2,
+                provider = ComponentName("pkg_name", "cls_name_2"),
+                rank = 1,
+                userSerialNumber = 0,
+                spanY = CommunalContentSize.HALF.span,
+            )
+            communalWidgetDao.addWidget(
+                widgetId = 3,
+                provider = ComponentName("pkg_name", "cls_name_3"),
+                rank = 2,
+                userSerialNumber = 0,
+                spanY = CommunalContentSize.THIRD.span,
+            )
+
+            // Verify that the widgets have the correct spanY values
+            assertThat(widgets())
+                .containsExactly(
+                    CommunalItemRank(uid = 1L, rank = 0),
+                    CommunalWidgetItem(
+                        uid = 1L,
+                        widgetId = 1,
+                        componentName = "pkg_name/cls_name_1",
+                        itemId = 1L,
+                        userSerialNumber = 0,
+                        spanY = CommunalContentSize.FULL.span,
+                    ),
+                    CommunalItemRank(uid = 2L, rank = 1),
+                    CommunalWidgetItem(
+                        uid = 2L,
+                        widgetId = 2,
+                        componentName = "pkg_name/cls_name_2",
+                        itemId = 2L,
+                        userSerialNumber = 0,
+                        spanY = CommunalContentSize.HALF.span,
+                    ),
+                    CommunalItemRank(uid = 3L, rank = 2),
+                    CommunalWidgetItem(
+                        uid = 3L,
+                        widgetId = 3,
+                        componentName = "pkg_name/cls_name_3",
+                        itemId = 3L,
+                        userSerialNumber = 0,
+                        spanY = CommunalContentSize.THIRD.span,
+                    ),
+                )
+                .inOrder()
+        }
+
+    @Test
     fun restoreCommunalHubState() =
         testScope.runTest {
             // Set up db
@@ -288,6 +352,7 @@
                         componentName = fakeWidget.componentName,
                         itemId = rank.uid,
                         userSerialNumber = fakeWidget.userSerialNumber,
+                        spanY = 3,
                     )
                 expected[rank] = widget
             }
@@ -343,6 +408,7 @@
                 componentName = widgetInfo1.provider.flattenToString(),
                 itemId = communalItemRankEntry1.uid,
                 userSerialNumber = widgetInfo1.userSerialNumber,
+                spanY = 3,
             )
         val communalWidgetItemEntry2 =
             CommunalWidgetItem(
@@ -351,6 +417,7 @@
                 componentName = widgetInfo2.provider.flattenToString(),
                 itemId = communalItemRankEntry2.uid,
                 userSerialNumber = widgetInfo2.userSerialNumber,
+                spanY = 3,
             )
         val communalWidgetItemEntry3 =
             CommunalWidgetItem(
@@ -359,6 +426,7 @@
                 componentName = widgetInfo3.provider.flattenToString(),
                 itemId = communalItemRankEntry3.uid,
                 userSerialNumber = widgetInfo3.userSerialNumber,
+                spanY = 3,
             )
         val fakeState =
             CommunalHubState().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
index eba395b..596db07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
@@ -117,6 +117,7 @@
                     componentName = defaultWidgets[0],
                     rank = 0,
                     userSerialNumber = 0,
+                    spanY = 3,
                 )
             verify(communalWidgetDao)
                 .addWidget(
@@ -124,6 +125,7 @@
                     componentName = defaultWidgets[1],
                     rank = 1,
                     userSerialNumber = 0,
+                    spanY = 3,
                 )
             verify(communalWidgetDao)
                 .addWidget(
@@ -131,6 +133,7 @@
                     componentName = defaultWidgets[2],
                     rank = 2,
                     userSerialNumber = 0,
+                    spanY = 3,
                 )
         }
 
@@ -152,6 +155,7 @@
                     componentName = any(),
                     rank = anyInt(),
                     userSerialNumber = anyInt(),
+                    spanY = anyInt(),
                 )
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 980a5ec..3d30ecc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -143,7 +143,8 @@
     fun communalWidgets_queryWidgetsFromDb() =
         testScope.runTest {
             val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1)
-            val communalWidgetItemEntry = CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L, 0)
+            val communalWidgetItemEntry =
+                CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L, 0, 3)
             fakeWidgets.value = mapOf(communalItemRankEntry to communalWidgetItemEntry)
             fakeProviders.value = mapOf(1 to providerInfoA)
 
@@ -169,19 +170,15 @@
             fakeWidgets.value =
                 mapOf(
                     CommunalItemRank(uid = 1L, rank = 1) to
-                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
+                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3),
                     CommunalItemRank(uid = 2L, rank = 2) to
-                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0),
+                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3),
                     CommunalItemRank(uid = 3L, rank = 3) to
-                        CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L, 0),
+                        CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L, 0, 3),
                     CommunalItemRank(uid = 4L, rank = 4) to
-                        CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L, 0),
+                        CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L, 0, 3),
                 )
-            fakeProviders.value =
-                mapOf(
-                    1 to providerInfoA,
-                    2 to providerInfoB,
-                )
+            fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB)
 
             // Expect to see only widget 1 and 2
             val communalWidgets by collectLastValue(underTest.communalWidgets)
@@ -207,15 +204,11 @@
             fakeWidgets.value =
                 mapOf(
                     CommunalItemRank(uid = 1L, rank = 1) to
-                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
+                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3),
                     CommunalItemRank(uid = 2L, rank = 2) to
-                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0),
+                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3),
                 )
-            fakeProviders.value =
-                mapOf(
-                    1 to providerInfoA,
-                    2 to providerInfoB,
-                )
+            fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB)
 
             // Expect two widgets
             val communalWidgets by collectLastValue(underTest.communalWidgets)
@@ -235,11 +228,7 @@
                 )
 
             // Provider info updated for widget 1
-            fakeProviders.value =
-                mapOf(
-                    1 to providerInfoC,
-                    2 to providerInfoB,
-                )
+            fakeProviders.value = mapOf(1 to providerInfoC, 2 to providerInfoB)
             runCurrent()
 
             assertThat(communalWidgets)
@@ -269,7 +258,7 @@
             whenever(
                     communalWidgetHost.allocateIdAndBindWidget(
                         any<ComponentName>(),
-                        any<UserHandle>()
+                        any<UserHandle>(),
                     )
                 )
                 .thenReturn(id)
@@ -294,7 +283,7 @@
             whenever(
                     communalWidgetHost.allocateIdAndBindWidget(
                         any<ComponentName>(),
-                        any<UserHandle>()
+                        any<UserHandle>(),
                     )
                 )
                 .thenReturn(id)
@@ -303,7 +292,7 @@
 
             verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
             verify(communalWidgetDao, never())
-                .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt())
+                .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt())
             verify(appWidgetHost).deleteAppWidgetId(id)
 
             // Verify backup not requested
@@ -321,7 +310,7 @@
             whenever(
                     communalWidgetHost.allocateIdAndBindWidget(
                         any<ComponentName>(),
-                        any<UserHandle>()
+                        any<UserHandle>(),
                     )
                 )
                 .thenReturn(id)
@@ -332,7 +321,7 @@
 
             verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
             verify(communalWidgetDao, never())
-                .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt())
+                .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt())
             verify(appWidgetHost).deleteAppWidgetId(id)
 
             // Verify backup not requested
@@ -350,7 +339,7 @@
             whenever(
                     communalWidgetHost.allocateIdAndBindWidget(
                         any<ComponentName>(),
-                        any<UserHandle>()
+                        any<UserHandle>(),
                     )
                 )
                 .thenReturn(id)
@@ -650,8 +639,10 @@
                     eq(newWidgetId),
                     componentNameCaptor.capture(),
                     eq(2),
-                    eq(testUserSerialNumber(workProfile))
+                    eq(testUserSerialNumber(workProfile)),
+                    anyInt(),
                 )
+
             assertThat(componentNameCaptor.firstValue)
                 .isEqualTo(ComponentName("pk_name", "fake_widget_2"))
         }
@@ -662,9 +653,9 @@
             fakeWidgets.value =
                 mapOf(
                     CommunalItemRank(uid = 1L, rank = 1) to
-                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
+                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3),
                     CommunalItemRank(uid = 2L, rank = 2) to
-                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0),
+                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3),
                 )
 
             // Widget 1 is installed
@@ -707,7 +698,7 @@
             fakeWidgets.value =
                 mapOf(
                     CommunalItemRank(uid = 1L, rank = 1) to
-                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
+                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3)
                 )
 
             // Widget 1 is pending install
@@ -732,7 +723,7 @@
                         componentName = ComponentName("pk_1", "cls_1"),
                         icon = fakeIcon,
                         user = mainUser,
-                    ),
+                    )
                 )
 
             // Package for widget 1 finished installing
@@ -749,10 +740,23 @@
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
                         rank = 1,
-                    ),
+                    )
                 )
         }
 
+    @Test
+    fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup() =
+        testScope.runTest {
+            val widgetId = 1
+            val newSpanY = 6
+
+            underTest.updateWidgetSpanY(widgetId, newSpanY)
+            runCurrent()
+
+            verify(communalWidgetDao).updateWidgetSpanY(widgetId, newSpanY)
+            verify(backupManager).dataChanged()
+        }
+
     private fun setAppWidgetIds(ids: List<Int>) {
         whenever(appWidgetHost.appWidgetIds).thenReturn(ids.toIntArray())
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch
diff --git a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 073ed61..b6ec6a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -21,16 +21,23 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository
+import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION
 import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_EFFECT
 import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
+import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository.RevealAnimatorRequest
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -52,6 +59,8 @@
 
     private val fakeKeyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
 
+    private val fakePowerRepository = kosmos.fakePowerRepository
+
     private val underTest = kosmos.lightRevealScrimInteractor
 
     private val reveal1 =
@@ -107,4 +116,50 @@
             runCurrent()
             assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1, reveal2), values)
         }
+
+    @Test
+    fun transitionToAod_folding_doesNotAnimateTheScrim() =
+        kosmos.testScope.runTest {
+            updateWakefulness(goToSleepReason = WakeSleepReason.FOLD)
+            runCurrent()
+
+            // Transition to AOD
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED)
+            )
+            runCurrent()
+
+            assertThat(fakeLightRevealScrimRepository.revealAnimatorRequests.last())
+                .isEqualTo(RevealAnimatorRequest(reveal = false, duration = 0))
+        }
+
+    @Test
+    fun transitionToAod_powerButton_animatesTheScrim() =
+        kosmos.testScope.runTest {
+            updateWakefulness(goToSleepReason = WakeSleepReason.POWER_BUTTON)
+            runCurrent()
+
+            // Transition to AOD
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED)
+            )
+            runCurrent()
+
+            assertThat(fakeLightRevealScrimRepository.revealAnimatorRequests.last())
+                .isEqualTo(
+                    RevealAnimatorRequest(
+                        reveal = false,
+                        duration = DEFAULT_REVEAL_DURATION
+                    )
+                )
+        }
+
+    private fun updateWakefulness(goToSleepReason: WakeSleepReason) {
+        fakePowerRepository.updateWakefulness(
+            rawState = WakefulnessState.STARTING_TO_SLEEP,
+            lastWakeReason = WakeSleepReason.POWER_BUTTON,
+            lastSleepReason = goToSleepReason,
+            powerButtonLaunchGestureTriggered = false
+        )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/table/TableLogBufferTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/log/table/TableLogBufferTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
index 84ec1a5..77be8c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.mediarouter.data.repository
 
 import androidx.test.filters.SmallTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
@@ -28,9 +29,11 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
 class MediaRouterRepositoryTest : SysuiTestCase() {
     val kosmos = Kosmos()
     val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt
index acd69af..da16640 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt
@@ -26,10 +26,11 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @RunWithLooper
 class QSFragmentComposeViewModelForceQSTest(private val testData: TestData) :
     AbstractQSFragmentComposeViewModelTest() {
@@ -75,7 +76,7 @@
     companion object {
         private const val EXPANSION = 0.3f
 
-        @Parameterized.Parameters(name = "{0}")
+        @Parameters(name = "{0}")
         @JvmStatic
         fun createTestData(): List<TestData> {
             return statusBarStates.flatMap { statusBarState ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilterTest.kt
deleted file mode 100644
index efde1ec..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilterTest.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.scene.ui.viewmodel
-
-import android.graphics.Region
-import android.view.setSystemGestureExclusionRegion
-import androidx.compose.ui.geometry.Offset
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.sceneContainerGestureFilterFactory
-import com.android.systemui.settings.displayTracker
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SceneContainerGestureFilterTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val displayId = kosmos.displayTracker.defaultDisplayId
-
-    private val underTest = kosmos.sceneContainerGestureFilterFactory.create(displayId)
-    private val activationJob = Job()
-
-    @Test
-    fun shouldFilterGesture_whenNoRegion_returnsFalse() =
-        testScope.runTest {
-            activate()
-            setSystemGestureExclusionRegion(displayId, null)
-            runCurrent()
-
-            assertThat(underTest.shouldFilterGesture(Offset(100f, 100f))).isFalse()
-        }
-
-    @Test
-    fun shouldFilterGesture_whenOutsideRegion_returnsFalse() =
-        testScope.runTest {
-            activate()
-            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
-            runCurrent()
-
-            assertThat(underTest.shouldFilterGesture(Offset(300f, 100f))).isFalse()
-        }
-
-    @Test
-    fun shouldFilterGesture_whenInsideRegion_returnsTrue() =
-        testScope.runTest {
-            activate()
-            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
-            runCurrent()
-
-            assertThat(underTest.shouldFilterGesture(Offset(100f, 100f))).isTrue()
-        }
-
-    @Test(expected = IllegalStateException::class)
-    fun shouldFilterGesture_beforeActivation_throws() =
-        testScope.runTest {
-            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
-            runCurrent()
-
-            underTest.shouldFilterGesture(Offset(100f, 100f))
-        }
-
-    @Test(expected = IllegalStateException::class)
-    fun shouldFilterGesture_afterCancellation_throws() =
-        testScope.runTest {
-            activate()
-            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
-            runCurrent()
-
-            cancel()
-
-            underTest.shouldFilterGesture(Offset(100f, 100f))
-        }
-
-    private fun TestScope.activate() {
-        underTest.activateIn(testScope, activationJob)
-        runCurrent()
-    }
-
-    private fun TestScope.cancel() {
-        activationJob.cancel()
-        runCurrent()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt
index 664315d..ca9500b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt
@@ -51,7 +51,7 @@
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -134,7 +134,7 @@
             runCurrent()
 
             // THEN the view does not play a haptic feedback constant
-            verifyZeroInteractions(view)
+            verifyNoMoreInteractions(view)
         }
 
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK, Flags.FLAG_DUAL_SHADE)
@@ -202,7 +202,7 @@
             runCurrent()
 
             // THEN the view does not play a haptic feedback constant
-            verifyZeroInteractions(view)
+            verifyNoMoreInteractions(view)
         }
 
     private fun createTransitionState(from: SceneKey, to: ContentKey) =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index a37f511..4ec0802 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
-import com.android.systemui.settings.displayTracker
 import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shared.flag.DualShade
@@ -82,7 +81,6 @@
         underTest =
             kosmos.sceneContainerViewModelFactory.create(
                 view,
-                kosmos.displayTracker.defaultDisplayId,
                 { motionEventHandler ->
                     this@SceneContainerViewModelTest.motionEventHandler = motionEventHandler
                 },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
index 15d6881..fcb366b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.EnableSceneContainer
@@ -50,6 +51,7 @@
 import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.TestScope
@@ -248,6 +250,27 @@
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Communal))
         }
 
+    @Test
+    fun upTransitionSceneKey_neverGoesBackToShadeScene() =
+        testScope.runTest {
+            val actions by collectValues(underTest.actions)
+            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            kosmos.sceneInteractor.changeScene(Scenes.Shade, "")
+            assertThat(currentScene).isEqualTo(Scenes.Shade)
+
+            kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "")
+            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+
+            actions.forEachIndexed { index, map ->
+                assertWithMessage(
+                        "Actions on index $index is incorrectly mapping back to the Shade scene!"
+                    )
+                    .that((map[Swipe.Up] as? UserActionResult.ChangeScene)?.toScene)
+                    .isNotEqualTo(Scenes.Shade)
+            }
+        }
+
     private fun TestScope.setDeviceEntered(isEntered: Boolean) {
         if (isEntered) {
             // Unlock the device marking the device has entered.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
index 28857a0..34f4608 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
@@ -208,8 +208,8 @@
             assertThat(footerVisible).isTrue()
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
     fun onClick_whenHistoryDisabled_leadsToSettingsPage() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.onClick)
@@ -222,8 +222,8 @@
             assertThat(onClick?.backStack).isEmpty()
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
     fun onClick_whenHistoryEnabled_leadsToHistoryPage() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.onClick)
@@ -237,8 +237,8 @@
                 .containsExactly(Settings.ACTION_NOTIFICATION_SETTINGS)
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
     fun onClick_whenOneModeHidingNotifications_leadsToModeSettings() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.onClick)
@@ -263,8 +263,8 @@
                 .containsExactly(Settings.ACTION_ZEN_MODE_SETTINGS)
         }
 
-    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
     @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
     fun onClick_whenMultipleModesHidingNotifications_leadsToGeneralModesSettings() =
         testScope.runTest {
             val onClick by collectLastValue(underTest.onClick)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
index a0f6431..9d93a9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
@@ -61,7 +61,7 @@
             interactor,
             kosmos.testDispatcher,
             mockDialogDelegate,
-            mockDialogEventLogger
+            mockDialogEventLogger,
         )
 
     @Test
@@ -97,7 +97,7 @@
             assertThat(tiles?.size).isEqualTo(3)
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Disabled by other")
-                assertThat(this.subtext).isEqualTo("Set up")
+                assertThat(this.subtext).isEqualTo("Not set")
                 assertThat(this.enabled).isEqualTo(false)
             }
             with(tiles?.elementAt(1)!!) {
@@ -323,10 +323,10 @@
             assertThat(tiles!!).hasSize(6)
             assertThat(tiles!![0].subtext).isEqualTo("When the going gets tough")
             assertThat(tiles!![1].subtext).isEqualTo("On • When in Rome")
-            assertThat(tiles!![2].subtext).isEqualTo("Set up")
+            assertThat(tiles!![2].subtext).isEqualTo("Not set")
             assertThat(tiles!![3].subtext).isEqualTo("Off")
             assertThat(tiles!![4].subtext).isEqualTo("On")
-            assertThat(tiles!![5].subtext).isEqualTo("Set up")
+            assertThat(tiles!![5].subtext).isEqualTo("Not set")
         }
 
     @Test
@@ -387,7 +387,7 @@
             }
             with(tiles?.elementAt(2)!!) {
                 assertThat(this.stateDescription).isEqualTo("Off")
-                assertThat(this.subtextDescription).isEqualTo("Set up")
+                assertThat(this.subtextDescription).isEqualTo("Not set")
             }
             with(tiles?.elementAt(3)!!) {
                 assertThat(this.stateDescription).isEqualTo("Off")
@@ -399,7 +399,7 @@
             }
             with(tiles?.elementAt(5)!!) {
                 assertThat(this.stateDescription).isEqualTo("Off")
-                assertThat(this.subtextDescription).isEqualTo("Set up")
+                assertThat(this.subtextDescription).isEqualTo("Not set")
             }
 
             // All tiles have the same long click info
@@ -451,7 +451,7 @@
                         .setName("Active without manual")
                         .setActive(true)
                         .setManualInvocationAllowed(false)
-                        .build(),
+                        .build()
                 )
             )
             runCurrent()
@@ -492,7 +492,7 @@
                         .setId("ID")
                         .setName("Disabled by other")
                         .setEnabled(false, /* byUser= */ false)
-                        .build(),
+                        .build()
                 )
             )
             runCurrent()
@@ -500,7 +500,7 @@
             assertThat(tiles?.size).isEqualTo(1)
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Disabled by other")
-                assertThat(this.subtext).isEqualTo("Set up")
+                assertThat(this.subtext).isEqualTo("Not set")
                 assertThat(this.enabled).isEqualTo(false)
 
                 // Click the tile
@@ -519,7 +519,7 @@
             // Check that nothing happened to the tile
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Disabled by other")
-                assertThat(this.subtext).isEqualTo("Set up")
+                assertThat(this.subtext).isEqualTo("Not set")
                 assertThat(this.enabled).isEqualTo(false)
             }
         }
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7209c44f9..2ddaa56 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1120,7 +1120,7 @@
     <string name="zen_mode_off">Off</string>
 
     <!-- Modes: label for a mode that needs to be set up [CHAR LIMIT=35] -->
-    <string name="zen_mode_set_up">Set up</string>
+    <string name="zen_mode_set_up">Not set</string>
 
     <!-- Modes: label for a mode that cannot be manually turned on [CHAR LIMIT=35] -->
     <string name="zen_mode_no_manual_invocation">Manage in settings</string>
diff --git a/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/4.json b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/4.json
new file mode 100644
index 0000000..c3fb8d4
--- /dev/null
+++ b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/4.json
@@ -0,0 +1,88 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 4,
+    "identityHash": "a49f2f7d25cf12d1baf9a3a3e6243b64",
+    "entities": [
+      {
+        "tableName": "communal_widget_table",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `widget_id` INTEGER NOT NULL, `component_name` TEXT NOT NULL, `item_id` INTEGER NOT NULL, `user_serial_number` INTEGER NOT NULL DEFAULT -1, `span_y` INTEGER NOT NULL DEFAULT 3)",
+        "fields": [
+          {
+            "fieldPath": "uid",
+            "columnName": "uid",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "widgetId",
+            "columnName": "widget_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "componentName",
+            "columnName": "component_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "itemId",
+            "columnName": "item_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userSerialNumber",
+            "columnName": "user_serial_number",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "-1"
+          },
+          {
+            "fieldPath": "spanY",
+            "columnName": "span_y",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "3"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "uid"
+          ]
+        }
+      },
+      {
+        "tableName": "communal_item_rank_table",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `rank` INTEGER NOT NULL DEFAULT 0)",
+        "fields": [
+          {
+            "fieldPath": "uid",
+            "columnName": "uid",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "rank",
+            "columnName": "rank",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "0"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "uid"
+          ]
+        }
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a49f2f7d25cf12d1baf9a3a3e6243b64')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b39aae9..a5bd559 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -19,6 +19,7 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
+import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
 
@@ -54,6 +55,7 @@
 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
 import android.util.RotationUtils;
@@ -211,9 +213,13 @@
         }
     };
 
-    private void closeDialog(String reason) {
+    private void closeDialog(String reasonString) {
+        closeDialog(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, reasonString);
+    }
+
+    private void closeDialog(@DismissedReason int reason, String reasonString) {
         if (isShowing()) {
-            Log.i(TAG, "Close BP, reason :" + reason);
+            Log.i(TAG, "Close BP, reason :" + reasonString);
             mCurrentDialog.dismissWithoutCallback(true /* animate */);
             mCurrentDialog = null;
 
@@ -223,8 +229,7 @@
 
             try {
                 if (mReceiver != null) {
-                    mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
-                            null /* credentialAttestation */);
+                    mReceiver.onDialogDismissed(reason, null /* credentialAttestation */);
                     mReceiver = null;
                 }
             } catch (RemoteException e) {
@@ -251,25 +256,7 @@
 
     private void cancelIfOwnerIsNotInForeground() {
         mExecution.assertIsMainThread();
-        if (mCurrentDialog != null) {
-            try {
-                mCurrentDialog.dismissWithoutCallback(true /* animate */);
-                mCurrentDialog = null;
-
-                for (Callback cb : mCallbacks) {
-                    cb.onBiometricPromptDismissed();
-                }
-
-                if (mReceiver != null) {
-                    mReceiver.onDialogDismissed(
-                            BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
-                            null /* credentialAttestation */);
-                    mReceiver = null;
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Remote exception", e);
-            }
-        }
+        closeDialog("owner not in foreground");
     }
 
     /**
@@ -1271,10 +1258,44 @@
         if (!promptInfo.isAllowBackgroundAuthentication() && isOwnerInBackground()) {
             cancelIfOwnerIsNotInForeground();
         } else {
-            mCurrentDialog.show(mWindowManager);
+            WindowManager wm = getWindowManagerForUser(userId);
+            if (wm != null) {
+                mCurrentDialog.show(wm);
+            } else {
+                closeDialog(BiometricPrompt.DISMISSED_REASON_ERROR_NO_WM,
+                        "unable to get WM instance for user");
+            }
         }
     }
 
+    @Nullable
+    private WindowManager getWindowManagerForUser(int userId) {
+        if (!mUserManager.isVisibleBackgroundUsersSupported()) {
+            return mWindowManager;
+        }
+        UserManager um = mContext.createContextAsUser(UserHandle.of(userId),
+                0 /* flags */).getSystemService(UserManager.class);
+        if (um == null) {
+            Log.e(TAG, "unable to get UserManager for user=" + userId);
+            return null;
+        }
+        if (!um.isUserVisible()) {
+            // not visible user - use default window manager
+            return mWindowManager;
+        }
+        int displayId = um.getMainDisplayIdAssignedToUser();
+        if (displayId == INVALID_DISPLAY) {
+            Log.e(TAG, "unable to get display assigned to user=" + userId);
+            return null;
+        }
+        Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            Log.e(TAG, "unable to get Display for user=" + userId);
+            return null;
+        }
+        return mContext.createDisplayContext(display).getSystemService(WindowManager.class);
+    }
+
     private void onDialogDismissed(@DismissedReason int reason) {
         if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason);
         if (mCurrentDialog == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
index 8f1854f..17f4f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
@@ -26,7 +26,7 @@
 import androidx.sqlite.db.SupportSQLiteDatabase
 import com.android.systemui.res.R
 
-@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 3)
+@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 4)
 abstract class CommunalDatabase : RoomDatabase() {
     abstract fun communalWidgetDao(): CommunalWidgetDao
 
@@ -43,19 +43,16 @@
          * @param callback An optional callback registered to the database. Only effective when a
          *   new instance is created.
          */
-        fun getInstance(
-            context: Context,
-            callback: Callback? = null,
-        ): CommunalDatabase {
+        fun getInstance(context: Context, callback: Callback? = null): CommunalDatabase {
             if (instance == null) {
                 instance =
                     Room.databaseBuilder(
                             context,
                             CommunalDatabase::class.java,
-                            context.resources.getString(R.string.config_communalDatabase)
+                            context.resources.getString(R.string.config_communalDatabase),
                         )
                         .also { builder ->
-                            builder.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
+                            builder.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
                             builder.fallbackToDestructiveMigration(dropAllTables = true)
                             callback?.let { callback -> builder.addCallback(callback) }
                         }
@@ -103,5 +100,21 @@
                     )
                 }
             }
+
+        /**
+         * This migration adds a span_y column to the communal_widget_table and sets its default
+         * value to 3.
+         */
+        @VisibleForTesting
+        val MIGRATION_3_4 =
+            object : Migration(3, 4) {
+                override fun migrate(db: SupportSQLiteDatabase) {
+                    Log.i(TAG, "Migrating from version 3 to 4")
+                    db.execSQL(
+                        "ALTER TABLE communal_widget_table " +
+                            "ADD COLUMN span_y INTEGER NOT NULL DEFAULT 3"
+                    )
+                }
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
index e33aead..f9d2a84 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
@@ -40,6 +40,12 @@
      */
     @ColumnInfo(name = "user_serial_number", defaultValue = "$USER_SERIAL_NUMBER_UNDEFINED")
     val userSerialNumber: Int,
+
+    /**
+     * The vertical span of the widget. Span_Y default value corresponds to
+     * CommunalContentSize.HALF.span
+     */
+    @ColumnInfo(name = "span_y", defaultValue = "3") val spanY: Int,
 ) {
     companion object {
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index 93b86bd..5dd4c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -25,6 +25,7 @@
 import androidx.room.Transaction
 import androidx.sqlite.db.SupportSQLiteDatabase
 import com.android.systemui.communal.nano.CommunalHubState
+import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.widgets.CommunalWidgetHost
 import com.android.systemui.communal.widgets.CommunalWidgetModule.Companion.DEFAULT_WIDGETS
 import com.android.systemui.dagger.SysUISingleton
@@ -153,14 +154,15 @@
 
     @Query(
         "INSERT INTO communal_widget_table" +
-            "(widget_id, component_name, item_id, user_serial_number) " +
-            "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber)"
+            "(widget_id, component_name, item_id, user_serial_number, span_y) " +
+            "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber, :spanY)"
     )
     fun insertWidget(
         widgetId: Int,
         componentName: String,
         itemId: Long,
         userSerialNumber: Int,
+        spanY: Int = 3,
     ): Long
 
     @Query("INSERT INTO communal_item_rank_table(rank) VALUES(:rank)")
@@ -169,6 +171,9 @@
     @Query("UPDATE communal_item_rank_table SET rank = :order WHERE uid = :itemUid")
     fun updateItemRank(itemUid: Long, order: Int)
 
+    @Query("UPDATE communal_widget_table SET span_y = :spanY WHERE widget_id = :widgetId")
+    fun updateWidgetSpanY(widgetId: Int, spanY: Int)
+
     @Query("DELETE FROM communal_widget_table") fun clearCommunalWidgetsTable()
 
     @Query("DELETE FROM communal_item_rank_table") fun clearCommunalItemRankTable()
@@ -189,12 +194,14 @@
         provider: ComponentName,
         rank: Int? = null,
         userSerialNumber: Int,
+        spanY: Int = CommunalContentSize.HALF.span,
     ): Long {
         return addWidget(
             widgetId = widgetId,
             componentName = provider.flattenToString(),
             rank = rank,
             userSerialNumber = userSerialNumber,
+            spanY = spanY,
         )
     }
 
@@ -204,6 +211,7 @@
         componentName: String,
         rank: Int? = null,
         userSerialNumber: Int,
+        spanY: Int = 3,
     ): Long {
         val widgets = getWidgetsNow()
 
@@ -224,6 +232,7 @@
             componentName = componentName,
             itemId = insertItemRank(newRank),
             userSerialNumber = userSerialNumber,
+            spanY = spanY,
         )
     }
 
@@ -246,7 +255,8 @@
         clearCommunalItemRankTable()
 
         state.widgets.forEach {
-            addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber)
+            val spanY = if (it.spanY != 0) it.spanY else CommunalContentSize.HALF.span
+            addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber, spanY)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index 6cdd9ff..3312f3c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -92,6 +92,14 @@
 
     /** Aborts the restore process and removes files from disk if necessary. */
     fun abortRestoreWidgets()
+
+    /**
+     * Update the spanY of a widget in the database.
+     *
+     * @param widgetId id of the widget to update.
+     * @param spanY new spanY value for the widget.
+     */
+    fun updateWidgetSpanY(widgetId: Int, spanY: Int)
 }
 
 @SysUISingleton
@@ -118,20 +126,30 @@
 
     /** Widget metadata from database + matching [AppWidgetProviderInfo] if any. */
     private val widgetEntries: Flow<List<CommunalWidgetEntry>> =
-        combine(
-            communalWidgetDao.getWidgets(),
-            communalWidgetHost.appWidgetProviders,
-        ) { entries, providers ->
+        combine(communalWidgetDao.getWidgets(), communalWidgetHost.appWidgetProviders) {
+            entries,
+            providers ->
             entries.mapNotNull { (rank, widget) ->
                 CommunalWidgetEntry(
                     appWidgetId = widget.widgetId,
                     componentName = widget.componentName,
                     rank = rank.rank,
-                    providerInfo = providers[widget.widgetId]
+                    providerInfo = providers[widget.widgetId],
                 )
             }
         }
 
+    override fun updateWidgetSpanY(widgetId: Int, spanY: Int) {
+        bgScope.launch {
+            communalWidgetDao.updateWidgetSpanY(widgetId, spanY)
+            logger.i({ "Updated spanY of widget $int1 to $int2." }) {
+                int1 = widgetId
+                int2 = spanY
+            }
+            backupManager.dataChanged()
+        }
+    }
+
     @OptIn(ExperimentalCoroutinesApi::class)
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
         widgetEntries
@@ -197,6 +215,7 @@
                     provider = provider,
                     rank = rank,
                     userSerialNumber = userManager.getUserSerialNumber(user.identifier),
+                    spanY = 3,
                 )
                 backupManager.dataChanged()
             } else {
@@ -325,6 +344,7 @@
                         componentName = restoredWidget.componentName
                         rank = restoredWidget.rank
                         userSerialNumber = userManager.getUserSerialNumber(newUser.identifier)
+                        spanY = restoredWidget.spanY
                     }
                 }
             val newState = CommunalHubState().apply { widgets = newWidgets.toTypedArray() }
@@ -383,6 +403,7 @@
             appWidgetId = entry.appWidgetId,
             providerInfo = entry.providerInfo!!,
             rank = entry.rank,
+            spanY = entry.spanY,
         )
     }
 
@@ -400,6 +421,7 @@
                 appWidgetId = entry.appWidgetId,
                 providerInfo = entry.providerInfo!!,
                 rank = entry.rank,
+                spanY = entry.spanY,
             )
         }
 
@@ -412,6 +434,7 @@
                 componentName = componentName,
                 icon = session.icon,
                 user = session.user,
+                spanY = entry.spanY,
             )
         } else {
             null
@@ -423,5 +446,6 @@
         val componentName: String,
         val rank: Int,
         var providerInfo: AppWidgetProviderInfo? = null,
+        var spanY: Int = 3,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto b/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto
index bc14ae1..7602a7a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto
+++ b/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto
@@ -38,5 +38,8 @@
 
         // Serial number of the user associated with the widget.
         int32 user_serial_number = 4;
+
+        // The vertical span of the widget
+        int32 span_y = 5;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index 63b1a14..bcbc8f6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -31,6 +31,7 @@
         override val appWidgetId: Int,
         val providerInfo: AppWidgetProviderInfo,
         override val rank: Int,
+        val spanY: Int = 3,
     ) : CommunalWidgetContentModel
 
     /** Widget is pending installation */
@@ -40,5 +41,6 @@
         val componentName: ComponentName,
         val icon: Bitmap?,
         val user: UserHandle,
+        val spanY: Int = 3,
     ) : CommunalWidgetContentModel
 }
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
index 099e3fc..4b9ac1d 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamClockTimeComplicationComponent.kt
@@ -21,9 +21,10 @@
 import android.view.View
 import android.widget.TextClock
 import com.android.internal.util.Preconditions
-import com.android.systemui.res.R
+import com.android.systemui.Flags
 import com.android.systemui.complication.DreamClockTimeComplication
 import com.android.systemui.complication.DreamClockTimeComplication.DreamClockTimeViewHolder
+import com.android.systemui.res.R
 import dagger.Module
 import dagger.Provides
 import dagger.Subcomponent
@@ -71,9 +72,13 @@
                             /* root = */ null,
                             /* attachToRoot = */ false,
                         ) as TextClock,
-                        "R.layout.dream_overlay_complication_clock_time did not properly inflate"
+                        "R.layout.dream_overlay_complication_clock_time did not properly inflate",
                     )
-                view.setFontVariationSettings(TAG_WEIGHT + WEIGHT)
+                if (Flags.dreamOverlayUpdatedFont()) {
+                    view.setFontVariationSettings("'wght' 600, 'opsz' 96")
+                } else {
+                    view.setFontVariationSettings(TAG_WEIGHT + WEIGHT)
+                }
                 return view
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 9443570..1497026 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -19,10 +19,12 @@
 import com.android.keyguard.logging.ScrimLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION
 import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.ScreenPowerState
+import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.util.kotlin.sample
 import dagger.Lazy
@@ -50,11 +52,29 @@
         scope.launch {
             transitionInteractor.startedKeyguardTransitionStep.collect {
                 scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it)
-                lightRevealScrimRepository.startRevealAmountAnimator(willBeRevealedInState(it.to))
+                val animationDuration =
+                    if (it.to == KeyguardState.AOD && isLastSleepDueToFold) {
+                        // Do not animate the scrim when folding as we want to cover the screen
+                        // with the scrim immediately while displays are switching.
+                        // This is needed to play the fold to AOD animation which starts with
+                        // fully black screen (see FoldAodAnimationController)
+                        0L
+                    } else {
+                        DEFAULT_REVEAL_DURATION
+                    }
+
+                lightRevealScrimRepository.startRevealAmountAnimator(
+                    willBeRevealedInState(it.to),
+                    duration = animationDuration
+                )
             }
         }
     }
 
+    private val isLastSleepDueToFold: Boolean
+        get() = powerInteractor.get().detailedWakefulness.value
+            .lastSleepReason == WakeSleepReason.FOLD
+
     /**
      * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
      * and use that for the starting transition.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 8386628..57cb10f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -121,7 +121,10 @@
     private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) {
         constraints.apply {
             constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT)
-            constrainHeight(R.id.lockscreen_clock_view_large, ConstraintSet.MATCH_CONSTRAINT)
+            // The following two lines make lockscreen_clock_view_large is constrained to available
+            // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
+            constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT)
+            constrainMaxHeight(R.id.lockscreen_clock_view_large, 0)
             val largeClockTopMargin =
                 SystemBarUtils.getStatusBarHeight(context) +
                     context.resources.getDimensionPixelSize(
@@ -138,7 +141,7 @@
                 R.id.lockscreen_clock_view_large,
                 ConstraintSet.END,
                 PARENT_ID,
-                ConstraintSet.END
+                ConstraintSet.END,
             )
 
             // In preview, we'll show UDFPS icon for UDFPS devices
@@ -160,14 +163,14 @@
                     BOTTOM,
                     PARENT_ID,
                     BOTTOM,
-                    clockBottomMargin
+                    clockBottomMargin,
                 )
             }
 
             constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT)
             constrainHeight(
                 R.id.lockscreen_clock_view,
-                context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height)
+                context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height),
             )
             connect(
                 R.id.lockscreen_clock_view,
@@ -175,7 +178,7 @@
                 PARENT_ID,
                 START,
                 context.resources.getDimensionPixelSize(customizationR.dimen.clock_padding_start) +
-                    context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+                    context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
             )
             val smallClockTopMargin =
                 context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
@@ -188,7 +191,7 @@
         context: Context,
         rootView: ConstraintLayout,
         previewClock: ClockController,
-        viewModel: KeyguardPreviewClockViewModel
+        viewModel: KeyguardPreviewClockViewModel,
     ) {
         val cs = ConstraintSet().apply { clone(rootView) }
         applyClockDefaultConstraints(context, cs)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index a4fe4e3..ad76b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -169,50 +169,34 @@
 
     private void enableZenMode(@Nullable Expandable expandable) {
         int zenDuration = mSettingZenDuration.getValue();
-        boolean showOnboarding = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0
-                && Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.ZEN_SETTINGS_UPDATED, 0) != 1;
-        if (showOnboarding) {
-            // don't show on-boarding again or notification ever
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
-            // turn on DND
-            mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
-            // show on-boarding screen
-            Intent intent = new Intent(Settings.ZEN_MODE_ONBOARDING);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
-        } else {
-            switch (zenDuration) {
-                case Settings.Secure.ZEN_DURATION_PROMPT:
-                    mUiHandler.post(() -> {
-                        Dialog dialog = makeZenModeDialog();
-                        if (expandable != null) {
-                            DialogTransitionAnimator.Controller controller =
-                                    expandable.dialogTransitionController(new DialogCuj(
-                                            InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                                            INTERACTION_JANK_TAG));
-                            if (controller != null) {
-                                mDialogTransitionAnimator.show(dialog,
-                                        controller, /* animateBackgroundBoundsChange= */ false);
-                            } else {
-                                dialog.show();
-                            }
+        switch (zenDuration) {
+            case Settings.Secure.ZEN_DURATION_PROMPT:
+                mUiHandler.post(() -> {
+                    Dialog dialog = makeZenModeDialog();
+                    if (expandable != null) {
+                        DialogTransitionAnimator.Controller controller =
+                                expandable.dialogTransitionController(new DialogCuj(
+                                        InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                        INTERACTION_JANK_TAG));
+                        if (controller != null) {
+                            mDialogTransitionAnimator.show(dialog,
+                                    controller, /* animateBackgroundBoundsChange= */ false);
                         } else {
                             dialog.show();
                         }
-                    });
-                    break;
-                case Settings.Secure.ZEN_DURATION_FOREVER:
-                    mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
-                    break;
-                default:
-                    Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration,
-                            mHost.getUserId(), true).id;
-                    mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
-                            conditionId, TAG);
-            }
+                    } else {
+                        dialog.show();
+                    }
+                });
+                break;
+            case Settings.Secure.ZEN_DURATION_FOREVER:
+                mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
+                break;
+            default:
+                Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration,
+                        mHost.getUserId(), true).id;
+                mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                        conditionId, TAG);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepository.kt
deleted file mode 100644
index a8d0777..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepository.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.data.repository
-
-import android.graphics.Region
-import android.view.ISystemGestureExclusionListener
-import android.view.IWindowManager
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
-import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-
-@SysUISingleton
-class SystemGestureExclusionRepository
-@Inject
-constructor(private val windowManager: IWindowManager) {
-
-    /**
-     * Returns [Flow] of the [Region] in which system gestures should be excluded on the display
-     * identified with [displayId].
-     */
-    fun exclusionRegion(displayId: Int): Flow<Region?> {
-        return conflatedCallbackFlow {
-            val listener =
-                object : ISystemGestureExclusionListener.Stub() {
-                    override fun onSystemGestureExclusionChanged(
-                        displayId: Int,
-                        restrictedRegion: Region?,
-                        unrestrictedRegion: Region?,
-                    ) {
-                        trySend(restrictedRegion)
-                    }
-                }
-            windowManager.registerSystemGestureExclusionListener(listener, displayId)
-
-            awaitClose {
-                windowManager.unregisterSystemGestureExclusionListener(listener, displayId)
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractor.kt
deleted file mode 100644
index 4cee874..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractor.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.domain.interactor
-
-import android.graphics.Region
-import com.android.systemui.scene.data.repository.SystemGestureExclusionRepository
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-
-class SystemGestureExclusionInteractor
-@Inject
-constructor(private val repository: SystemGestureExclusionRepository) {
-
-    /**
-     * Returns [Flow] of the [Region] in which system gestures should be excluded on the display
-     * identified with [displayId].
-     */
-    fun exclusionRegion(displayId: Int): Flow<Region?> {
-        return repository.exclusionRegion(displayId)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index a8be580..38f4e73 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -107,13 +107,7 @@
             view.viewModel(
                 traceName = "SceneWindowRootViewBinder",
                 minWindowLifecycleState = WindowLifecycleState.ATTACHED,
-                factory = {
-                    viewModelFactory.create(
-                        view,
-                        view.context.displayId,
-                        motionEventHandlerReceiver,
-                    )
-                },
+                factory = { viewModelFactory.create(view, motionEventHandlerReceiver) },
             ) { viewModel ->
                 try {
                     view.setViewTreeOnBackPressedDispatcherOwner(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilter.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilter.kt
deleted file mode 100644
index a1d915a..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilter.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.ui.viewmodel
-
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.geometry.Offset
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
-import com.android.systemui.scene.domain.interactor.SystemGestureExclusionInteractor
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlin.math.roundToInt
-
-/** Decides whether drag gestures should be filtered out in the scene container framework. */
-class SceneContainerGestureFilter
-@AssistedInject
-constructor(interactor: SystemGestureExclusionInteractor, @Assisted displayId: Int) :
-    ExclusiveActivatable() {
-
-    private val hydrator = Hydrator("SceneContainerGestureFilter.hydrator")
-    private val exclusionRegion by
-        hydrator.hydratedStateOf(
-            traceName = "exclusionRegion",
-            initialValue = null,
-            source = interactor.exclusionRegion(displayId),
-        )
-
-    override suspend fun onActivated(): Nothing {
-        hydrator.activate()
-    }
-
-    /**
-     * Returns `true` if a drag gesture starting at [startPosition] should be filtered out (e.g.
-     * ignored, `false` otherwise.
-     *
-     * Invoke this and pass in the position of the `ACTION_DOWN` pointer event that began the
-     * gesture.
-     */
-    fun shouldFilterGesture(startPosition: Offset): Boolean {
-        check(isActive) { "Must be activated to use!" }
-
-        return exclusionRegion?.contains(startPosition.x.roundToInt(), startPosition.y.roundToInt())
-            ?: false
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(displayId: Int): SceneContainerGestureFilter
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index f505385..889380a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -19,7 +19,6 @@
 import android.view.MotionEvent
 import android.view.View
 import androidx.compose.runtime.getValue
-import androidx.compose.ui.geometry.Offset
 import com.android.app.tracing.coroutines.launch
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.DefaultEdgeDetector
@@ -61,10 +60,8 @@
     shadeInteractor: ShadeInteractor,
     private val splitEdgeDetector: SplitEdgeDetector,
     private val logger: SceneLogger,
-    gestureFilterFactory: SceneContainerGestureFilter.Factory,
     hapticsViewModelFactory: SceneContainerHapticsViewModel.Factory,
     @Assisted view: View,
-    @Assisted displayId: Int,
     @Assisted private val motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
 ) : ExclusiveActivatable() {
 
@@ -92,8 +89,6 @@
                 },
         )
 
-    private val gestureFilter: SceneContainerGestureFilter = gestureFilterFactory.create(displayId)
-
     override suspend fun onActivated(): Nothing {
         try {
             // Sends a MotionEventHandler to the owner of the view-model so they can report
@@ -112,7 +107,6 @@
 
             coroutineScope {
                 launch { hydrator.activate() }
-                launch { gestureFilter.activate() }
                 launch("SceneContainerHapticsViewModel") { hapticsViewModel.activate() }
             }
             awaitCancellation()
@@ -262,17 +256,6 @@
         }
     }
 
-    /**
-     * Returns `true` if a drag gesture starting at [startPosition] should be filtered out (e.g.
-     * ignored, `false` otherwise.
-     *
-     * Invoke this and pass in the position of the `ACTION_DOWN` pointer event that began the
-     * gesture.
-     */
-    fun shouldFilterGesture(startPosition: Offset): Boolean {
-        return gestureFilter.shouldFilterGesture(startPosition)
-    }
-
     /** Defines interface for classes that can handle externally-reported [MotionEvent]s. */
     interface MotionEventHandler {
         /** Notifies that a [MotionEvent] has occurred. */
@@ -289,7 +272,6 @@
     interface Factory {
         fun create(
             view: View,
-            displayId: Int,
             motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
         ): SceneContainerViewModel
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
index b5d45a4..fb7c34f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
@@ -15,17 +15,17 @@
 
 /** [ScreenshotData] represents the current state of a single screenshot being acquired. */
 data class ScreenshotData(
-    @ScreenshotType var type: Int,
-    @ScreenshotSource var source: Int,
+    @ScreenshotType val type: Int,
+    @ScreenshotSource val source: Int,
     /** UserHandle for the owner of the app being screenshotted, if known. */
-    var userHandle: UserHandle?,
+    val userHandle: UserHandle?,
     /** ComponentName of the top-most app in the screenshot. */
-    var topComponent: ComponentName?,
+    val topComponent: ComponentName?,
     var screenBounds: Rect?,
-    var taskId: Int,
+    val taskId: Int,
     var insets: Insets,
     var bitmap: Bitmap?,
-    var displayId: Int,
+    val displayId: Int,
 ) {
     val packageNameString
         get() = topComponent?.packageName ?: ""
@@ -50,16 +50,21 @@
             )
 
         @VisibleForTesting
-        fun forTesting() =
+        fun forTesting(
+            userHandle: UserHandle? = null,
+            source: Int = ScreenshotSource.SCREENSHOT_KEY_CHORD,
+            topComponent: ComponentName? = null,
+            bitmap: Bitmap? = null,
+        ) =
             ScreenshotData(
                 type = WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
-                source = ScreenshotSource.SCREENSHOT_KEY_CHORD,
-                userHandle = null,
-                topComponent = null,
+                source = source,
+                userHandle = userHandle,
+                topComponent = topComponent,
                 screenBounds = null,
                 taskId = 0,
                 insets = Insets.NONE,
-                bitmap = null,
+                bitmap = bitmap,
                 displayId = Display.DEFAULT_DISPLAY,
             )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 38608d0..ab8a953 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -202,10 +202,10 @@
     // Return the single display to be screenshot based upon the request.
     private suspend fun getDisplayToScreenshot(screenshotRequest: ScreenshotRequest): Display {
         return when (screenshotRequest.source) {
-            // TODO(b/367394043): Overview requests should use a display ID provided in
-            //  ScreenshotRequest.
             ScreenshotSource.SCREENSHOT_OVERVIEW ->
-                displayRepository.getDisplay(Display.DEFAULT_DISPLAY)
+                // Show on the display where overview was shown if available.
+                displayRepository.getDisplay(screenshotRequest.displayId)
+                    ?: displayRepository.getDisplay(Display.DEFAULT_DISPLAY)
                     ?: error("Can't find default display")
 
             // Key chord and vendor gesture occur on the device itself, so screenshot the device's
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
index cc6e8c2..3113dc4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
@@ -32,6 +32,7 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
 
 /**
  * Models the UI state for the user actions that the user can perform to navigate to other scenes.
@@ -50,7 +51,9 @@
         combine(
                 shadeInteractor.shadeMode,
                 qsSceneAdapter.isCustomizerShowing,
-                sceneBackInteractor.backScene.map { it ?: SceneFamilies.Home },
+                sceneBackInteractor.backScene
+                    .filter { it != Scenes.Shade }
+                    .map { it ?: SceneFamilies.Home },
             ) { shadeMode, isCustomizerShowing, backScene ->
                 buildMap<UserAction, UserActionResult> {
                     if (!isCustomizerShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
index 6764839c..4f595ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
@@ -76,7 +76,7 @@
                         // can be manually toggled on
                         mode.rule.isEnabled -> mode.isActive || mode.rule.isManualInvocationAllowed
                         // Mode was created as disabled, or disabled by the app that owns it ->
-                        // will be shown with a "Set up" text
+                        // will be shown with a "Not set" text
                         !mode.rule.isEnabled -> mode.status == ZenMode.Status.DISABLED_BY_OTHER
                         else -> false
                     }
@@ -120,7 +120,7 @@
                         },
                         onLongClick = { openSettings(mode) },
                         onLongClickLabel =
-                            context.resources.getString(R.string.accessibility_long_click_tile)
+                            context.resources.getString(R.string.accessibility_long_click_tile),
                     )
                 }
             }
@@ -137,10 +137,10 @@
 
     /**
      * Returns a description of the mode, which is:
-     *   * a prompt to set up the mode if it is not enabled
-     *   * if it cannot be manually activated, text that says so
-     *   * otherwise, the trigger description of the mode if it exists...
-     *   * ...or null if it doesn't
+     * * a prompt to set up the mode if it is not enabled
+     * * if it cannot be manually activated, text that says so
+     * * otherwise, the trigger description of the mode if it exists...
+     * * ...or null if it doesn't
      *
      * This description is used directly for the content description of a mode tile for screen
      * readers, and for the tile subtext will be augmented with the current status of the mode.
@@ -174,7 +174,7 @@
                     context,
                     R.style.Theme_SystemUI_Dialog,
                     /* cancelIsNeutral= */ true,
-                    zenDialogMetricsLogger
+                    zenDialogMetricsLogger,
                 )
                 .createDialog()
         SystemUIDialog.applyFlags(dialog)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
index ad25502..7d5a334 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
@@ -148,6 +148,31 @@
         )
     }
 
+    @Test
+    fun migrate3To4_addSpanYColumn_defaultValuePopulated() {
+        val databaseV3 = migrationTestHelper.createDatabase(DATABASE_NAME, version = 3)
+
+        val fakeWidgetsV3 =
+            listOf(
+                FakeCommunalWidgetItemV3(1, "test_widget_1", 11, 0),
+                FakeCommunalWidgetItemV3(2, "test_widget_2", 12, 10),
+                FakeCommunalWidgetItemV3(3, "test_widget_3", 13, 0),
+            )
+        databaseV3.insertWidgetsV3(fakeWidgetsV3)
+
+        databaseV3.verifyWidgetsV3(fakeWidgetsV3)
+
+        val databaseV4 =
+            migrationTestHelper.runMigrationsAndValidate(
+                name = DATABASE_NAME,
+                version = 4,
+                validateDroppedTables = false,
+                CommunalDatabase.MIGRATION_3_4,
+            )
+
+        databaseV4.verifyWidgetsV4(fakeWidgetsV3.map { it.getV4() })
+    }
+
     private fun SupportSQLiteDatabase.insertWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) {
         widgets.forEach { widget ->
             execSQL(
@@ -157,6 +182,22 @@
         }
     }
 
+    private fun SupportSQLiteDatabase.insertWidgetsV3(widgets: List<FakeCommunalWidgetItemV3>) {
+        widgets.forEach { widget ->
+            execSQL(
+                "INSERT INTO communal_widget_table(" +
+                    "widget_id, " +
+                    "component_name, " +
+                    "item_id, " +
+                    "user_serial_number) " +
+                    "VALUES(${widget.widgetId}, " +
+                    "'${widget.componentName}', " +
+                    "${widget.itemId}, " +
+                    "${widget.userSerialNumber})"
+            )
+        }
+    }
+
     private fun SupportSQLiteDatabase.verifyWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) {
         val cursor = query("SELECT * FROM communal_widget_table")
         assertThat(cursor.moveToFirst()).isTrue()
@@ -193,6 +234,42 @@
         assertThat(cursor.isAfterLast).isTrue()
     }
 
+    private fun SupportSQLiteDatabase.verifyWidgetsV3(widgets: List<FakeCommunalWidgetItemV3>) {
+        val cursor = query("SELECT * FROM communal_widget_table")
+        assertThat(cursor.moveToFirst()).isTrue()
+
+        widgets.forEach { widget ->
+            assertThat(cursor.getInt(cursor.getColumnIndex("widget_id"))).isEqualTo(widget.widgetId)
+            assertThat(cursor.getString(cursor.getColumnIndex("component_name")))
+                .isEqualTo(widget.componentName)
+            assertThat(cursor.getInt(cursor.getColumnIndex("item_id"))).isEqualTo(widget.itemId)
+            assertThat(cursor.getInt(cursor.getColumnIndex("user_serial_number")))
+                .isEqualTo(widget.userSerialNumber)
+
+            cursor.moveToNext()
+        }
+        assertThat(cursor.isAfterLast).isTrue()
+    }
+
+    private fun SupportSQLiteDatabase.verifyWidgetsV4(widgets: List<FakeCommunalWidgetItemV4>) {
+        val cursor = query("SELECT * FROM communal_widget_table")
+        assertThat(cursor.moveToFirst()).isTrue()
+
+        widgets.forEach { widget ->
+            assertThat(cursor.getInt(cursor.getColumnIndex("widget_id"))).isEqualTo(widget.widgetId)
+            assertThat(cursor.getString(cursor.getColumnIndex("component_name")))
+                .isEqualTo(widget.componentName)
+            assertThat(cursor.getInt(cursor.getColumnIndex("item_id"))).isEqualTo(widget.itemId)
+            assertThat(cursor.getInt(cursor.getColumnIndex("user_serial_number")))
+                .isEqualTo(widget.userSerialNumber)
+            assertThat(cursor.getInt(cursor.getColumnIndex("span_y"))).isEqualTo(widget.spanY)
+
+            cursor.moveToNext()
+        }
+
+        assertThat(cursor.isAfterLast).isTrue()
+    }
+
     private fun SupportSQLiteDatabase.insertRanks(ranks: List<FakeCommunalItemRank>) {
         ranks.forEach { rank ->
             execSQL("INSERT INTO communal_item_rank_table(rank) VALUES(${rank.rank})")
@@ -238,10 +315,27 @@
         val userSerialNumber: Int,
     )
 
-    private data class FakeCommunalItemRank(
-        val rank: Int,
+    private fun FakeCommunalWidgetItemV3.getV4(): FakeCommunalWidgetItemV4 {
+        return FakeCommunalWidgetItemV4(widgetId, componentName, itemId, userSerialNumber, 3)
+    }
+
+    private data class FakeCommunalWidgetItemV3(
+        val widgetId: Int,
+        val componentName: String,
+        val itemId: Int,
+        val userSerialNumber: Int,
     )
 
+    private data class FakeCommunalWidgetItemV4(
+        val widgetId: Int,
+        val componentName: String,
+        val itemId: Int,
+        val userSerialNumber: Int,
+        val spanY: Int,
+    )
+
+    private data class FakeCommunalItemRank(val rank: Int)
+
     companion object {
         private const val DATABASE_NAME = "communal_db"
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 2f8f45c..0b9c06f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -901,7 +901,7 @@
 
         // 1st time is onStart(), 2nd time is getActiveAutoSwitchNonDdsSubId()
         verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any());
-        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size() == 2);
+        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size()).isEqualTo(2);
 
         // Adds non DDS subId again
         doReturn(SUB_ID2).when(info).getSubscriptionId();
@@ -912,7 +912,7 @@
 
         // Does not add due to cached subInfo in mSubIdTelephonyCallbackMap.
         verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any());
-        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size() == 2);
+        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size()).isEqualTo(2);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 148a2e5..52266ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -29,7 +29,6 @@
 import java.util.UUID
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.kotlin.any
@@ -47,16 +46,11 @@
     private val uiEventLogger = mock<UiEventLogger>()
     private val actionsCallback = mock<ScreenshotActionsController.ActionsCallback>()
 
-    private val request = ScreenshotData.forTesting()
+    private val request = ScreenshotData.forTesting(userHandle = UserHandle.OWNER)
     private val validResult = ScreenshotSavedResult(Uri.EMPTY, Process.myUserHandle(), 0)
 
     private lateinit var actionsProvider: ScreenshotActionsProvider
 
-    @Before
-    fun setUp() {
-        request.userHandle = UserHandle.OWNER
-    }
-
     @Test
     fun previewActionAccessed_beforeScreenshotCompleted_doesNothing() {
         actionsProvider = createActionsProvider()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
index 15da77d..4000d6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -47,7 +47,7 @@
     lateinit var screenshotView: ViewGroup
 
     val userHandle = UserHandle.of(5)
-    val screenshotData = ScreenshotData.forTesting()
+    val screenshotData = ScreenshotData.forTesting(userHandle = userHandle)
 
     val appName = "app name"
     lateinit var workProfileData: WorkProfileMessageController.WorkProfileFirstRunData
@@ -61,7 +61,7 @@
                 workProfileMessageController,
                 profileMessageController,
                 screenshotDetectionController,
-                TestScope(UnconfinedTestDispatcher())
+                TestScope(UnconfinedTestDispatcher()),
             )
         screenshotView = ConstraintLayout(mContext)
         workProfileData = WorkProfileMessageController.WorkProfileFirstRunData(appName, icon)
@@ -83,8 +83,6 @@
         container.addView(detectionNoticeView)
 
         messageContainer.setView(screenshotView)
-
-        screenshotData.userHandle = userHandle
     }
 
     @Test
@@ -92,7 +90,7 @@
         val profileData =
             ProfileMessageController.ProfileFirstRunData(
                 LabeledIcon(appName, icon),
-                ProfileMessageController.FirstRunProfile.PRIVATE
+                ProfileMessageController.FirstRunProfile.PRIVATE,
             )
         whenever(profileMessageController.onScreenshotTaken(eq(userHandle))).thenReturn(profileData)
         messageContainer.onScreenshotTaken(screenshotData)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
index 1538c72..c5070286 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
@@ -61,8 +61,8 @@
 
     @Test
     fun testMaybeNotifyOfScreenshot_ignoresOverview() {
-        val data = ScreenshotData.forTesting()
-        data.source = WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
+        val data =
+            ScreenshotData.forTesting(source = WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW)
 
         val list = controller.maybeNotifyOfScreenshot(data)
 
@@ -72,8 +72,8 @@
 
     @Test
     fun testMaybeNotifyOfScreenshot_emptySet() {
-        val data = ScreenshotData.forTesting()
-        data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+        val data =
+            ScreenshotData.forTesting(source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD)
 
         whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
             .thenReturn(listOf())
@@ -85,8 +85,8 @@
 
     @Test
     fun testMaybeNotifyOfScreenshot_oneApp() {
-        val data = ScreenshotData.forTesting()
-        data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+        val data =
+            ScreenshotData.forTesting(source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD)
 
         val component = ComponentName("package1", "class1")
         val appName = "app name"
@@ -95,7 +95,7 @@
         whenever(
                 packageManager.getActivityInfo(
                     eq(component),
-                    any(PackageManager.ComponentInfoFlags::class.java)
+                    any(PackageManager.ComponentInfoFlags::class.java),
                 )
             )
             .thenReturn(activityInfo)
@@ -112,8 +112,8 @@
 
     @Test
     fun testMaybeNotifyOfScreenshot_multipleApps() {
-        val data = ScreenshotData.forTesting()
-        data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+        val data =
+            ScreenshotData.forTesting(source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD)
 
         val component1 = ComponentName("package1", "class1")
         val component2 = ComponentName("package2", "class2")
@@ -129,21 +129,21 @@
         whenever(
                 packageManager.getActivityInfo(
                     eq(component1),
-                    any(PackageManager.ComponentInfoFlags::class.java)
+                    any(PackageManager.ComponentInfoFlags::class.java),
                 )
             )
             .thenReturn(activityInfo1)
         whenever(
                 packageManager.getActivityInfo(
                     eq(component2),
-                    any(PackageManager.ComponentInfoFlags::class.java)
+                    any(PackageManager.ComponentInfoFlags::class.java),
                 )
             )
             .thenReturn(activityInfo2)
         whenever(
                 packageManager.getActivityInfo(
                     eq(component3),
-                    any(PackageManager.ComponentInfoFlags::class.java)
+                    any(PackageManager.ComponentInfoFlags::class.java),
                 )
             )
             .thenReturn(activityInfo3)
@@ -165,11 +165,13 @@
 
     private fun includesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
         ComponentInfoFlagMatcher(mask, mask)
+
     private fun excludesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
         ComponentInfoFlagMatcher(mask, 0)
 
     private class ComponentInfoFlagMatcher(
-        @PackageManager.ComponentInfoFlagsBits val mask: Int, val value: Int
+        @PackageManager.ComponentInfoFlagsBits val mask: Int,
+        val value: Int,
     ) : ArgumentMatcher<PackageManager.ComponentInfoFlags> {
         override fun matches(flags: PackageManager.ComponentInfoFlags?): Boolean {
             return flags != null && (mask.toLong() and flags.value) == value.toLong()
@@ -182,26 +184,28 @@
 
     @Test
     fun testMaybeNotifyOfScreenshot_disabledApp() {
-        val data = ScreenshotData.forTesting()
-        data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+        val data =
+            ScreenshotData.forTesting(source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD)
 
         val component = ComponentName("package1", "class1")
         val appName = "app name"
         val activityInfo = mock(ActivityInfo::class.java)
 
         whenever(
-            packageManager.getActivityInfo(
-                eq(component),
-                argThat(includesFlagBits(MATCH_DISABLED_COMPONENTS or MATCH_ANY_USER))
+                packageManager.getActivityInfo(
+                    eq(component),
+                    argThat(includesFlagBits(MATCH_DISABLED_COMPONENTS or MATCH_ANY_USER)),
+                )
             )
-        ).thenReturn(activityInfo)
+            .thenReturn(activityInfo)
 
         whenever(
-            packageManager.getActivityInfo(
-                eq(component),
-                argThat(excludesFlagBits(MATCH_DISABLED_COMPONENTS))
+                packageManager.getActivityInfo(
+                    eq(component),
+                    argThat(excludesFlagBits(MATCH_DISABLED_COMPONENTS)),
+                )
             )
-        ).thenThrow(PackageManager.NameNotFoundException::class.java)
+            .thenThrow(PackageManager.NameNotFoundException::class.java)
 
         whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
             .thenReturn(listOf(component))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index 15705fb..0bea560 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -257,6 +257,58 @@
         }
 
     @Test
+    @EnableFlags(Flags.FLAG_SCREENSHOT_MULTIDISPLAY_FOCUS_CHANGE)
+    fun executeScreenshots_fromOverview_honorsDisplay() =
+        testScope.runTest {
+            val displayId = 1
+            setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = displayId))
+            val onSaved = { _: Uri? -> }
+            screenshotExecutor.executeScreenshots(
+                createScreenshotRequest(
+                    displayId = displayId,
+                    source = WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW,
+                ),
+                onSaved,
+                callback,
+            )
+
+            val dataCaptor = ArgumentCaptor<ScreenshotData>()
+
+            verify(controller).handleScreenshot(dataCaptor.capture(), any(), any())
+
+            assertThat(dataCaptor.value.displayId).isEqualTo(displayId)
+
+            screenshotExecutor.onDestroy()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SCREENSHOT_MULTIDISPLAY_FOCUS_CHANGE)
+    fun executeScreenshots_fromOverviewInvalidDisplay_usesDefault() =
+        testScope.runTest {
+            setDisplays(
+                display(TYPE_INTERNAL, id = Display.DEFAULT_DISPLAY),
+                display(TYPE_EXTERNAL, id = 1),
+            )
+            val onSaved = { _: Uri? -> }
+            screenshotExecutor.executeScreenshots(
+                createScreenshotRequest(
+                    displayId = 5,
+                    source = WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW,
+                ),
+                onSaved,
+                callback,
+            )
+
+            val dataCaptor = ArgumentCaptor<ScreenshotData>()
+
+            verify(controller).handleScreenshot(dataCaptor.capture(), any(), any())
+
+            assertThat(dataCaptor.value.displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+            screenshotExecutor.onDestroy()
+        }
+
+    @Test
     fun onDestroy_propagatedToControllers() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -527,9 +579,14 @@
         runCurrent()
     }
 
-    private fun createScreenshotRequest(type: Int = WindowManager.TAKE_SCREENSHOT_FULLSCREEN) =
-        ScreenshotRequest.Builder(type, WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER)
+    private fun createScreenshotRequest(
+        type: Int = WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+        source: Int = WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER,
+        displayId: Int = Display.DEFAULT_DISPLAY,
+    ) =
+        ScreenshotRequest.Builder(type, source)
             .setTopComponent(topComponent)
+            .setDisplayId(displayId)
             .also {
                 if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
                     it.setBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888))
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 5d7e7c7..1302faa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -31,7 +31,7 @@
         provider: ComponentName,
         user: UserHandle,
         rank: Int?,
-        configurator: WidgetConfigurator?
+        configurator: WidgetConfigurator?,
     ) {
         coroutineScope.launch {
             val id = nextWidgetId++
@@ -93,6 +93,22 @@
         _communalWidgets.value = fakeDatabase.values.toList()
     }
 
+    override fun updateWidgetSpanY(widgetId: Int, spanY: Int) {
+        coroutineScope.launch {
+            fakeDatabase[widgetId]?.let { widget ->
+                when (widget) {
+                    is CommunalWidgetContentModel.Available -> {
+                        fakeDatabase[widgetId] = widget.copy(spanY = spanY)
+                    }
+                    is CommunalWidgetContentModel.Pending -> {
+                        fakeDatabase[widgetId] = widget.copy(spanY = spanY)
+                    }
+                }
+                _communalWidgets.value = fakeDatabase.values.toList()
+            }
+        }
+    }
+
     override fun restoreWidgets(oldToNewWidgetIdMap: Map<Int, Int>) {}
 
     override fun abortRestoreWidgets() {}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
index f26bb83..805a710 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
@@ -35,6 +35,8 @@
     private val _revealAmount: MutableStateFlow<Float> = MutableStateFlow(0.0f)
     override val revealAmount: Flow<Float> = _revealAmount
 
+    val revealAnimatorRequests: MutableList<RevealAnimatorRequest> = arrayListOf()
+
     override val isAnimating: Boolean
         get() = false
 
@@ -44,5 +46,12 @@
         } else {
             _revealAmount.value = 0.0f
         }
+
+        revealAnimatorRequests.add(RevealAnimatorRequest(reveal, duration))
     }
+
+    data class RevealAnimatorRequest(
+        val reveal: Boolean,
+        val duration: Long
+    )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index f842db4..020f0a6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -8,17 +8,14 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.interactor.systemGestureExclusionInteractor
 import com.android.systemui.scene.shared.logger.sceneLogger
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.FakeOverlay
-import com.android.systemui.scene.ui.viewmodel.SceneContainerGestureFilter
 import com.android.systemui.scene.ui.viewmodel.SceneContainerHapticsViewModel
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
 import com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
-import com.android.systemui.settings.displayTracker
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import kotlinx.coroutines.flow.MutableStateFlow
 import org.mockito.kotlin.mock
@@ -72,16 +69,15 @@
 }
 
 val Kosmos.sceneContainerViewModel by Fixture {
-    sceneContainerViewModelFactory.create(mock<View>(), displayTracker.defaultDisplayId, {}).apply {
-        setTransitionState(transitionState)
-    }
+    sceneContainerViewModelFactory
+        .create(mock<View>()) {}
+        .apply { setTransitionState(transitionState) }
 }
 
 val Kosmos.sceneContainerViewModelFactory by Fixture {
     object : SceneContainerViewModel.Factory {
         override fun create(
             view: View,
-            displayId: Int,
             motionEventHandlerReceiver: (SceneContainerViewModel.MotionEventHandler?) -> Unit,
         ): SceneContainerViewModel =
             SceneContainerViewModel(
@@ -91,26 +87,13 @@
                 shadeInteractor = shadeInteractor,
                 splitEdgeDetector = splitEdgeDetector,
                 logger = sceneLogger,
-                gestureFilterFactory = sceneContainerGestureFilterFactory,
                 hapticsViewModelFactory = sceneContainerHapticsViewModelFactory,
                 view = view,
-                displayId = displayId,
                 motionEventHandlerReceiver = motionEventHandlerReceiver,
             )
     }
 }
 
-val Kosmos.sceneContainerGestureFilterFactory by Fixture {
-    object : SceneContainerGestureFilter.Factory {
-        override fun create(displayId: Int): SceneContainerGestureFilter {
-            return SceneContainerGestureFilter(
-                interactor = systemGestureExclusionInteractor,
-                displayId = displayId,
-            )
-        }
-    }
-}
-
 val Kosmos.sceneContainerHapticsViewModelFactory by Fixture {
     object : SceneContainerHapticsViewModel.Factory {
         override fun create(view: View): SceneContainerHapticsViewModel {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepositoryKosmos.kt
deleted file mode 100644
index 15ed1b3..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepositoryKosmos.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.data.repository
-
-import android.view.windowManagerService
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-
-val Kosmos.systemGestureExclusionRepository by Fixture {
-    SystemGestureExclusionRepository(windowManager = windowManagerService)
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractorKosmos.kt
deleted file mode 100644
index 3e46c3f..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractorKosmos.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.domain.interactor
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.scene.data.repository.systemGestureExclusionRepository
-
-val Kosmos.systemGestureExclusionInteractor by Fixture {
-    SystemGestureExclusionInteractor(repository = systemGestureExclusionRepository)
-}
diff --git a/packages/SystemUI/frp/Android.bp b/packages/SystemUI/utils/kairos/Android.bp
similarity index 88%
rename from packages/SystemUI/frp/Android.bp
rename to packages/SystemUI/utils/kairos/Android.bp
index c3381db..1442591 100644
--- a/packages/SystemUI/frp/Android.bp
+++ b/packages/SystemUI/utils/kairos/Android.bp
@@ -20,9 +20,9 @@
 }
 
 java_library {
-    name: "kt-frp",
+    name: "kairos",
     host_supported: true,
-    kotlincflags: ["-opt-in=com.android.systemui.experimental.frp.ExperimentalFrpApi"],
+    kotlincflags: ["-opt-in=com.android.systemui.kairos.ExperimentalFrpApi"],
     srcs: ["src/**/*.kt"],
     static_libs: [
         "kotlin-stdlib",
@@ -31,7 +31,7 @@
 }
 
 java_test {
-    name: "kt-frp-test",
+    name: "kairos-test",
     optimize: {
         enabled: false,
     },
@@ -39,7 +39,7 @@
         "test/**/*.kt",
     ],
     static_libs: [
-        "kt-frp",
+        "kairos",
         "junit",
         "kotlin-stdlib",
         "kotlin-test",
diff --git a/packages/SystemUI/frp/OWNERS b/packages/SystemUI/utils/kairos/OWNERS
similarity index 100%
rename from packages/SystemUI/frp/OWNERS
rename to packages/SystemUI/utils/kairos/OWNERS
diff --git a/packages/SystemUI/frp/README.md b/packages/SystemUI/utils/kairos/README.md
similarity index 89%
rename from packages/SystemUI/frp/README.md
rename to packages/SystemUI/utils/kairos/README.md
index 9c5bdb0..85f622c 100644
--- a/packages/SystemUI/frp/README.md
+++ b/packages/SystemUI/utils/kairos/README.md
@@ -1,4 +1,4 @@
-# kt-frp
+# Kairos
 
 A functional reactive programming (FRP) library for Kotlin.
 
@@ -13,12 +13,12 @@
 
 ### Details for nerds
 
-`kt-frp` implements an applicative / monadic flavor of FRP, using a push-pull
+`Kairos` implements an applicative / monadic flavor of FRP, using a push-pull
 methodology to allow for efficient updates.
 
 "Real" functional reactive programming should be specified with denotational
 semantics ([wikipedia](https://en.wikipedia.org/wiki/Denotational_semantics)):
-you can view the semantics for `kt-frp` [here](docs/semantics.md).
+you can view the semantics for `Kairos` [here](docs/semantics.md).
 
 ## Usage
 
@@ -61,4 +61,4 @@
 
 ## Resources
 
-- [Cheatsheet for those coming from Kotlin Flow](docs/flow-to-frp-cheatsheet.md)
+- [Cheatsheet for those coming from Kotlin Flow](docs/flow-to-kairos-cheatsheet.md)
diff --git a/packages/SystemUI/frp/docs/flow-to-frp-cheatsheet.md b/packages/SystemUI/utils/kairos/docs/flow-to-kairos-cheatsheet.md
similarity index 90%
rename from packages/SystemUI/frp/docs/flow-to-frp-cheatsheet.md
rename to packages/SystemUI/utils/kairos/docs/flow-to-kairos-cheatsheet.md
index e20f3e6..9f7fd02 100644
--- a/packages/SystemUI/frp/docs/flow-to-frp-cheatsheet.md
+++ b/packages/SystemUI/utils/kairos/docs/flow-to-kairos-cheatsheet.md
@@ -1,27 +1,27 @@
-# From Flows to FRP
+# From Flows to Kairos
 
 ## Key differences
 
-* FRP evaluates all events (`TFlow` emissions + observers) in a transaction.
+* Kairos evaluates all events (`TFlow` emissions + observers) in a transaction.
 
-* FRP splits `Flow` APIs into two distinct types: `TFlow` and `TState`
+* Kairos splits `Flow` APIs into two distinct types: `TFlow` and `TState`
 
     * `TFlow` is roughly equivalent to `SharedFlow` w/ a replay cache that
-      exists for the duration of the current FRP transaction and shared with
+      exists for the duration of the current Kairos transaction and shared with
       `SharingStarted.WhileSubscribed()`
 
     * `TState` is roughly equivalent to `StateFlow` shared with
       `SharingStarted.Eagerly`, but the current value can only be queried within
-      a FRP transaction, and the value is only updated at the end of the
+      a Kairos transaction, and the value is only updated at the end of the
       transaction
 
-* FRP further divides `Flow` APIs based on how they internally use state:
+* Kairos further divides `Flow` APIs based on how they internally use state:
 
   * **FrpTransactionScope:** APIs that internally query some state need to be
-    performed within an FRP transaction
+    performed within an Kairos transaction
 
     * this scope is available from the other scopes, and from most lambdas
-      passed to other FRP APIs
+      passed to other Kairos APIs
 
   * **FrpStateScope:** APIs that internally accumulate state in reaction to
     events need to be performed within an FRP State scope (akin to a
@@ -126,8 +126,8 @@
 developers generally append `.onStart { emit(initialValue) }` to the `Flows`
 that don't immediately emit.
 
-FRP avoids this gotcha by forcing usage of `TState` for `combine`, thus ensuring
-that there is always a current value to be combined for each input.
+Kairos avoids this gotcha by forcing usage of `TState` for `combine`, thus
+ensuring that there is always a current value to be combined for each input.
 
 ## collect { … }
 
@@ -158,8 +158,8 @@
 #### Explanation
 
 To keep all state-reads consistent, the current value of a TState can only be
-queried within an FRP transaction, modeled with `FrpTransactionScope`. Note that
-both `FrpStateScope` and `FrpBuildScope` extend `FrpTransactionScope`.
+queried within a Kairos transaction, modeled with `FrpTransactionScope`. Note
+that both `FrpStateScope` and `FrpBuildScope` extend `FrpTransactionScope`.
 
 ### I want to sample a TFlow
 
@@ -198,7 +198,7 @@
 
 #### Explanation
 
-Under FRP's rules, a `TFlow` may only emit up to once per transaction. This
+Under Kairos's rules, a `TFlow` may only emit up to once per transaction. This
 means that if we are merging two or more `TFlows` that are emitting at the same
 time (within the same transaction), the resulting merged `TFlow` must emit a
 single value. The lambda argument allows the developer to decide what to do in
diff --git a/packages/SystemUI/frp/docs/semantics.md b/packages/SystemUI/utils/kairos/docs/semantics.md
similarity index 96%
rename from packages/SystemUI/frp/docs/semantics.md
rename to packages/SystemUI/utils/kairos/docs/semantics.md
index b533190..d43bb44 100644
--- a/packages/SystemUI/frp/docs/semantics.md
+++ b/packages/SystemUI/utils/kairos/docs/semantics.md
@@ -1,11 +1,11 @@
 # FRP Semantics
 
-`kt-frp`'s pure API is based off of the following denotational semantics
+`Kairos`'s pure API is based off of the following denotational semantics
 ([wikipedia](https://en.wikipedia.org/wiki/Denotational_semantics)).
 
-The semantics model `kt-frp` types as time-varying values; by making `Time` a
+The semantics model `Kairos` types as time-varying values; by making `Time` a
 first-class value, we can define a referentially-transparent API that allows us
-to reason about the behavior of the pure FRP combinators. This is
+to reason about the behavior of the pure `Kairos` combinators. This is
 implementation-agnostic; we can compare the behavior of any implementation with
 expected behavior denoted by these semantics to identify bugs.
 
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/Combinators.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/Combinators.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
index 298c071..8bf3a43 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/Combinators.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.util.These
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.none
+import com.android.systemui.kairos.util.These
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.none
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.conflate
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpBuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
similarity index 98%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpBuildScope.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
index 6e4c9eb..4de6deb 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpBuildScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
@@ -16,11 +16,11 @@
 
 @file:OptIn(ExperimentalCoroutinesApi::class)
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.map
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.map
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.coroutines.RestrictsSuspension
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpEffectScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpEffectScope.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt
index a8ec98f..be2eb43 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpEffectScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
 import kotlin.coroutines.RestrictsSuspension
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpNetwork.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
similarity index 94%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpNetwork.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
index acc76d9..b688eaf 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpNetwork.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.internal.BuildScopeImpl
-import com.android.systemui.experimental.frp.internal.Network
-import com.android.systemui.experimental.frp.internal.StateScopeImpl
-import com.android.systemui.experimental.frp.internal.util.awaitCancellationAndThen
-import com.android.systemui.experimental.frp.internal.util.childScope
+import com.android.systemui.kairos.internal.BuildScopeImpl
+import com.android.systemui.kairos.internal.Network
+import com.android.systemui.kairos.internal.StateScopeImpl
+import com.android.systemui.kairos.internal.util.awaitCancellationAndThen
+import com.android.systemui.kairos.internal.util.childScope
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.coroutines.coroutineContext
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpScope.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpScope.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpScope.kt
index a5a7977..ad6b2c8 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpScope.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
 import kotlin.coroutines.RestrictsSuspension
 import kotlin.coroutines.resume
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpStateScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpStateScope.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpStateScope.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpStateScope.kt
index 61336f4..c7ea680 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpStateScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpStateScope.kt
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.combine as combinePure
-import com.android.systemui.experimental.frp.map as mapPure
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Left
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.Right
-import com.android.systemui.experimental.frp.util.WithPrev
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.map
-import com.android.systemui.experimental.frp.util.none
-import com.android.systemui.experimental.frp.util.partitionEithers
-import com.android.systemui.experimental.frp.util.zipWith
+import com.android.systemui.kairos.combine as combinePure
+import com.android.systemui.kairos.map as mapPure
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.Right
+import com.android.systemui.kairos.util.WithPrev
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.map
+import com.android.systemui.kairos.util.none
+import com.android.systemui.kairos.util.partitionEithers
+import com.android.systemui.kairos.util.zipWith
 import kotlin.coroutines.RestrictsSuspension
 
 typealias FrpStateful<R> = suspend FrpStateScope.() -> R
@@ -715,7 +715,7 @@
         stateB: TState<B>,
         transform: suspend FrpTransactionScope.(A, B) -> Z,
     ): TState<Z> =
-        com.android.systemui.experimental.frp
+        com.android.systemui.kairos
             .combine(stateA, stateB) { a, b -> transactionally { transform(a, b) } }
             .sampleTransactionals()
 
@@ -733,7 +733,7 @@
         stateD: TState<D>,
         transform: suspend FrpTransactionScope.(A, B, C, D) -> Z,
     ): TState<Z> =
-        com.android.systemui.experimental.frp
+        com.android.systemui.kairos
             .combine(stateA, stateB, stateC, stateD) { a, b, c, d ->
                 transactionally { transform(a, b, c, d) }
             }
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpTransactionScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpTransactionScope.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpTransactionScope.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpTransactionScope.kt
index b0b9dbc..a7ae1d9 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/FrpTransactionScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpTransactionScope.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
 import kotlin.coroutines.RestrictsSuspension
 
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/TFlow.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
similarity index 90%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/TFlow.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
index cca6c9a..7ba1aca 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/TFlow.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
@@ -14,36 +14,36 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.internal.DemuxImpl
-import com.android.systemui.experimental.frp.internal.Init
-import com.android.systemui.experimental.frp.internal.InitScope
-import com.android.systemui.experimental.frp.internal.InputNode
-import com.android.systemui.experimental.frp.internal.Network
-import com.android.systemui.experimental.frp.internal.NoScope
-import com.android.systemui.experimental.frp.internal.TFlowImpl
-import com.android.systemui.experimental.frp.internal.activated
-import com.android.systemui.experimental.frp.internal.cached
-import com.android.systemui.experimental.frp.internal.constInit
-import com.android.systemui.experimental.frp.internal.filterNode
-import com.android.systemui.experimental.frp.internal.init
-import com.android.systemui.experimental.frp.internal.map
-import com.android.systemui.experimental.frp.internal.mapImpl
-import com.android.systemui.experimental.frp.internal.mapMaybeNode
-import com.android.systemui.experimental.frp.internal.mergeNodes
-import com.android.systemui.experimental.frp.internal.mergeNodesLeft
-import com.android.systemui.experimental.frp.internal.neverImpl
-import com.android.systemui.experimental.frp.internal.switchDeferredImplSingle
-import com.android.systemui.experimental.frp.internal.switchPromptImpl
-import com.android.systemui.experimental.frp.internal.util.hashString
-import com.android.systemui.experimental.frp.util.Either
-import com.android.systemui.experimental.frp.util.Left
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.Right
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.map
-import com.android.systemui.experimental.frp.util.toMaybe
+import com.android.systemui.kairos.internal.DemuxImpl
+import com.android.systemui.kairos.internal.Init
+import com.android.systemui.kairos.internal.InitScope
+import com.android.systemui.kairos.internal.InputNode
+import com.android.systemui.kairos.internal.Network
+import com.android.systemui.kairos.internal.NoScope
+import com.android.systemui.kairos.internal.TFlowImpl
+import com.android.systemui.kairos.internal.activated
+import com.android.systemui.kairos.internal.cached
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.filterNode
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.map
+import com.android.systemui.kairos.internal.mapImpl
+import com.android.systemui.kairos.internal.mapMaybeNode
+import com.android.systemui.kairos.internal.mergeNodes
+import com.android.systemui.kairos.internal.mergeNodesLeft
+import com.android.systemui.kairos.internal.neverImpl
+import com.android.systemui.kairos.internal.switchDeferredImplSingle
+import com.android.systemui.kairos.internal.switchPromptImpl
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.util.Either
+import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.Right
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.map
+import com.android.systemui.kairos.util.toMaybe
 import java.util.concurrent.atomic.AtomicReference
 import kotlin.reflect.KProperty
 import kotlinx.coroutines.CompletableDeferred
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/TState.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
similarity index 91%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/TState.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
index a5ec503..a4c6956 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/TState.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
@@ -14,29 +14,29 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.internal.DerivedMapCheap
-import com.android.systemui.experimental.frp.internal.Init
-import com.android.systemui.experimental.frp.internal.InitScope
-import com.android.systemui.experimental.frp.internal.Network
-import com.android.systemui.experimental.frp.internal.NoScope
-import com.android.systemui.experimental.frp.internal.Schedulable
-import com.android.systemui.experimental.frp.internal.TFlowImpl
-import com.android.systemui.experimental.frp.internal.TStateImpl
-import com.android.systemui.experimental.frp.internal.TStateSource
-import com.android.systemui.experimental.frp.internal.activated
-import com.android.systemui.experimental.frp.internal.cached
-import com.android.systemui.experimental.frp.internal.constInit
-import com.android.systemui.experimental.frp.internal.constS
-import com.android.systemui.experimental.frp.internal.filterNode
-import com.android.systemui.experimental.frp.internal.flatMap
-import com.android.systemui.experimental.frp.internal.init
-import com.android.systemui.experimental.frp.internal.map
-import com.android.systemui.experimental.frp.internal.mapCheap
-import com.android.systemui.experimental.frp.internal.mapImpl
-import com.android.systemui.experimental.frp.internal.util.hashString
-import com.android.systemui.experimental.frp.internal.zipStates
+import com.android.systemui.kairos.internal.DerivedMapCheap
+import com.android.systemui.kairos.internal.Init
+import com.android.systemui.kairos.internal.InitScope
+import com.android.systemui.kairos.internal.Network
+import com.android.systemui.kairos.internal.NoScope
+import com.android.systemui.kairos.internal.Schedulable
+import com.android.systemui.kairos.internal.TFlowImpl
+import com.android.systemui.kairos.internal.TStateImpl
+import com.android.systemui.kairos.internal.TStateSource
+import com.android.systemui.kairos.internal.activated
+import com.android.systemui.kairos.internal.cached
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.constS
+import com.android.systemui.kairos.internal.filterNode
+import com.android.systemui.kairos.internal.flatMap
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.map
+import com.android.systemui.kairos.internal.mapCheap
+import com.android.systemui.kairos.internal.mapImpl
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.internal.zipStates
 import kotlin.reflect.KProperty
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/Transactional.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
similarity index 85%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/Transactional.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
index 0e7b420..6b1c8c8 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/Transactional.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.internal.InitScope
-import com.android.systemui.experimental.frp.internal.NoScope
-import com.android.systemui.experimental.frp.internal.TransactionalImpl
-import com.android.systemui.experimental.frp.internal.init
-import com.android.systemui.experimental.frp.internal.transactionalImpl
-import com.android.systemui.experimental.frp.internal.util.hashString
+import com.android.systemui.kairos.internal.InitScope
+import com.android.systemui.kairos.internal.NoScope
+import com.android.systemui.kairos.internal.TransactionalImpl
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.transactionalImpl
+import com.android.systemui.kairos.internal.util.hashString
 import kotlinx.coroutines.CompletableDeferred
 
 /**
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/debug/Debug.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt
similarity index 83%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/debug/Debug.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt
index 8062341..4f302a1 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/debug/Debug.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt
@@ -14,25 +14,25 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.debug
+package com.android.systemui.kairos.debug
 
-import com.android.systemui.experimental.frp.MutableTState
-import com.android.systemui.experimental.frp.TState
-import com.android.systemui.experimental.frp.TStateInit
-import com.android.systemui.experimental.frp.TStateLoop
-import com.android.systemui.experimental.frp.internal.DerivedFlatten
-import com.android.systemui.experimental.frp.internal.DerivedMap
-import com.android.systemui.experimental.frp.internal.DerivedMapCheap
-import com.android.systemui.experimental.frp.internal.DerivedZipped
-import com.android.systemui.experimental.frp.internal.Init
-import com.android.systemui.experimental.frp.internal.TStateDerived
-import com.android.systemui.experimental.frp.internal.TStateImpl
-import com.android.systemui.experimental.frp.internal.TStateSource
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.None
-import com.android.systemui.experimental.frp.util.none
-import com.android.systemui.experimental.frp.util.orElseGet
+import com.android.systemui.kairos.MutableTState
+import com.android.systemui.kairos.TState
+import com.android.systemui.kairos.TStateInit
+import com.android.systemui.kairos.TStateLoop
+import com.android.systemui.kairos.internal.DerivedFlatten
+import com.android.systemui.kairos.internal.DerivedMap
+import com.android.systemui.kairos.internal.DerivedMapCheap
+import com.android.systemui.kairos.internal.DerivedZipped
+import com.android.systemui.kairos.internal.Init
+import com.android.systemui.kairos.internal.TStateDerived
+import com.android.systemui.kairos.internal.TStateImpl
+import com.android.systemui.kairos.internal.TStateSource
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.None
+import com.android.systemui.kairos.util.none
+import com.android.systemui.kairos.util.orElseGet
 
 // object IdGen {
 //    private val counter = AtomicLong()
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/BuildScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
similarity index 89%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/BuildScopeImpl.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
index 127abd8..90f1aea 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/BuildScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
@@ -14,34 +14,34 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.CoalescingMutableTFlow
-import com.android.systemui.experimental.frp.FrpBuildScope
-import com.android.systemui.experimental.frp.FrpCoalescingProducerScope
-import com.android.systemui.experimental.frp.FrpDeferredValue
-import com.android.systemui.experimental.frp.FrpEffectScope
-import com.android.systemui.experimental.frp.FrpNetwork
-import com.android.systemui.experimental.frp.FrpProducerScope
-import com.android.systemui.experimental.frp.FrpSpec
-import com.android.systemui.experimental.frp.FrpStateScope
-import com.android.systemui.experimental.frp.FrpTransactionScope
-import com.android.systemui.experimental.frp.GroupedTFlow
-import com.android.systemui.experimental.frp.LocalFrpNetwork
-import com.android.systemui.experimental.frp.MutableTFlow
-import com.android.systemui.experimental.frp.TFlow
-import com.android.systemui.experimental.frp.TFlowInit
-import com.android.systemui.experimental.frp.groupByKey
-import com.android.systemui.experimental.frp.init
-import com.android.systemui.experimental.frp.internal.util.childScope
-import com.android.systemui.experimental.frp.internal.util.launchOnCancel
-import com.android.systemui.experimental.frp.internal.util.mapValuesParallel
-import com.android.systemui.experimental.frp.launchEffect
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.None
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.map
+import com.android.systemui.kairos.CoalescingMutableTFlow
+import com.android.systemui.kairos.FrpBuildScope
+import com.android.systemui.kairos.FrpCoalescingProducerScope
+import com.android.systemui.kairos.FrpDeferredValue
+import com.android.systemui.kairos.FrpEffectScope
+import com.android.systemui.kairos.FrpNetwork
+import com.android.systemui.kairos.FrpProducerScope
+import com.android.systemui.kairos.FrpSpec
+import com.android.systemui.kairos.FrpStateScope
+import com.android.systemui.kairos.FrpTransactionScope
+import com.android.systemui.kairos.GroupedTFlow
+import com.android.systemui.kairos.LocalFrpNetwork
+import com.android.systemui.kairos.MutableTFlow
+import com.android.systemui.kairos.TFlow
+import com.android.systemui.kairos.TFlowInit
+import com.android.systemui.kairos.groupByKey
+import com.android.systemui.kairos.init
+import com.android.systemui.kairos.internal.util.childScope
+import com.android.systemui.kairos.internal.util.launchOnCancel
+import com.android.systemui.kairos.internal.util.mapValuesParallel
+import com.android.systemui.kairos.launchEffect
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.None
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.map
 import java.util.concurrent.atomic.AtomicReference
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/DeferScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/DeferScope.kt
similarity index 88%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/DeferScope.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/DeferScope.kt
index f72ba5f..f65307c 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/DeferScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/DeferScope.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.asyncImmediate
-import com.android.systemui.experimental.frp.internal.util.launchImmediate
+import com.android.systemui.kairos.internal.util.asyncImmediate
+import com.android.systemui.kairos.internal.util.launchImmediate
 import kotlinx.coroutines.CoroutineName
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Demux.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt
similarity index 96%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Demux.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt
index 418220f..e7b9952 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Demux.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt
@@ -16,13 +16,13 @@
 
 @file:Suppress("NOTHING_TO_INLINE")
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.hashString
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.flatMap
-import com.android.systemui.experimental.frp.util.getMaybe
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.flatMap
+import com.android.systemui.kairos.util.getMaybe
 import java.util.concurrent.ConcurrentHashMap
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.async
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/EvalScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
similarity index 84%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/EvalScopeImpl.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
index 38bc22f..815473f 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/EvalScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.FrpDeferredValue
-import com.android.systemui.experimental.frp.FrpTransactionScope
-import com.android.systemui.experimental.frp.TFlow
-import com.android.systemui.experimental.frp.TFlowInit
-import com.android.systemui.experimental.frp.TFlowLoop
-import com.android.systemui.experimental.frp.TState
-import com.android.systemui.experimental.frp.TStateInit
-import com.android.systemui.experimental.frp.Transactional
-import com.android.systemui.experimental.frp.emptyTFlow
-import com.android.systemui.experimental.frp.init
-import com.android.systemui.experimental.frp.mapCheap
-import com.android.systemui.experimental.frp.switch
+import com.android.systemui.kairos.FrpDeferredValue
+import com.android.systemui.kairos.FrpTransactionScope
+import com.android.systemui.kairos.TFlow
+import com.android.systemui.kairos.TFlowInit
+import com.android.systemui.kairos.TFlowLoop
+import com.android.systemui.kairos.TState
+import com.android.systemui.kairos.TStateInit
+import com.android.systemui.kairos.Transactional
+import com.android.systemui.kairos.emptyTFlow
+import com.android.systemui.kairos.init
+import com.android.systemui.kairos.mapCheap
+import com.android.systemui.kairos.switch
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/FilterNode.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/FilterNode.kt
similarity index 83%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/FilterNode.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/FilterNode.kt
index 4f2a769..bc06a36 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/FilterNode.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/FilterNode.kt
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.none
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.none
 
 internal inline fun <A, B> mapMaybeNode(
     crossinline getPulse: suspend EvalScope.() -> TFlowImpl<A>,
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Graph.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
similarity index 99%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Graph.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
index 9425870..3aec319 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Graph.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.Bag
+import com.android.systemui.kairos.internal.util.Bag
 import java.util.TreeMap
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Init.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Init.kt
similarity index 89%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Init.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Init.kt
index efb7a09..57db9a4 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Init.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Init.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.none
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.none
 import java.util.concurrent.atomic.AtomicBoolean
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Inputs.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt
similarity index 94%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Inputs.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt
index 85c87fe..8efaf79 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Inputs.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.Key
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.just
+import com.android.systemui.kairos.internal.util.Key
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.just
 import java.util.concurrent.atomic.AtomicBoolean
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.sync.Mutex
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/InternalScopes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
similarity index 81%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/InternalScopes.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
index b6cd906..af864e6 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/InternalScopes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.FrpBuildScope
-import com.android.systemui.experimental.frp.FrpStateScope
-import com.android.systemui.experimental.frp.FrpTransactionScope
-import com.android.systemui.experimental.frp.TFlow
-import com.android.systemui.experimental.frp.internal.util.HeteroMap
-import com.android.systemui.experimental.frp.internal.util.Key
-import com.android.systemui.experimental.frp.util.Maybe
+import com.android.systemui.kairos.FrpBuildScope
+import com.android.systemui.kairos.FrpStateScope
+import com.android.systemui.kairos.FrpTransactionScope
+import com.android.systemui.kairos.TFlow
+import com.android.systemui.kairos.internal.util.HeteroMap
+import com.android.systemui.kairos.internal.util.Key
+import com.android.systemui.kairos.util.Maybe
 
 internal interface InitScope {
     val networkId: Any
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Mux.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Mux.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
index e616d62..f7ff15f 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Mux.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
@@ -16,11 +16,11 @@
 
 @file:Suppress("NOTHING_TO_INLINE")
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.ConcurrentNullableHashMap
-import com.android.systemui.experimental.frp.internal.util.hashString
-import com.android.systemui.experimental.frp.util.Just
+import com.android.systemui.kairos.internal.util.ConcurrentNullableHashMap
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.util.Just
 import java.util.concurrent.ConcurrentHashMap
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.sync.Mutex
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/MuxDeferred.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
similarity index 93%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/MuxDeferred.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
index 6d43285..08bee85 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/MuxDeferred.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
@@ -14,28 +14,28 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.Key
-import com.android.systemui.experimental.frp.internal.util.associateByIndexTo
-import com.android.systemui.experimental.frp.internal.util.hashString
-import com.android.systemui.experimental.frp.internal.util.mapParallel
-import com.android.systemui.experimental.frp.internal.util.mapValuesNotNullParallelTo
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Left
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.None
-import com.android.systemui.experimental.frp.util.Right
-import com.android.systemui.experimental.frp.util.These
-import com.android.systemui.experimental.frp.util.flatMap
-import com.android.systemui.experimental.frp.util.getMaybe
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.maybeThat
-import com.android.systemui.experimental.frp.util.maybeThis
-import com.android.systemui.experimental.frp.util.merge
-import com.android.systemui.experimental.frp.util.orElseGet
-import com.android.systemui.experimental.frp.util.partitionEithers
-import com.android.systemui.experimental.frp.util.these
+import com.android.systemui.kairos.internal.util.Key
+import com.android.systemui.kairos.internal.util.associateByIndexTo
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.internal.util.mapParallel
+import com.android.systemui.kairos.internal.util.mapValuesNotNullParallelTo
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.None
+import com.android.systemui.kairos.util.Right
+import com.android.systemui.kairos.util.These
+import com.android.systemui.kairos.util.flatMap
+import com.android.systemui.kairos.util.getMaybe
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.maybeThat
+import com.android.systemui.kairos.util.maybeThis
+import com.android.systemui.kairos.util.merge
+import com.android.systemui.kairos.util.orElseGet
+import com.android.systemui.kairos.util.partitionEithers
+import com.android.systemui.kairos.util.these
 import java.util.TreeMap
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/MuxPrompt.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
similarity index 95%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/MuxPrompt.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
index ea0c150..cdfafa9 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/MuxPrompt.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.Key
-import com.android.systemui.experimental.frp.internal.util.launchImmediate
-import com.android.systemui.experimental.frp.internal.util.mapParallel
-import com.android.systemui.experimental.frp.internal.util.mapValuesNotNullParallelTo
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Left
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.None
-import com.android.systemui.experimental.frp.util.Right
-import com.android.systemui.experimental.frp.util.filterJust
-import com.android.systemui.experimental.frp.util.map
-import com.android.systemui.experimental.frp.util.partitionEithers
+import com.android.systemui.kairos.internal.util.Key
+import com.android.systemui.kairos.internal.util.launchImmediate
+import com.android.systemui.kairos.internal.util.mapParallel
+import com.android.systemui.kairos.internal.util.mapValuesNotNullParallelTo
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.None
+import com.android.systemui.kairos.util.Right
+import com.android.systemui.kairos.util.filterJust
+import com.android.systemui.kairos.util.map
+import com.android.systemui.kairos.util.partitionEithers
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Network.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
similarity index 95%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Network.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
index b5ffe75..f0df89d 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Network.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.TState
-import com.android.systemui.experimental.frp.internal.util.HeteroMap
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.none
+import com.android.systemui.kairos.TState
+import com.android.systemui.kairos.internal.util.HeteroMap
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.none
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.ConcurrentLinkedDeque
 import java.util.concurrent.ConcurrentLinkedQueue
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/NoScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NoScope.kt
similarity index 93%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/NoScope.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NoScope.kt
index 6375918..fbd9689 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/NoScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NoScope.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.FrpScope
+import com.android.systemui.kairos.FrpScope
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/NodeTypes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NodeTypes.kt
similarity index 95%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/NodeTypes.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NodeTypes.kt
index e7f76a0..0002407 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/NodeTypes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NodeTypes.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.util.Maybe
+import com.android.systemui.kairos.util.Maybe
 
 /*
 Dmux
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Output.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
similarity index 94%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Output.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
index e60dcca..a3af2d3 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Output.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.util.Just
+import com.android.systemui.kairos.util.Just
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/PullNodes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt
similarity index 91%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/PullNodes.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt
index b4656e0..dac98e0 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/PullNodes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.Key
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.map
+import com.android.systemui.kairos.internal.util.Key
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.map
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Deferred
 
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Scheduler.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
similarity index 98%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Scheduler.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
index 4fef865..872fb7a 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/Scheduler.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalCoroutinesApi::class)
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.PriorityBlockingQueue
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/StateScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
similarity index 89%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/StateScopeImpl.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
index c1d1076..baf4101 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/StateScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
@@ -14,27 +14,27 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.FrpDeferredValue
-import com.android.systemui.experimental.frp.FrpStateScope
-import com.android.systemui.experimental.frp.FrpStateful
-import com.android.systemui.experimental.frp.FrpTransactionScope
-import com.android.systemui.experimental.frp.GroupedTFlow
-import com.android.systemui.experimental.frp.TFlow
-import com.android.systemui.experimental.frp.TFlowInit
-import com.android.systemui.experimental.frp.TFlowLoop
-import com.android.systemui.experimental.frp.TState
-import com.android.systemui.experimental.frp.TStateInit
-import com.android.systemui.experimental.frp.emptyTFlow
-import com.android.systemui.experimental.frp.groupByKey
-import com.android.systemui.experimental.frp.init
-import com.android.systemui.experimental.frp.internal.util.mapValuesParallel
-import com.android.systemui.experimental.frp.mapCheap
-import com.android.systemui.experimental.frp.merge
-import com.android.systemui.experimental.frp.switch
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.map
+import com.android.systemui.kairos.FrpDeferredValue
+import com.android.systemui.kairos.FrpStateScope
+import com.android.systemui.kairos.FrpStateful
+import com.android.systemui.kairos.FrpTransactionScope
+import com.android.systemui.kairos.GroupedTFlow
+import com.android.systemui.kairos.TFlow
+import com.android.systemui.kairos.TFlowInit
+import com.android.systemui.kairos.TFlowLoop
+import com.android.systemui.kairos.TState
+import com.android.systemui.kairos.TStateInit
+import com.android.systemui.kairos.emptyTFlow
+import com.android.systemui.kairos.groupByKey
+import com.android.systemui.kairos.init
+import com.android.systemui.kairos.internal.util.mapValuesParallel
+import com.android.systemui.kairos.mapCheap
+import com.android.systemui.kairos.merge
+import com.android.systemui.kairos.switch
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.map
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TFlowImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TFlowImpl.kt
similarity index 94%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TFlowImpl.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TFlowImpl.kt
index 7997864..b904b48 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TFlowImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TFlowImpl.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.util.Maybe
+import com.android.systemui.kairos.util.Maybe
 
 /* Initialized TFlow */
 internal fun interface TFlowImpl<out A> {
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TStateImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
similarity index 95%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TStateImpl.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
index d8b6dac..5cec05c 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TStateImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.Key
-import com.android.systemui.experimental.frp.internal.util.associateByIndex
-import com.android.systemui.experimental.frp.internal.util.hashString
-import com.android.systemui.experimental.frp.internal.util.mapValuesParallel
-import com.android.systemui.experimental.frp.util.Just
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.none
+import com.android.systemui.kairos.internal.util.Key
+import com.android.systemui.kairos.internal.util.associateByIndex
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.internal.util.mapValuesParallel
+import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.none
 import java.util.concurrent.atomic.AtomicLong
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineStart
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TransactionalImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TransactionalImpl.kt
similarity index 89%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TransactionalImpl.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TransactionalImpl.kt
index c3f80a1..8647bdd 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/TransactionalImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TransactionalImpl.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal
+package com.android.systemui.kairos.internal
 
-import com.android.systemui.experimental.frp.internal.util.Key
-import com.android.systemui.experimental.frp.internal.util.hashString
+import com.android.systemui.kairos.internal.util.Key
+import com.android.systemui.kairos.internal.util.hashString
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Deferred
 
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/Bag.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Bag.kt
similarity index 96%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/Bag.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Bag.kt
index cc5538e..4718519 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/Bag.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Bag.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal.util
+package com.android.systemui.kairos.internal.util
 
 internal class Bag<T> private constructor(private val intMap: MutableMap<T, Int>) :
     Set<T> by intMap.keys {
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/ConcurrentNullableHashMap.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/ConcurrentNullableHashMap.kt
similarity index 95%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/ConcurrentNullableHashMap.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/ConcurrentNullableHashMap.kt
index 449aa19..6c8ae7c 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/ConcurrentNullableHashMap.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/ConcurrentNullableHashMap.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal.util
+package com.android.systemui.kairos.internal.util
 
 import java.util.concurrent.ConcurrentHashMap
 
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/HeteroMap.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/HeteroMap.kt
similarity index 86%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/HeteroMap.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/HeteroMap.kt
index 14a567c..5cee2dd 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/HeteroMap.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/HeteroMap.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal.util
+package com.android.systemui.kairos.internal.util
 
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.None
-import com.android.systemui.experimental.frp.util.just
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.None
+import com.android.systemui.kairos.util.just
 import java.util.concurrent.ConcurrentHashMap
 
 internal interface Key<A>
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/MapUtils.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/MapUtils.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/MapUtils.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/MapUtils.kt
index 6f19a76..ebf9a66 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/MapUtils.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/MapUtils.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.internal.util
+package com.android.systemui.kairos.internal.util
 
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.async
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/Util.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Util.kt
similarity index 97%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/Util.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Util.kt
index 0a47429..6bb7f9f 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/internal/util/Util.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Util.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalCoroutinesApi::class)
 
-package com.android.systemui.experimental.frp.internal.util
+package com.android.systemui.kairos.internal.util
 
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/Either.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Either.kt
similarity index 98%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/Either.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Either.kt
index dca8364..ad9f7d7 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/Either.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Either.kt
@@ -16,7 +16,7 @@
 
 @file:Suppress("NOTHING_TO_INLINE")
 
-package com.android.systemui.experimental.frp.util
+package com.android.systemui.kairos.util
 
 /**
  * Contains a value of two possibilities: `Left<A>` or `Right<B>`
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/Maybe.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
similarity index 99%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/Maybe.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
index 59c680e..c3cae38 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/Maybe.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
@@ -16,7 +16,7 @@
 
 @file:Suppress("NOTHING_TO_INLINE", "SuspendCoroutine")
 
-package com.android.systemui.experimental.frp.util
+package com.android.systemui.kairos.util
 
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/These.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/These.kt
similarity index 98%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/These.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/These.kt
index 5404c07..aa95e0d 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/These.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/These.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.util
+package com.android.systemui.kairos.util
 
 /** Contains at least one of two potential values. */
 sealed class These<A, B> {
diff --git a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/WithPrev.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/WithPrev.kt
similarity index 93%
rename from packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/WithPrev.kt
rename to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/WithPrev.kt
index e52a6e1..5cfaa3e 100644
--- a/packages/SystemUI/frp/src/com/android/systemui/experimental/frp/util/WithPrev.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/WithPrev.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.experimental.frp.util
+package com.android.systemui.kairos.util
 
 /** Holds a [newValue] emitted from a `TFlow`, along with the [previousValue] emitted value. */
 data class WithPrev<out S, out T : S>(val previousValue: S, val newValue: T)
diff --git a/packages/SystemUI/frp/test/com/android/systemui/experimental/frp/FrpTests.kt b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
similarity index 98%
rename from packages/SystemUI/frp/test/com/android/systemui/experimental/frp/FrpTests.kt
rename to packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
index a58f499..165230b 100644
--- a/packages/SystemUI/frp/test/com/android/systemui/experimental/frp/FrpTests.kt
+++ b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
@@ -16,17 +16,17 @@
 
 @file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalFrpApi::class)
 
-package com.android.systemui.experimental.frp
+package com.android.systemui.kairos
 
-import com.android.systemui.experimental.frp.util.Either
-import com.android.systemui.experimental.frp.util.Left
-import com.android.systemui.experimental.frp.util.Maybe
-import com.android.systemui.experimental.frp.util.None
-import com.android.systemui.experimental.frp.util.Right
-import com.android.systemui.experimental.frp.util.just
-import com.android.systemui.experimental.frp.util.map
-import com.android.systemui.experimental.frp.util.maybe
-import com.android.systemui.experimental.frp.util.none
+import com.android.systemui.kairos.util.Either
+import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.None
+import com.android.systemui.kairos.util.Right
+import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.map
+import com.android.systemui.kairos.util.maybe
+import com.android.systemui.kairos.util.none
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.seconds
 import kotlin.time.DurationUnit
@@ -55,7 +55,7 @@
 import org.junit.Assert.assertTrue
 import org.junit.Test
 
-class FrpTests {
+class KairosTests {
 
     @Test
     fun basic() = runFrpTest { network ->
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 994bdb5..6489905 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -206,7 +206,7 @@
 
     // Inform that DND settings have changed on OS upgrade
     // Package: android
-    NOTE_ZEN_UPGRADE = 48;
+    NOTE_ZEN_UPGRADE = 48  [deprecated = true];
 
     // Notification to suggest automatic battery saver.
     // Package: android
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 6b6b39d..a77ba62 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -47,7 +47,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MagnificationSpec;
 import android.view.View;
@@ -1637,9 +1636,10 @@
      * <strong>if scale is >= {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}</strong>.
      * We assume if the scale is < {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}, there
      * will be no obvious magnification effect.
+     * Only the value of the default display is persisted in user's settings.
      */
     public void persistScale(int displayId) {
-        final float scale = getScale(Display.DEFAULT_DISPLAY);
+        final float scale = getScale(displayId);
         if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) {
             return;
         }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index e0cbf8d..d0c3daf 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -205,6 +205,7 @@
                 .verifyCallerCanExecuteAppFunction(
                         callingUid,
                         callingPid,
+                        targetUser,
                         requestInternal.getCallingPackage(),
                         targetPackageName,
                         requestInternal.getClientRequest().getFunctionIdentifier())
@@ -447,7 +448,7 @@
             Slog.e(TAG, "Failed to bind to the AppFunctionService");
             safeExecuteAppFunctionCallback.onResult(
                     ExecuteAppFunctionResponse.newFailure(
-                            ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
+                            ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
                             "Failed to bind the AppFunctionService.",
                             /* extras= */ null));
         }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index 3592ed5..5393b93 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -64,6 +64,9 @@
      * {@link Manifest.permission#EXECUTE_APP_FUNCTIONS} granted. In some cases, app functions can
      * still opt-out of caller having {@link Manifest.permission#EXECUTE_APP_FUNCTIONS}.
      *
+     * @param callingUid The calling uid.
+     * @param callingPid The calling pid.
+     * @param targetUser The user which the caller is requesting to execute as.
      * @param callerPackageName The calling package (as previously validated).
      * @param targetPackageName The package that owns the app function to execute.
      * @param functionId The id of the app function to execute.
@@ -72,6 +75,7 @@
     AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
             int callingUid,
             int callingPid,
+            @NonNull UserHandle targetUser,
             @NonNull String callerPackageName,
             @NonNull String targetPackageName,
             @NonNull String functionId);
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index 8b6251a..e85a70d 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -93,6 +93,7 @@
     public AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
             int callingUid,
             int callingPid,
+            @NonNull UserHandle targetUser,
             @NonNull String callerPackageName,
             @NonNull String targetPackageName,
             @NonNull String functionId) {
@@ -122,7 +123,10 @@
 
         FutureAppSearchSession futureAppSearchSession =
                 new FutureAppSearchSessionImpl(
-                        mContext.getSystemService(AppSearchManager.class),
+                        Objects.requireNonNull(
+                                mContext
+                                        .createContextAsUser(targetUser, 0)
+                                        .getSystemService(AppSearchManager.class)),
                         THREAD_POOL_EXECUTOR,
                         new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
 
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index cd2dd3a..81ae717 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -42,6 +42,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.compat.CompatChanges;
 import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
 import android.companion.virtual.ActivityPolicyExemption;
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.IVirtualDeviceActivityListener;
@@ -153,6 +154,9 @@
 
     private static final String PERSISTENT_ID_PREFIX_CDM_ASSOCIATION = "companion:";
 
+    private static final List<String> DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS = List.of(
+            AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
+
     /**
      * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched.
      */
@@ -498,6 +502,10 @@
         return mAssociationInfo == null ? mParams.getName() : mAssociationInfo.getDisplayName();
     }
 
+    String getDeviceProfile() {
+        return mAssociationInfo == null ? null : mAssociationInfo.getDeviceProfile();
+    }
+
     /** Returns the public representation of the device. */
     VirtualDevice getPublicVirtualDeviceObject() {
         return mPublicVirtualDeviceObject;
@@ -1294,6 +1302,11 @@
         return hasCustomAudioInputSupportInternal();
     }
 
+    @Override
+    public boolean canCreateMirrorDisplays() {
+        return DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS.contains(getDeviceProfile());
+    }
+
     private boolean hasCustomAudioInputSupportInternal() {
         if (!Flags.vdmPublicApis()) {
             return false;
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 9060250..2acedd5 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -47,6 +47,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.EventLog;
 import android.util.IndentingPrintWriter;
 import android.util.LongArrayQueue;
 import android.util.Slog;
@@ -200,6 +201,13 @@
     // aborted.
     private static final String METADATA_FILE = "/metadata/watchdog/mitigation_count.txt";
 
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+     */
+    private static final int LOG_TAG_RESCUE_NOTE = 2900;
+
     private static final Object sPackageWatchdogLock = new Object();
     @GuardedBy("sPackageWatchdogLock")
     private static PackageWatchdog sPackageWatchdog;
@@ -2024,7 +2032,7 @@
             } else {
                 int count = getCount() + 1;
                 setCount(count);
-                EventLogTags.writeRescueNote(Process.ROOT_UID, count, window);
+                EventLog.writeEvent(LOG_TAG_RESCUE_NOTE, Process.ROOT_UID, count, window);
                 if (Flags.recoverabilityDetection()) {
                     // After a reboot (e.g. by WARM_REBOOT or mainline rollback) we apply
                     // mitigations without waiting for DEFAULT_BOOT_LOOP_TRIGGER_COUNT.
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index ada1953..feb5775 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -42,6 +42,7 @@
 import android.sysprop.CrashRecoveryProperties;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
 
@@ -154,6 +155,14 @@
     private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
             | ApplicationInfo.FLAG_SYSTEM;
 
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+     */
+    private static final int LOG_TAG_RESCUE_SUCCESS = 2902;
+    private static final int LOG_TAG_RESCUE_FAILURE = 2903;
+
     /** Register the Rescue Party observer as a Package Watchdog health observer */
     public static void registerHealthObserver(Context context) {
         PackageWatchdog.getInstance(context).registerHealthObserver(
@@ -523,7 +532,7 @@
         Slog.w(TAG, "Attempting rescue level " + levelToString(level));
         try {
             executeRescueLevelInternal(context, level, failedPackage);
-            EventLogTags.writeRescueSuccess(level);
+            EventLog.writeEvent(LOG_TAG_RESCUE_SUCCESS, level);
             String successMsg = "Finished rescue level " + levelToString(level);
             if (!TextUtils.isEmpty(failedPackage)) {
                 successMsg += " for package " + failedPackage;
@@ -704,7 +713,7 @@
     private static void logRescueException(int level, @Nullable String failedPackageName,
             Throwable t) {
         final String msg = getCompleteMessage(t);
-        EventLogTags.writeRescueFailure(level, msg);
+        EventLog.writeEvent(LOG_TAG_RESCUE_FAILURE, level, msg);
         String failureMsg = "Failed rescue level " + levelToString(level);
         if (!TextUtils.isEmpty(failedPackageName)) {
             failureMsg += " for package " + failedPackageName;
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index a459ea9..ce66dc3 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -114,6 +114,9 @@
             "options": [
                 {
                     "include-filter": "android.os.storage.cts.StorageManagerTest"
+                },
+                {
+                    "include-filter": "android.os.storage.cts.StorageStatsManagerTest"
                 }
             ]
         }
@@ -173,15 +176,6 @@
                     "include-filter": "com.android.server.wm.BackgroundActivityStart*"
                 }
             ]
-        },
-        {
-            "name": "CtsOsTestCases",
-            "file_patterns": ["StorageManagerService\\.java"],
-            "options": [
-                {
-                    "include-filter": "android.os.storage.cts.StorageStatsManagerTest"
-                }
-            ]
         }
    ]
 }
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 71b6456..aca6d0b 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -1005,7 +1005,8 @@
                 case (int) AppsStartInfoProto.Package.USERS:
                     AppStartInfoContainer container =
                             new AppStartInfoContainer(mAppStartInfoHistoryListSize);
-                    int uid = container.readFromProto(proto, AppsStartInfoProto.Package.USERS);
+                    int uid = container.readFromProto(proto, AppsStartInfoProto.Package.USERS,
+                            pkgName);
                     synchronized (mLock) {
                         mData.put(pkgName, uid, container);
                     }
@@ -1403,7 +1404,7 @@
             proto.end(token);
         }
 
-        int readFromProto(ProtoInputStream proto, long fieldId)
+        int readFromProto(ProtoInputStream proto, long fieldId, String packageName)
                 throws IOException, WireTypeMismatchException, ClassNotFoundException {
             long token = proto.start(fieldId);
             for (int next = proto.nextField();
@@ -1418,6 +1419,7 @@
                         // have a create time.
                         ApplicationStartInfo info = new ApplicationStartInfo(0);
                         info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO);
+                        info.setPackageName(packageName);
                         mInfos.add(info);
                         break;
                     case (int) AppsStartInfoProto.Package.User.MONITORING_ENABLED:
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 78a0a11..796de19 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -458,7 +458,13 @@
         }
 
         void setThreadPriority(int tid, int priority) {
-            Process.setThreadPriority(tid, priority);
+            if (Flags.resetOnForkEnabled()) {
+                Process.setThreadScheduler(tid,
+                    Process.SCHED_OTHER | Process.SCHED_RESET_ON_FORK,
+                    priority);
+            } else {
+                 Process.setThreadPriority(tid, priority);
+            }
         }
 
     }
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 4f6da3b..cc66378 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -214,3 +214,10 @@
     description: "Defer submitting display events to frozen processes."
     bug: "326315985"
 }
+
+flag {
+    name: "reset_on_fork_enabled"
+    namespace: "system_performance"
+    description: "Set reset_on_fork flag."
+    bug: "370988407"
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 87504154..0475b94 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -384,11 +384,12 @@
     /**
      * Indicates if a Bluetooth SCO activation request owner is controlling
      * the SCO audio state itself or not.
-     * @param uid the UI of the SOC request owner app
+     * @param uid the UID of the SOC request owner app
      * @return true if we should control SCO audio state, false otherwise
      */
     private boolean shouldStartScoForUid(int uid) {
-        return !(uid == Process.BLUETOOTH_UID || uid == Process.PHONE_UID);
+        return !(UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)
+                || UserHandle.isSameApp(uid, Process.PHONE_UID));
     }
 
     @GuardedBy("mDeviceStateLock")
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 5fd12c2..09de894 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -385,11 +385,6 @@
                     || !updatedDevice.getDeviceAddress().equals(ads.getDeviceAddress())) {
                 continue;
             }
-            if (mDeviceBroker.isSADevice(updatedDevice) == mDeviceBroker.isSADevice(ads)) {
-                ads.setHasHeadTracker(updatedDevice.hasHeadTracker());
-                ads.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
-                ads.setSAEnabled(updatedDevice.isSAEnabled());
-            }
             ads.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
 
             mDeviceBroker.postUpdatedAdiDeviceState(ads, false /*initSA*/);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 561030e..c37d471 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1583,8 +1583,11 @@
 
         synchronized (mCachedAbsVolDrivingStreamsLock) {
             mCachedAbsVolDrivingStreams.forEach((dev, stream) -> {
-                mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", /*enabled=*/true,
-                        stream);
+                boolean enabled = true;
+                if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+                    enabled = mAvrcpAbsVolSupported;
+                }
+                mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream);
             });
         }
     }
@@ -4881,7 +4884,7 @@
                 if (absDev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
                     enabled = mAvrcpAbsVolSupported;
                 }
-                if (stream != streamType) {
+                if (stream != streamType || !enabled) {
                     mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev, /*address=*/"",
                             enabled, streamType);
                 }
@@ -10097,9 +10100,6 @@
 
                 case MSG_INIT_SPATIALIZER:
                     onInitSpatializer();
-                    // the device inventory can only be synchronized after the
-                    // spatializer has been initialized
-                    mDeviceBroker.postSynchronizeAdiDevicesInInventory(null);
                     mAudioEventWakeLock.release();
                     break;
 
@@ -10383,10 +10383,10 @@
     }
 
     /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) {
-        mAvrcpAbsVolSupported = support;
-        if (absVolumeIndexFix()) {
-            int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
-            synchronized (mCachedAbsVolDrivingStreamsLock) {
+        synchronized (mCachedAbsVolDrivingStreamsLock) {
+            mAvrcpAbsVolSupported = support;
+            if (absVolumeIndexFix()) {
+                int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
                 mCachedAbsVolDrivingStreams.compute(a2dpDev, (dev, stream) -> {
                     if (!mAvrcpAbsVolSupported) {
                         mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
@@ -12499,6 +12499,12 @@
         pw.println("\nLoudness alignment:");
         mLoudnessCodecHelper.dump(pw);
 
+        pw.println("\nAbsolute voume devices:");
+        synchronized (mCachedAbsVolDrivingStreamsLock) {
+            mCachedAbsVolDrivingStreams.forEach((dev, stream) -> pw.println(
+                    "Device type: 0x" + Integer.toHexString(dev) + ", driving stream " + stream));
+        }
+
         mAudioSystem.dump(pw);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index abfbddc..3afecf1 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -879,6 +879,14 @@
                     );
                     break;
 
+                case BiometricPrompt.DISMISSED_REASON_ERROR_NO_WM:
+                    mClientReceiver.onError(
+                            getEligibleModalities(),
+                            BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                            0 /* vendorCode */
+                    );
+                    break;
+
                 default:
                     Slog.w(TAG, "Unhandled reason: " + reason);
                     break;
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index de7bce7..8734136 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -253,9 +253,15 @@
 
         // Check if any of the non-biometric and non-credential bits are set. If so, this is
         // invalid.
-        final int testBits = ~(Authenticators.DEVICE_CREDENTIAL
-                | Authenticators.BIOMETRIC_MIN_STRENGTH
-                | Authenticators.MANDATORY_BIOMETRICS);
+        final int testBits;
+        if (Flags.mandatoryBiometrics()) {
+            testBits = ~(Authenticators.DEVICE_CREDENTIAL
+                    | Authenticators.BIOMETRIC_MIN_STRENGTH
+                    | Authenticators.MANDATORY_BIOMETRICS);
+        } else {
+            testBits = ~(Authenticators.DEVICE_CREDENTIAL
+                    | Authenticators.BIOMETRIC_MIN_STRENGTH);
+        }
         if ((authenticators & testBits) != 0) {
             Slog.e(BiometricService.TAG, "Non-biometric, non-credential bits found."
                     + " Authenticators: " + authenticators);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index af9c9ac..8d96ba9 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -377,6 +377,7 @@
  *              </point>
  *            </map>
  *          </luxToBrightnessMapping>
+ *          <idleStylusTimeoutMillis>10000</idleStylusTimeoutMillis>
  *      </autoBrightness>
  *
  *      <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
@@ -708,6 +709,10 @@
 
     private static final int KEEP_CURRENT_BRIGHTNESS = -1;
 
+    // The default value to 0 which will signify that the stylus usage immediately stopped
+    // after it was started. This will make the system behave as if the stylus was never used
+    private static final int DEFAULT_IDLE_STYLUS_TIMEOUT_MILLIS = 0;
+
     private final Context mContext;
 
     // The details of the ambient light sensor associated with this display.
@@ -754,6 +759,9 @@
     @Nullable
     private DisplayBrightnessMappingConfig mDisplayBrightnessMapping;
 
+    private int mIdleStylusTimeoutMillis =
+            DEFAULT_IDLE_STYLUS_TIMEOUT_MILLIS;
+
     private float mBacklightMinimum = Float.NaN;
     private float mBacklightMaximum = Float.NaN;
     private float mBrightnessDefault = Float.NaN;
@@ -1730,6 +1738,7 @@
                 + ", mDisplayBrightnessMapping= " + mDisplayBrightnessMapping
                 + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
                 + ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
+                + ", mIdleStylusTimeoutMillis= " + mIdleStylusTimeoutMillis
                 + "\n"
                 + "mDefaultLowBlockingZoneRefreshRate= " + mDefaultLowBlockingZoneRefreshRate
                 + ", mDefaultHighBlockingZoneRefreshRate= " + mDefaultHighBlockingZoneRefreshRate
@@ -2389,10 +2398,19 @@
         loadAutoBrightnessDarkeningLightDebounceIdle(autoBrightness);
         mDisplayBrightnessMapping = new DisplayBrightnessMappingConfig(mContext, mFlags,
                 autoBrightness, getBacklightToBrightnessSpline());
+        loadIdleStylusTimeoutMillis(autoBrightness);
         loadEnableAutoBrightness(autoBrightness);
     }
 
     /**
+     * Gets the timeout post the stylus usage after which the automatic brightness will be enabled
+     * again
+     */
+    public int getIdleStylusTimeoutMillis() {
+        return mIdleStylusTimeoutMillis;
+    }
+
+    /**
      * Loads the auto-brightness brightening light debounce. Internally, this takes care of loading
      * the value from the display config, and if not present, falls back to config.xml.
      */
@@ -2923,6 +2941,16 @@
         return levels;
     }
 
+    private void loadIdleStylusTimeoutMillis(AutoBrightness autoBrightness) {
+        if (autoBrightness == null) {
+            return;
+        }
+        BigInteger idleStylusTimeoutMillis = autoBrightness.getIdleStylusTimeoutMillis();
+        if (idleStylusTimeoutMillis != null) {
+            mIdleStylusTimeoutMillis = idleStylusTimeoutMillis.intValue();
+        }
+    }
+
     private void loadEnableAutoBrightness(AutoBrightness autobrightness) {
         // mDdcAutoBrightnessAvailable is initialised to true, so that we fallback to using the
         // config.xml values if the autobrightness tag is not defined in the ddc file.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4152ec9..bb503aa 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1661,33 +1661,49 @@
         return false;
     }
 
+    private boolean hasVideoOutputPermission(String func) {
+        return checkCallingPermission(CAPTURE_VIDEO_OUTPUT, func)
+                || hasSecureVideoOutputPermission(func);
+    }
+
+    private boolean hasSecureVideoOutputPermission(String func) {
+        return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, func);
+    }
+
+    private boolean canCreateMirrorDisplays(IVirtualDevice virtualDevice) {
+        if (virtualDevice == null) {
+            return false;
+        }
+        try {
+            return virtualDevice.canCreateMirrorDisplays();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to query virtual device for permissions", e);
+            return false;
+        }
+    }
+
     private boolean canProjectVideo(IMediaProjection projection) {
-        if (projection != null) {
-            try {
-                if (projection.canProjectVideo()) {
-                    return true;
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to query projection service for permissions", e);
-            }
+        if (projection == null) {
+            return false;
         }
-        if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
-            return true;
+        try {
+            return projection.canProjectVideo();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to query projection service for permissions", e);
+            return false;
         }
-        return canProjectSecureVideo(projection);
     }
 
     private boolean canProjectSecureVideo(IMediaProjection projection) {
-        if (projection != null) {
-            try {
-                if (projection.canProjectSecureVideo()) {
-                    return true;
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to query projection service for permissions", e);
-            }
+        if (projection == null) {
+            return false;
         }
-        return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
+        try {
+            return projection.canProjectSecureVideo();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to query projection service for permissions", e);
+            return false;
+        }
     }
 
     private boolean checkCallingPermission(String permission, String func) {
@@ -1793,7 +1809,8 @@
                 && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
             // Only a valid media projection or a virtual device can create a mirror virtual
             // display.
-            if (!canProjectVideo(projection) && virtualDevice == null) {
+            if (!canProjectVideo(projection) && !canCreateMirrorDisplays(virtualDevice)
+                    && !hasVideoOutputPermission("createVirtualDisplayInternal")) {
                 throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
                         + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
                         + "MediaProjection token in order to create a screen sharing virtual "
@@ -1803,7 +1820,8 @@
             }
         }
         if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
-            if (!canProjectSecureVideo(projection)) {
+            if (!canProjectSecureVideo(projection)
+                    && !hasSecureVideoOutputPermission("createVirtualDisplayInternal")) {
                 throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
                         + "or an appropriate MediaProjection token to create a "
                         + "secure virtual display.");
@@ -2093,16 +2111,6 @@
         }
     }
 
-    private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) {
-        synchronized (mSyncRoot) {
-            if (mVirtualDisplayAdapter == null) {
-                return;
-            }
-
-            mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn);
-        }
-    }
-
     private void setVirtualDisplayRotationInternal(IBinder appToken,
             @Surface.Rotation int rotation) {
         int displayId;
@@ -4615,16 +4623,6 @@
         }
 
         @Override // Binder call
-        public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                setVirtualDisplayStateInternal(callback.asBinder(), isOn);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override // Binder call
         public void setVirtualDisplayRotation(IVirtualDisplayCallback callback,
                 @Surface.Rotation int rotation) {
             if (!android.companion.virtualdevice.flags.Flags.virtualDisplayRotationApi()) {
@@ -5648,6 +5646,21 @@
         public void onPresentation(int displayId, boolean isShown) {
             mExternalDisplayPolicy.onPresentation(displayId, isShown);
         }
+
+        @Override
+        public void stylusGestureStarted(long eventTime) {
+            if (mFlags.isBlockAutobrightnessChangesOnStylusUsage()) {
+                DisplayPowerController displayPowerController;
+                synchronized (mSyncRoot) {
+                    displayPowerController = mDisplayPowerControllers.get(
+                            Display.DEFAULT_DISPLAY);
+                }
+                // We assume that the stylus is being used on the default display. This should
+                // be changed to the displayId on which it is being used once we start getting this
+                // information from the input manager service
+                displayPowerController.stylusGestureStarted(eventTime);
+            }
+        }
     }
 
     class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 03fec011..8f07bb3 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -167,12 +167,11 @@
     private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
     private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
     private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18;
-
-
+    private static final int MSG_SET_STYLUS_BEING_USED = 19;
+    private static final int MSG_SET_STYLUS_USE_ENDED = 20;
 
     private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
 
-
     // State machine constants for tracking initial brightness ramp skipping when enabled.
     private static final int RAMP_STATE_SKIP_NONE = 0;
     private static final int RAMP_STATE_SKIP_INITIAL = 1;
@@ -191,6 +190,10 @@
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80,
         90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200,
         1400, 1600, 1800, 2000, 2250, 2500, 2750, 3000};
+
+    private static final int STYLUS_USAGE_DEBOUNCE_TIME  = 1000;
+    private static final int NANO_SECONDS_TO_MILLI_SECONDS_RATIO  = 1_000_000;
+
     private static final int[] BRIGHTNESS_RANGE_INDEX = {
         FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_UNKNOWN,
         FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__BUCKET_INDEX__RANGE_0_1,
@@ -498,6 +501,11 @@
     @GuardedBy("mLock")
     private int mPendingOverrideDozeScreenStateLocked;
 
+    private long mLastStylusUsageEventTime = -1;
+
+    // The time of inactivity after which the stylus can be assumed to be no longer in use.
+    private long mIdleStylusTimeoutMillisConfig = 0;
+
     /**
      * Creates the display power controller.
      */
@@ -518,6 +526,7 @@
         mSensorManager = sensorManager;
         mHandler = new DisplayControllerHandler(handler.getLooper());
         mDisplayDeviceConfig = mDisplayDevice.getDisplayDeviceConfig();
+        mIdleStylusTimeoutMillisConfig = mDisplayDeviceConfig.getIdleStylusTimeoutMillis();
         mIsEnabled = logicalDisplay.isEnabledLocked();
         mIsInTransition = logicalDisplay.isInTransitionLocked();
         mIsDisplayInternal = displayDeviceInfo.type == Display.TYPE_INTERNAL;
@@ -893,6 +902,7 @@
                 mPhysicalDisplayName = displayName;
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
+                mIdleStylusTimeoutMillisConfig = mDisplayDeviceConfig.getIdleStylusTimeoutMillis();
                 mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
                 loadFromDisplayDeviceConfig(token, info, hbmMetadata);
                 mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
@@ -2971,6 +2981,18 @@
         return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
     }
 
+    public void stylusGestureStarted(long eventTimeNanoSeconds) {
+        long eventTimeMs = eventTimeNanoSeconds / NANO_SECONDS_TO_MILLI_SECONDS_RATIO;
+        if (mLastStylusUsageEventTime == -1
+                || eventTimeMs > mLastStylusUsageEventTime + STYLUS_USAGE_DEBOUNCE_TIME) {
+            synchronized (mLock) {
+                // Add a message to notify the stylus usage has started
+                mHandler.sendEmptyMessageAtTime(MSG_SET_STYLUS_BEING_USED, mClock.uptimeMillis());
+            }
+            mLastStylusUsageEventTime = eventTimeMs;
+        }
+    }
+
     private final class DisplayControllerHandler extends Handler {
         DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -3087,6 +3109,20 @@
                         updatePowerState();
                     }
                     break;
+                case MSG_SET_STYLUS_BEING_USED:
+                    // Remove any MSG_SET_STYLUS_USE_ENDED message from the handler queue and
+                    // post a delayed MSG_SET_STYLUS_USE_ENDED message to delay the stylus
+                    // usage ended event processing
+                    mHandler.removeMessages(MSG_SET_STYLUS_USE_ENDED);
+                    Message message = mHandler.obtainMessage(MSG_SET_STYLUS_USE_ENDED);
+                    mHandler.sendMessageAtTime(message,
+                            mClock.uptimeMillis() + mIdleStylusTimeoutMillisConfig);
+                    mDisplayBrightnessController.setStylusBeingUsed(true);
+                    break;
+                case MSG_SET_STYLUS_USE_ENDED:
+                    mDisplayBrightnessController.setStylusBeingUsed(false);
+                    updatePowerState();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index bcb600d..06a9103 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -355,8 +355,8 @@
     public SparseArray<int[]> getDisplayIdsByGroupIdLocked() {
         SparseArray<int[]> displayIdsByGroupIds = new SparseArray<>();
         for (int i = 0; i < mDisplayGroups.size(); i++) {
-            int groupId = mDisplayGroups.get(i).getGroupId();
-            displayIdsByGroupIds.put(groupId, getDisplayIdsForGroupLocked(groupId));
+            final int displayGroupId = mDisplayGroups.keyAt(i);
+            displayIdsByGroupIds.put(displayGroupId, getDisplayIdsForGroupLocked(displayGroupId));
         }
         return displayIdsByGroupIds;
     }
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 9b02f4b..e77c5ec 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -207,13 +207,6 @@
         return device;
     }
 
-    void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) {
-        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
-        if (device != null) {
-            device.setDisplayState(isOn);
-        }
-    }
-
     DisplayDevice getDisplayDevice(IBinder appToken) {
         return mVirtualDisplayDevices.get(appToken);
     }
@@ -273,7 +266,6 @@
         private boolean mStopped;
         private int mPendingChanges;
         private Display.Mode mMode;
-        private boolean mIsDisplayOn;
         private int mDisplayIdToMirror;
         private boolean mIsWindowManagerMirroring;
         private DisplayCutout mDisplayCutout;
@@ -299,9 +291,8 @@
             mCallback = callback;
             mProjection = projection;
             mMediaProjectionCallback = mediaProjectionCallback;
-            mDisplayState = Display.STATE_UNKNOWN;
+            mDisplayState = Display.STATE_ON;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
-            mIsDisplayOn = surface != null;
             mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
             mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled();
         }
@@ -394,6 +385,8 @@
                 float sdrBrightnessState, DisplayOffloadSessionImpl displayOffloadSession) {
             if (state != mDisplayState) {
                 mDisplayState = state;
+                mInfo = null;
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
                 if (state == Display.STATE_OFF) {
                     mCallback.dispatchDisplayPaused();
                 } else {
@@ -416,12 +409,13 @@
 
         public void setSurfaceLocked(Surface surface) {
             if (!mStopped && mSurface != surface) {
-                if ((mSurface != null) != (surface != null)) {
+                if (mDisplayState == Display.STATE_ON
+                        && ((mSurface == null) != (surface == null))) {
+                    mInfo = null;
                     sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
                 }
                 sendTraversalRequestLocked();
                 mSurface = surface;
-                mInfo = null;
                 mPendingChanges |= PENDING_SURFACE_CHANGE;
             }
         }
@@ -439,14 +433,6 @@
             }
         }
 
-        void setDisplayState(boolean isOn) {
-            if (mIsDisplayOn != isOn) {
-                mIsDisplayOn = isOn;
-                mInfo = null;
-                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
-            }
-        }
-
         public void stopLocked() {
             Slog.d(TAG, "Virtual Display: stopping device " + mName);
             setSurfaceLocked(null);
@@ -567,7 +553,11 @@
                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
                         DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
 
-                mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF;
+                if (mSurface == null) {
+                    mInfo.state = Display.STATE_OFF;
+                } else {
+                    mInfo.state = mDisplayState;
+                }
 
                 mInfo.ownerUid = mOwnerUid;
                 mInfo.ownerPackageName = mOwnerPackageName;
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 72a91d5..71fdaf3 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -501,6 +501,13 @@
         return true;
     }
 
+    /**
+     * Notifies if the stylus is currently being used or not.
+     */
+    public void setStylusBeingUsed(boolean isEnabled) {
+        // Todo(b/369977976) - Disable the auto-brightness strategy
+    }
+
     @VisibleForTesting
     static class Injector {
         DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(Context context,
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 df66893..5284d1c 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -203,6 +203,10 @@
             Flags.FLAG_NORMAL_BRIGHTNESS_FOR_DOZE_PARAMETER,
             Flags::normalBrightnessForDozeParameter
     );
+    private final FlagState mBlockAutobrightnessChangesOnStylusUsage = new FlagState(
+            Flags.FLAG_BLOCK_AUTOBRIGHTNESS_CHANGES_ON_STYLUS_USAGE,
+            Flags::blockAutobrightnessChangesOnStylusUsage
+    );
 
     private final FlagState mEnableBatteryStatsForAllDisplays = new FlagState(
             Flags.FLAG_ENABLE_BATTERY_STATS_FOR_ALL_DISPLAYS,
@@ -436,6 +440,13 @@
     }
 
     /**
+     * @return {@code true} if autobrightness is to be blocked when stylus is being used
+     */
+    public boolean isBlockAutobrightnessChangesOnStylusUsage() {
+        return mBlockAutobrightnessChangesOnStylusUsage.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
@@ -479,6 +490,7 @@
         pw.println(" " + mNormalBrightnessForDozeParameter);
         pw.println(" " + mIdleScreenConfigInSubscribingLightSensor);
         pw.println(" " + mEnableBatteryStatsForAllDisplays);
+        pw.println(" " + mBlockAutobrightnessChangesOnStylusUsage);
     }
 
     private static class FlagState {
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 e3ebe5b..252ed09 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
@@ -364,4 +364,12 @@
     description: "Flag to enable battery stats for all displays."
     bug: "366112793"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    name: "block_autobrightness_changes_on_stylus_usage"
+    namespace: "display_manager"
+    description: "Block the usage of ALS to control the display brightness when stylus is being used"
+    bug: "352411468"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f045576..8acf583 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2694,6 +2694,9 @@
     @SuppressWarnings("unused")
     private void notifyStylusGestureStarted(int deviceId, long eventTime) {
         mBatteryController.notifyStylusGestureStarted(deviceId, eventTime);
+        if (mDisplayManagerInternal != null) {
+            mDisplayManagerInternal.stylusGestureStarted(eventTime);
+        }
     }
 
     /**
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 47f579d..e7e519e 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -42,6 +42,8 @@
 import android.app.IProcessObserver;
 import android.app.KeyguardManager;
 import android.app.compat.CompatChanges;
+import android.app.role.RoleManager;
+import android.companion.AssociationRequest;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.content.ComponentName;
@@ -94,7 +96,7 @@
 
 /**
  * Manages MediaProjection sessions.
- *
+ * <p>
  * The {@link MediaProjectionManagerService} manages the creation and lifetime of MediaProjections,
  * as well as the capabilities they grant. Any service using MediaProjection tokens as permission
  * grants <b>must</b> validate the token before use by calling {@link
@@ -137,6 +139,7 @@
     private final PackageManager mPackageManager;
     private final WindowManagerInternal mWmInternal;
     private final KeyguardManager mKeyguardManager;
+    private final RoleManager mRoleManager;
 
     private final MediaRouter mMediaRouter;
     private final MediaRouterCallback mMediaRouterCallback;
@@ -173,6 +176,7 @@
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mKeyguardManager.addKeyguardLockedStateListener(
                 mContext.getMainExecutor(), this::onKeyguardLockedStateChanged);
+        mRoleManager = mContext.getSystemService(RoleManager.class);
         Watchdog.getInstance().addMonitor(this);
     }
 
@@ -182,6 +186,7 @@
      *   - be one of the bugreport allowlisted packages, or
      *   - hold the OP_PROJECT_MEDIA AppOp.
      */
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
     private boolean canCaptureKeyguard() {
         if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
             return true;
@@ -193,6 +198,9 @@
             if (mPackageManager.checkPermission(RECORD_SENSITIVE_CONTENT,
                     mProjectionGrant.packageName)
                     == PackageManager.PERMISSION_GRANTED) {
+                Slog.v(TAG,
+                        "Allowing keyguard capture for package with RECORD_SENSITIVE_CONTENT "
+                                + "permission");
                 return true;
             }
             if (AppOpsManager.MODE_ALLOWED == mAppOps.noteOpNoThrow(AppOpsManager.OP_PROJECT_MEDIA,
@@ -200,6 +208,13 @@
                     "recording lockscreen")) {
                 // Some tools use media projection by granting the OP_PROJECT_MEDIA app
                 // op via a shell command. Those tools can be granted keyguard capture
+                Slog.v(TAG,
+                        "Allowing keyguard capture for package with OP_PROJECT_MEDIA AppOp ");
+                return true;
+            }
+            if (isProjectionAppHoldingAppStreamingRoleLocked()) {
+                Slog.v(TAG,
+                        "Allowing keyguard capture for package holding app streaming role.");
                 return true;
             }
             return SystemConfig.getInstance().getBugreportWhitelistedPackages()
@@ -698,6 +713,20 @@
         }
     }
 
+    /**
+     * Application holding the app streaming role
+     * ({@value AssociationRequest#DEVICE_PROFILE_APP_STREAMING}) are allowed to record the
+     * lockscreen.
+     *
+     * @return true if the is held by the recording application.
+     */
+    @GuardedBy("mLock")
+    private boolean isProjectionAppHoldingAppStreamingRoleLocked() {
+        return mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+                        mContext.getUser())
+                .contains(mProjectionGrant.packageName);
+    }
+
     private void dump(final PrintWriter pw) {
         pw.println("MEDIA PROJECTION MANAGER (dumpsys media_projection)");
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index ff263d1..bdca555 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -37,7 +37,6 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.util.NotificationMessagingUtil;
 
 import java.io.PrintWriter;
@@ -173,13 +172,6 @@
             maybeLogInterceptDecision(record, false, "criticalNotification");
             return false;
         }
-        // Make an exception to policy for the notification saying that policy has changed
-        if (NotificationManager.Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)
-                && "android".equals(record.getSbn().getPackageName())
-                && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.getSbn().getId()) {
-            maybeLogInterceptDecision(record, false, "systemDndChangedNotification");
-            return false;
-        }
         switch (zen) {
             case Global.ZEN_MODE_NO_INTERRUPTIONS:
                 // #notevenalarms
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 626c3dd..ea211a9 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -54,10 +54,8 @@
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
 import android.app.Flags;
-import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
-import android.app.PendingIntent;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
@@ -74,7 +72,6 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
-import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
@@ -90,7 +87,6 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
@@ -117,8 +113,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
@@ -309,7 +303,6 @@
         mHandler.postMetricsTimer();
         cleanUpZenRules();
         mIsSystemServicesReady = true;
-        showZenUpgradeNotification(mZenMode);
     }
 
     /**
@@ -485,7 +478,7 @@
             populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true);
             rule = maybeRestoreRemovedRule(newConfig, pkg, rule, automaticZenRule, origin);
             newConfig.automaticRules.put(rule.id, rule);
-            maybeReplaceDefaultRule(newConfig, automaticZenRule);
+            maybeReplaceDefaultRule(newConfig, null, automaticZenRule);
 
             if (setConfigLocked(newConfig, origin, reason, rule.component, true, callingUid)) {
                 return rule.id;
@@ -535,13 +528,24 @@
         return ruleToRestore;
     }
 
-    private static void maybeReplaceDefaultRule(ZenModeConfig config, AutomaticZenRule addedRule) {
+    /**
+     * Possibly delete built-in rules if a more suitable rule is added or updated.
+     *
+     * <p>Today, this is done in one case: delete a disabled "Sleeping" rule if a Bedtime Mode is
+     * added (or an existing mode is turned into {@link AutomaticZenRule#TYPE_BEDTIME}, when
+     * upgrading). Because only the {@code config_systemWellbeing} package is allowed to use rules
+     * of this type, this will not trigger wantonly.
+     *
+     * @param oldRule If non-null, {@code rule} is updating {@code oldRule}. Otherwise,
+     *                {@code rule} is being added.
+     */
+    private static void maybeReplaceDefaultRule(ZenModeConfig config, @Nullable ZenRule oldRule,
+            AutomaticZenRule rule) {
         if (!Flags.modesApi()) {
             return;
         }
-        if (addedRule.getType() == AutomaticZenRule.TYPE_BEDTIME) {
-            // Delete a built-in disabled "Sleeping" rule when a BEDTIME rule is added; it may have
-            // smarter triggers and it will prevent confusion about which one to use.
+        if (rule.getType() == AutomaticZenRule.TYPE_BEDTIME
+                && (oldRule == null || oldRule.type != rule.getType())) {
             // Note: we must not verify canManageAutomaticZenRule here, since most likely they
             // won't have the same owner (sleeping - system; bedtime - DWB).
             ZenRule sleepingRule = config.automaticRules.get(
@@ -589,6 +593,10 @@
                 // condition) when no changes happen.
                 return true;
             }
+
+            if (Flags.modesUi()) {
+                maybeReplaceDefaultRule(newConfig, oldRule, automaticZenRule);
+            }
             return setConfigLocked(newConfig, origin, reason,
                     newRule.component, true, callingUid);
         }
@@ -1584,8 +1592,6 @@
             String reason, @Nullable String caller, int callingUid) {
         setManualZenMode(zenMode, conditionId, origin, reason, caller, true /*setRingerMode*/,
                 callingUid);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 0);
     }
 
     private void setManualZenMode(int zenMode, Uri conditionId, @ConfigOrigin int origin,
@@ -1783,17 +1789,6 @@
                 SystemZenRules.maybeUpgradeRules(mContext, config);
             }
 
-            // Resolve user id for settings.
-            userId = userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId;
-            if (config.version < ZenModeConfig.XML_VERSION_ZEN_UPGRADE) {
-                Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1, userId);
-            } else {
-                // devices not restoring/upgrading already have updated zen settings
-                Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
-            }
-
             if (Flags.modesApi() && forRestore) {
                 // Note: forBackup doesn't write deletedRules, but just in case.
                 config.deletedRules.clear();
@@ -2062,7 +2057,6 @@
         Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
         ZenLog.traceSetZenMode(Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, -1),
                 "updated setting");
-        showZenUpgradeNotification(zen);
     }
 
     private int getPreviousRingerModeSetting() {
@@ -2117,12 +2111,6 @@
             for (ZenRule automaticRule : mConfig.automaticRules.values()) {
                 if (automaticRule.isActive()) {
                     if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
-                        // automatic rule triggered dnd and user hasn't seen update dnd dialog
-                        if (Settings.Secure.getInt(mContext.getContentResolver(),
-                                Settings.Secure.ZEN_SETTINGS_SUGGESTION_VIEWED, 1) == 0) {
-                            Settings.Secure.putInt(mContext.getContentResolver(),
-                                    Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 1);
-                        }
                         zen = automaticRule.zenMode;
                     }
                 }
@@ -2702,62 +2690,6 @@
         }
     }
 
-    private void showZenUpgradeNotification(int zen) {
-        final boolean isWatch = mContext.getPackageManager().hasSystemFeature(
-            PackageManager.FEATURE_WATCH);
-        final boolean showNotification = mIsSystemServicesReady
-                && zen != Global.ZEN_MODE_OFF
-                && !isWatch
-                && Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0
-                && Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.ZEN_SETTINGS_UPDATED, 0) != 1;
-
-        if (isWatch) {
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
-        }
-
-        if (showNotification) {
-            mNotificationManager.notify(TAG, SystemMessage.NOTE_ZEN_UPGRADE,
-                    createZenUpgradeNotification());
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
-        }
-    }
-
-    @VisibleForTesting
-    protected Notification createZenUpgradeNotification() {
-        final Bundle extras = new Bundle();
-        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
-                mContext.getResources().getString(R.string.global_action_settings));
-        int title = R.string.zen_upgrade_notification_title;
-        int content = R.string.zen_upgrade_notification_content;
-        int drawable = R.drawable.ic_zen_24dp;
-        if (NotificationManager.Policy.areAllVisualEffectsSuppressed(
-                getConsolidatedNotificationPolicy().suppressedVisualEffects)) {
-            title = R.string.zen_upgrade_notification_visd_title;
-            content = R.string.zen_upgrade_notification_visd_content;
-            drawable = R.drawable.ic_dnd_block_notifications;
-        }
-
-        Intent onboardingIntent = new Intent(Settings.ZEN_MODE_ONBOARDING);
-        onboardingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        return new Notification.Builder(mContext, SystemNotificationChannels.DO_NOT_DISTURB)
-                .setAutoCancel(true)
-                .setSmallIcon(R.drawable.ic_settings_24dp)
-                .setLargeIcon(Icon.createWithResource(mContext, drawable))
-                .setContentTitle(mContext.getResources().getString(title))
-                .setContentText(mContext.getResources().getString(content))
-                .setContentIntent(PendingIntent.getActivity(mContext, 0, onboardingIntent,
-                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
-                .setAutoCancel(true)
-                .setLocalOnly(true)
-                .addExtras(extras)
-                .setStyle(new Notification.BigTextStyle())
-                .build();
-    }
-
     private int drawableResNameToResId(String packageName, String resourceName) {
         if (TextUtils.isEmpty(resourceName)) {
             return 0;
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 89ced12..4665a72 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2208,10 +2208,10 @@
             return true;
         }
         boolean permissionGranted = requireFullPermission ? hasPermission(
-                Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                 : (hasPermission(
-                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
-                        || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, callingUid));
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                        || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS));
         if (!permissionGranted) {
             if (Process.isIsolatedUid(callingUid) && isKnownIsolatedComputeApp(callingUid)) {
                 return checkIsolatedOwnerHasPermission(callingUid, requireFullPermission);
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 1569fa0..02afdd6 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -80,6 +80,7 @@
 import com.android.server.pm.PackageDexOptimizer.DexOptResult;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.local.PackageManagerLocalImpl;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -819,10 +820,16 @@
         final PackageSetting ps = installRequest.getScannedPackageSetting();
         final String packageName = ps.getPackageName();
 
+        PackageSetting uncommittedPs = null;
+        if (Flags.improveInstallFreeze()) {
+            uncommittedPs = ps;
+        }
+
         PackageManagerLocal packageManagerLocal =
                 LocalManagerRegistry.getManager(PackageManagerLocal.class);
         try (PackageManagerLocal.FilteredSnapshot snapshot =
-                     packageManagerLocal.withFilteredSnapshot()) {
+                     PackageManagerLocalImpl.withFilteredSnapshot(packageManagerLocal,
+                uncommittedPs)) {
             boolean ignoreDexoptProfile =
                     (installRequest.getInstallFlags()
                             & PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE)
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index aca65bf..83292b7 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -148,6 +148,7 @@
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.ExceptionUtils;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
@@ -1014,13 +1015,17 @@
         final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
         try {
             CriticalEventLog.getInstance().logInstallPackagesStarted();
-
             if (prepareInstallPackages(requests)
                     && scanInstallPackages(requests, createdAppId, versionInfos)) {
                 List<ReconciledPackage> reconciledPackages =
                         reconcileInstallPackages(requests, versionInfos);
-                if (reconciledPackages != null
-                        && renameAndUpdatePaths(requests)
+                if (reconciledPackages == null) {
+                    return;
+                }
+                if (Flags.improveInstallFreeze()) {
+                    prepPerformDexoptIfNeeded(reconciledPackages);
+                }
+                if (renameAndUpdatePaths(requests)
                         && commitInstallPackages(reconciledPackages)) {
                     success = true;
                 }
@@ -1031,6 +1036,75 @@
         }
     }
 
+    private int[] getNewUsers(InstallRequest installRequest, int[] allUsers)
+            throws PackageManagerException {
+        final int userId = installRequest.getUserId();
+        if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT
+                && !mPm.mUserManager.exists(userId)) {
+            throw new PackageManagerException(PackageManagerException.INTERNAL_ERROR_MISSING_USER,
+                    "User " + userId + " doesn't exist or has been removed");
+        }
+
+        final IntArray newUserIds = new IntArray();
+        if (userId != UserHandle.USER_ALL) {
+            newUserIds.add(userId);
+        } else if (allUsers != null) {
+            final int[] installedForUsers = installRequest.getOriginUsers();
+            for (int currentUserId : allUsers) {
+                final boolean installedForCurrentUser = ArrayUtils.contains(
+                        installedForUsers, currentUserId);
+                final boolean restrictedByPolicy =
+                        mPm.isUserRestricted(currentUserId,
+                                UserManager.DISALLOW_INSTALL_APPS)
+                                || mPm.isUserRestricted(currentUserId,
+                                UserManager.DISALLOW_DEBUGGING_FEATURES);
+                if (installedForCurrentUser || !restrictedByPolicy) {
+                    newUserIds.add(currentUserId);
+                }
+            }
+        }
+
+        if (newUserIds.size() == 0) {
+            throw new PackageManagerException(PackageManagerException.INTERNAL_ERROR_MISSING_USER,
+                    "User " + userId + " doesn't exist or has been removed");
+        } else {
+            return newUserIds.toArray();
+        }
+    }
+
+    private void prepPerformDexoptIfNeeded(List<ReconciledPackage> reconciledPackages) {
+        for (ReconciledPackage reconciledPkg : reconciledPackages) {
+            final InstallRequest request = reconciledPkg.mInstallRequest;
+            // prepare profiles
+            final PackageSetting ps = request.getScannedPackageSetting();
+            final PackageSetting oldPkgSetting = request.getScanRequestOldPackageSetting();
+            final int[] allUsers = mPm.mUserManager.getUserIds();
+            if (reconciledPkg.mCollectedSharedLibraryInfos != null
+                    || (oldPkgSetting != null
+                    && !oldPkgSetting.getSharedLibraryDependencies().isEmpty())) {
+                // Reconcile if the new package or the old package uses shared libraries.
+                // It is possible that the old package uses shared libraries but the new
+                // one doesn't.
+                mSharedLibraries.executeSharedLibrariesUpdate(request.getParsedPackage(), ps,
+                        null, null, reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
+            }
+            try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
+                final int[] newUsers = getNewUsers(request, allUsers);
+                // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
+                mAppDataHelper.prepareAppDataPostCommitLIF(ps, 0, newUsers);
+                if (request.isClearCodeCache()) {
+                    mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL,
+                            FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
+                                    | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+                }
+            } catch (PackageManagerException e) {
+                request.setError(e.error, e.getMessage());
+                return;
+            }
+            DexOptHelper.performDexoptIfNeeded(request, mDexManager, mContext, null);
+        }
+    }
+
     private boolean renameAndUpdatePaths(List<InstallRequest> requests) {
         try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
             for (InstallRequest request : requests) {
@@ -2655,20 +2729,22 @@
                 incrementalStorages.add(storage);
             }
 
-            // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
-            mAppDataHelper.prepareAppDataPostCommitLIF(ps, 0, installRequest.getNewUsers());
-            if (installRequest.isClearCodeCache()) {
-                mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL,
-                        FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
-                                | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-            }
             if (installRequest.isInstallReplace() && pkg != null) {
                 mDexManager.notifyPackageUpdated(packageName,
                         pkg.getBaseApkPath(), pkg.getSplitCodePaths());
             }
+            if (!Flags.improveInstallFreeze()) {
+                // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
+                mAppDataHelper.prepareAppDataPostCommitLIF(ps, 0, installRequest.getNewUsers());
+                if (installRequest.isClearCodeCache()) {
+                    mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL,
+                            FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
+                                    | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+                }
 
-            DexOptHelper.performDexoptIfNeeded(installRequest, mDexManager, mContext,
-                    mPm.mInstallLock.getRawLock());
+                DexOptHelper.performDexoptIfNeeded(installRequest, mDexManager, mContext,
+                        mPm.mInstallLock.getRawLock());
+            }
         }
         PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
                 incrementalStorages);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 8657de2..5653da0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -716,7 +716,7 @@
                     visiblePackages.add(info.getActivityInfo().packageName);
                 }
                 final List<ApplicationInfo> installedPackages =
-                        mPackageManagerInternal.getInstalledApplicationsCrossUser(
+                        mPackageManagerInternal.getInstalledApplications(
                                 /* flags= */ 0, user.getIdentifier(), callingUid);
                 for (ApplicationInfo applicationInfo : installedPackages) {
                     if (!visiblePackages.contains(applicationInfo.packageName)) {
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index cf5de89..a28e3c1 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -244,7 +244,7 @@
                 return;
             }
             int registerUid = registerUser.getUid();
-            if (allowUids != null && registerUid != Process.SYSTEM_UID
+            if (allowUids != null && !UserHandle.isSameApp(registerUid, Process.SYSTEM_UID)
                     && !ArrayUtils.contains(allowUids, registerUid)) {
                 if (DEBUG) {
                     Slog.w(TAG, "Skip invoke PackageMonitorCallback for " + intent.getAction()
diff --git a/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java b/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
index 55afb17..c22e382 100644
--- a/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
+++ b/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
@@ -31,6 +31,7 @@
 import com.android.server.pm.PackageManagerLocal;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedUserApi;
 import com.android.server.pm.snapshot.PackageDataSnapshot;
 
@@ -71,8 +72,26 @@
     @NonNull
     @Override
     public FilteredSnapshotImpl withFilteredSnapshot(int callingUid, @NonNull UserHandle user) {
+        return withFilteredSnapshot(callingUid, user, /* uncommittedPs= */ null);
+    }
+
+    /**
+     * Creates a {@link FilteredSnapshot} with a uncommitted {@link PackageState} that is used for
+     * dexopt in the art service to get the correct package state before the package is committed.
+     */
+    @NonNull
+    public static FilteredSnapshotImpl withFilteredSnapshot(PackageManagerLocal pm,
+            @NonNull PackageState uncommittedPs) {
+        return ((PackageManagerLocalImpl) pm).withFilteredSnapshot(Binder.getCallingUid(),
+                Binder.getCallingUserHandle(), uncommittedPs);
+    }
+
+    @NonNull
+    private FilteredSnapshotImpl withFilteredSnapshot(int callingUid, @NonNull UserHandle user,
+            @Nullable PackageState uncommittedPs) {
         return new FilteredSnapshotImpl(callingUid, user,
-                mService.snapshotComputer(false /*allowLiveComputer*/), null);
+                mService.snapshotComputer(/* allowLiveComputer= */ false),
+                /* parentSnapshot= */ null, uncommittedPs);
     }
 
     @Override
@@ -145,7 +164,8 @@
 
         @Override
         public FilteredSnapshot filtered(int callingUid, @NonNull UserHandle user) {
-            return new FilteredSnapshotImpl(callingUid, user, mSnapshot, this);
+            return new FilteredSnapshotImpl(callingUid, user, mSnapshot, this,
+                    /* uncommittedPs= */ null);
         }
 
         @SuppressWarnings("RedundantSuppression")
@@ -209,13 +229,18 @@
         @Nullable
         private final UnfilteredSnapshotImpl mParentSnapshot;
 
+        @Nullable
+        private final PackageState mUncommitPackageState;
+
         private FilteredSnapshotImpl(int callingUid, @NonNull UserHandle user,
                 @NonNull PackageDataSnapshot snapshot,
-                @Nullable UnfilteredSnapshotImpl parentSnapshot) {
+                @Nullable UnfilteredSnapshotImpl parentSnapshot,
+                @Nullable PackageState uncommittedPs) {
             super(snapshot);
             mCallingUid = callingUid;
             mUserId = user.getIdentifier();
             mParentSnapshot = parentSnapshot;
+            mUncommitPackageState = uncommittedPs;
         }
 
         @Override
@@ -237,6 +262,10 @@
         @Override
         public PackageState getPackageState(@NonNull String packageName) {
             checkClosed();
+            if (mUncommitPackageState != null
+                    && packageName.equals(mUncommitPackageState.getPackageName())) {
+                return mUncommitPackageState;
+            }
             return mSnapshot.getPackageStateFiltered(packageName, mCallingUid, mUserId);
         }
 
@@ -250,6 +279,11 @@
                 var filteredPackageStates = new ArrayMap<String, PackageState>();
                 for (int index = 0, size = packageStates.size(); index < size; index++) {
                     var packageState = packageStates.valueAt(index);
+                    if (mUncommitPackageState != null
+                            && packageState.getPackageName().equals(
+                            mUncommitPackageState.getPackageName())) {
+                        packageState = (PackageStateInternal) mUncommitPackageState;
+                    }
                     if (!mSnapshot.shouldFilterApplication(packageState, mCallingUid, mUserId)) {
                         filteredPackageStates.put(packageStates.keyAt(index), packageState);
                     }
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 027e69c..66ec53e 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -667,9 +667,11 @@
     public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
         List<KeyboardShortcutInfo> shortcuts = new ArrayList();
         if (modifierShortcutManagerRefactor()) {
+            Context context = modifierShortcutManagerMultiuser()
+                    ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
             for (Bookmark b : mBookmarks.values()) {
                 KeyboardShortcutInfo info = shortcutInfoFromIntent(
-                        b.getShortcutChar(), b.getIntent(mContext), b.isShift());
+                        b.getShortcutChar(), b.getIntent(context), b.isShift());
                 if (info != null) {
                     shortcuts.add(info);
                 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 197f007..68e781f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -373,6 +373,7 @@
     //The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS
     static final int DOUBLE_PRESS_PRIMARY_NOTHING = 0;
     static final int DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP = 1;
+    static final int DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP = 2;
 
     // Must match: config_triplePressOnStemPrimaryBehavior in config.xml
     // The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS
@@ -1140,9 +1141,9 @@
                         + mShortPressOnPowerBehavior);
 
         if (count == 2) {
-            powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
+            powerMultiPressAction(displayId, eventTime, interactive, mDoublePressOnPowerBehavior);
         } else if (count == 3) {
-            powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
+            powerMultiPressAction(displayId, eventTime, interactive, mTriplePressOnPowerBehavior);
         } else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
             Slog.d(TAG, "No behavior defined for power press count " + count);
         } else if (count == 1 && shouldHandleShortPressPowerAction(interactive, eventTime)) {
@@ -1306,7 +1307,8 @@
         }
     }
 
-    private void powerMultiPressAction(long eventTime, boolean interactive, int behavior) {
+    private void powerMultiPressAction(int displayId, long eventTime, boolean interactive,
+            int behavior) {
         switch (behavior) {
             case MULTI_PRESS_POWER_NOTHING:
                 break;
@@ -1321,7 +1323,7 @@
                     Settings.Global.putInt(mContext.getContentResolver(),
                             Settings.Global.THEATER_MODE_ON, 0);
                     if (!interactive) {
-                        wakeUpFromWakeKey(eventTime, KEYCODE_POWER, /* isDown= */ false);
+                        wakeUpFromWakeKey(displayId, eventTime, KEYCODE_POWER, /* isDown= */ false);
                     }
                 } else {
                     Slog.i(TAG, "Toggling theater mode on.");
@@ -1337,7 +1339,7 @@
             case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
                 Slog.i(TAG, "Starting brightness boost.");
                 if (!interactive) {
-                    wakeUpFromWakeKey(eventTime, KEYCODE_POWER, /* isDown= */ false);
+                    wakeUpFromWakeKey(displayId, eventTime, KEYCODE_POWER, /* isDown= */ false);
                 }
                 mPowerManager.boostScreenBrightness(eventTime);
                 break;
@@ -1596,6 +1598,12 @@
                     performStemPrimaryDoublePressSwitchToRecentTask();
                 }
                 break;
+            case DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP:
+                final int stemPrimaryKeyDeviceId = INVALID_INPUT_DEVICE_ID;
+                handleKeyGestureInKeyGestureController(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
+                        stemPrimaryKeyDeviceId, KEYCODE_STEM_PRIMARY, /* metaState= */ 0);
+                break;
         }
     }
 
@@ -5539,7 +5547,7 @@
         if (mRequestedOrSleepingDefaultDisplay) {
             mCameraGestureTriggeredDuringGoingToSleep = true;
             // Wake device up early to prevent display doing redundant turning off/on stuff.
-            mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture();
+            mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture(event.getDisplayId());
         }
         return true;
     }
@@ -5637,8 +5645,8 @@
     public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
             long whenNanos, int policyFlags) {
         if ((policyFlags & FLAG_WAKE) != 0) {
-            if (mWindowWakeUpPolicy.wakeUpFromMotion(
-                        whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) {
+            if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
+                    action == MotionEvent.ACTION_DOWN)) {
                 // Woke up. Pass motion events to user.
                 return ACTION_PASS_TO_USER;
             }
@@ -5652,8 +5660,8 @@
         // there will be no dream to intercept the touch and wake into ambient.  The device should
         // wake up in this case.
         if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
-            if (mWindowWakeUpPolicy.wakeUpFromMotion(
-                        whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) {
+            if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
+                    action == MotionEvent.ACTION_DOWN)) {
                 // Woke up. Pass motion events to user.
                 return ACTION_PASS_TO_USER;
             }
@@ -5995,13 +6003,14 @@
             return;
         }
         wakeUpFromWakeKey(
+                event.getDisplayId(),
                 event.getEventTime(),
                 event.getKeyCode(),
                 event.getAction() == KeyEvent.ACTION_DOWN);
     }
 
-    private void wakeUpFromWakeKey(long eventTime, int keyCode, boolean isDown) {
-        if (mWindowWakeUpPolicy.wakeUpFromKey(eventTime, keyCode, isDown)) {
+    private void wakeUpFromWakeKey(int displayId, long eventTime, int keyCode, boolean isDown) {
+        if (mWindowWakeUpPolicy.wakeUpFromKey(displayId, eventTime, keyCode, isDown)) {
             final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER;
             // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
             if (shouldWakeUpWithHomeIntent() &&  keyCanLaunchHome) {
@@ -7244,6 +7253,8 @@
                 return "DOUBLE_PRESS_PRIMARY_NOTHING";
             case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
                 return "DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP";
+            case DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP:
+                return "DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP";
             default:
                 return Integer.toString(behavior);
         }
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
index af1ad13..966d84f4 100644
--- a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -25,6 +25,7 @@
 import static android.view.KeyEvent.KEYCODE_POWER;
 
 import static com.android.server.policy.Flags.supportInputWakeupDelegate;
+import static com.android.server.power.feature.flags.Flags.perDisplayWakeByTouch;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -107,13 +108,14 @@
     /**
      * Wakes up from a key event.
      *
+     * @param displayId the id of the display to wake.
      * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
      * @param keyCode the {@link android.view.KeyEvent} key code of the key event.
      * @param isDown {@code true} if the event's action is {@link KeyEvent#ACTION_DOWN}.
      * @return {@code true} if the policy allows the requested wake up and the request has been
      *      executed; {@code false} otherwise.
      */
-    boolean wakeUpFromKey(long eventTime, int keyCode, boolean isDown) {
+    boolean wakeUpFromKey(int displayId, long eventTime, int keyCode, boolean isDown) {
         final boolean wakeAllowedDuringTheaterMode =
                 keyCode == KEYCODE_POWER
                         ? mAllowTheaterModeWakeFromPowerKey
@@ -127,6 +129,7 @@
             return true;
         }
         wakeUp(
+                displayId,
                 eventTime,
                 keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
                 keyCode == KEYCODE_POWER ? "POWER" : "KEY");
@@ -136,12 +139,13 @@
     /**
      * Wakes up from a motion event.
      *
+     * @param displayId the id of the display to wake.
      * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
      * @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
      * @return {@code true} if the policy allows the requested wake up and the request has been
      *      executed; {@code false} otherwise.
      */
-    boolean wakeUpFromMotion(long eventTime, int source, boolean isDown) {
+    boolean wakeUpFromMotion(int displayId, long eventTime, int source, boolean isDown) {
         if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) {
             if (DEBUG) Slog.d(TAG, "Unable to wake up from motion.");
             return false;
@@ -150,7 +154,7 @@
                 && mInputWakeUpDelegate.wakeUpFromMotion(eventTime, source, isDown)) {
             return true;
         }
-        wakeUp(eventTime, WAKE_REASON_WAKE_MOTION, "MOTION");
+        wakeUp(displayId, eventTime, WAKE_REASON_WAKE_MOTION, "MOTION");
         return true;
     }
 
@@ -166,7 +170,7 @@
             if (DEBUG) Slog.d(TAG, "Unable to wake up from camera cover.");
             return false;
         }
-        wakeUp(eventTime, WAKE_REASON_CAMERA_LAUNCH, "CAMERA_COVER");
+        wakeUp(Display.DEFAULT_DISPLAY, eventTime, WAKE_REASON_CAMERA_LAUNCH, "CAMERA_COVER");
         return true;
     }
 
@@ -181,22 +185,24 @@
             if (DEBUG) Slog.d(TAG, "Unable to wake up from lid.");
             return false;
         }
-        wakeUp(mClock.uptimeMillis(), WAKE_REASON_LID, "LID");
+        wakeUp(Display.DEFAULT_DISPLAY, mClock.uptimeMillis(), WAKE_REASON_LID, "LID");
         return true;
     }
 
     /**
      * Wakes up to prevent sleeping when opening camera through power button.
      *
+     * @param displayId the id of the display to wake.
      * @return {@code true} if the policy allows the requested wake up and the request has been
      *      executed; {@code false} otherwise.
      */
-    boolean wakeUpFromPowerKeyCameraGesture() {
+    boolean wakeUpFromPowerKeyCameraGesture(int displayId) {
         if (!canWakeUp(mAllowTheaterModeWakeFromPowerKey)) {
             if (DEBUG) Slog.d(TAG, "Unable to wake up from power key camera gesture.");
             return false;
         }
-        wakeUp(mClock.uptimeMillis(), WAKE_REASON_CAMERA_LAUNCH, "CAMERA_GESTURE_PREVENT_LOCK");
+        wakeUp(displayId, mClock.uptimeMillis(), WAKE_REASON_CAMERA_LAUNCH,
+                "CAMERA_GESTURE_PREVENT_LOCK");
         return true;
     }
 
@@ -211,7 +217,7 @@
             if (DEBUG) Slog.d(TAG, "Unable to wake up from gesture.");
             return false;
         }
-        wakeUp(mClock.uptimeMillis(), WAKE_REASON_GESTURE, "GESTURE");
+        wakeUp(Display.DEFAULT_DISPLAY, mClock.uptimeMillis(), WAKE_REASON_GESTURE, "GESTURE");
         return true;
     }
 
@@ -234,7 +240,11 @@
     }
 
     /** Wakes up {@link PowerManager}. */
-    private void wakeUp(long wakeTime, @WakeReason int reason, String details) {
-        mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
+    private void wakeUp(int displayId, long wakeTime, @WakeReason int reason, String details) {
+        if (perDisplayWakeByTouch()) {
+            mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details, displayId);
+        } else {
+            mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index dc48242..c969eff 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -21,6 +21,7 @@
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
 import static com.android.server.power.hint.Flags.adpfSessionTag;
 import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
+import static com.android.server.power.hint.Flags.resetOnForkEnabled;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1057,6 +1058,11 @@
                     Slogf.w(TAG, errMsg);
                     throw new SecurityException(errMsg);
                 }
+                if (resetOnForkEnabled()){
+                    for (int tid : tids) {
+                        Process.setThreadScheduler(tid, Process.SCHED_RESET_ON_FORK, 0);
+                    }
+                }
 
                 if (adpfSessionTag() && tag == SessionTag.APP) {
                     // If the category of the app is a game,
@@ -1447,6 +1453,11 @@
                             Slogf.w(TAG, errMsg);
                             throw new SecurityException(errMsg);
                         }
+                        if (resetOnForkEnabled()){
+                            for (int tid : tids) {
+                                Process.setThreadScheduler(tid, Process.SCHED_RESET_ON_FORK, 0);
+                            }
+                        }
                         if (powerhintThreadCleanup()) {
                             synchronized (mNonIsolatedTidsLock) {
                                 for (int i = nonIsolated.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig
index 55afa05..e56b68c 100644
--- a/services/core/java/com/android/server/power/hint/flags.aconfig
+++ b/services/core/java/com/android/server/power/hint/flags.aconfig
@@ -14,3 +14,10 @@
     description: "Feature flag for adding session tag to hint session atom"
     bug: "345011125"
 }
+
+flag {
+    name: "reset_on_fork_enabled"
+    namespace: "game"
+    description: "Set reset_on_fork flag."
+    bug: "370988407"
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5372abe..2972771 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -230,6 +230,7 @@
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
 import static com.android.server.wm.StartingData.AFTER_TRANSACTION_COPY_TO_CLIENT;
+import static com.android.server.wm.StartingData.AFTER_TRANSACTION_IDLE;
 import static com.android.server.wm.StartingData.AFTER_TRANSACTION_REMOVE_DIRECTLY;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
@@ -286,6 +287,9 @@
 import android.app.servertransaction.TopResumedActivityChangeItem;
 import android.app.servertransaction.TransferSplashScreenViewStateItem;
 import android.app.usage.UsageEvents.Event;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -465,6 +469,11 @@
     // finished destroying itself.
     private static final int DESTROY_TIMEOUT = 10 * 1000;
 
+    @ChangeId
+    @Overridable
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415;
+
     final ActivityTaskManagerService mAtmService;
     final ActivityCallerState mCallerState;
     @NonNull
@@ -2635,8 +2644,10 @@
         if (finishing || !mHandleExitSplashScreen || mStartingSurface == null
                 || mStartingWindow == null
                 || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
-                // skip copy splash screen to client if it was resized
-                || (mStartingData != null && mStartingData.mResizedFromTransfer)
+                // Skip copy splash screen to client if it was resized, or the starting data already
+                // requested to be removed after transaction commit.
+                || (mStartingData != null && (mStartingData.mResizedFromTransfer
+                        || mStartingData.mRemoveAfterTransaction != AFTER_TRANSACTION_IDLE))
                 || isRelaunching()) {
             return false;
         }
@@ -3179,11 +3190,20 @@
      * will be ignored.
      */
     boolean isUniversalResizeable() {
-        return mWmService.mConstants.mIgnoreActivityOrientationRequest
-                && info.applicationInfo.category != ApplicationInfo.CATEGORY_GAME
-                // If the user preference respects aspect ratio, then it becomes non-resizable.
-                && !mAppCompatController.getAppCompatOverrides().getAppCompatAspectRatioOverrides()
-                        .shouldApplyUserMinAspectRatioOverride();
+        if (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME) {
+            return false;
+        }
+        final boolean compatEnabled = Flags.universalResizableByDefault()
+                && mDisplayContent != null && mDisplayContent.getConfiguration()
+                    .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+                && mDisplayContent.getIgnoreOrientationRequest()
+                && info.isChangeEnabled(UNIVERSAL_RESIZABLE_BY_DEFAULT);
+        if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) {
+            return false;
+        }
+        // If the user preference respects aspect ratio, then it becomes non-resizable.
+        return !mAppCompatController.getAppCompatOverrides().getAppCompatAspectRatioOverrides()
+                .shouldApplyUserMinAspectRatioOverride();
     }
 
     boolean isResizeable() {
@@ -8179,7 +8199,7 @@
     @ActivityInfo.ScreenOrientation
     protected int getOverrideOrientation() {
         int candidateOrientation = super.getOverrideOrientation();
-        if (isUniversalResizeable() && ActivityInfo.isFixedOrientation(candidateOrientation)) {
+        if (ActivityInfo.isFixedOrientation(candidateOrientation) && isUniversalResizeable()) {
             candidateOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
         }
         return mAppCompatController.getOrientationPolicy()
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 49380d4..87fa62a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2759,10 +2759,7 @@
             mInTask = null;
             // Launch ResolverActivity in the source task, so that it stays in the task bounds
             // when in freeform workspace.
-            // Also put noDisplay activities in the source task. These by itself can be placed
-            // in any task/root-task, however it could launch other activities like
-            // ResolverActivity, and we want those to stay in the original task.
-            if ((mStartActivity.isResolverOrDelegateActivity() || mStartActivity.noDisplay)
+            if (mStartActivity.isResolverOrDelegateActivity()
                     && mSourceRecord != null && mSourceRecord.inFreeformWindowingMode()) {
                 mAddingToTask = true;
             }
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index a74b006..4824c16 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.util.Log;
 import android.view.Surface;
@@ -128,7 +129,7 @@
         /**
          * Set the parameters to prepare the dim to be relative parented to the dimming container
          */
-        void prepareReparent(@NonNull WindowContainer<?> geometryParent,
+        void prepareReparent(@Nullable WindowContainer<?> geometryParent,
                 @NonNull WindowState relativeParent) {
             mAnimationHelper.setRequestedRelativeParent(relativeParent);
             mAnimationHelper.setRequestedGeometryParent(geometryParent);
@@ -221,7 +222,7 @@
      * @param dimmingContainer      The container that is dimming. The dim layer will be rel-z
      *                              parented below it
      */
-    public void adjustPosition(@NonNull WindowContainer<?> geometryParent,
+    public void adjustPosition(@Nullable WindowContainer<?> geometryParent,
                                     @NonNull WindowState dimmingContainer) {
         if (mDimState != null) {
             mDimState.prepareReparent(geometryParent, dimmingContainer);
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 298edae..3999e03 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -108,7 +108,7 @@
     }
 
     // Sets the requested layer to reparent the dim to without applying it immediately
-    void setRequestedGeometryParent(WindowContainer<?> geometryParent) {
+    void setRequestedGeometryParent(@Nullable WindowContainer<?> geometryParent) {
         if (geometryParent != null) {
             mRequestedProperties.mGeometryParent = geometryParent;
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0878912..66f9230 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5215,7 +5215,7 @@
             // but not window manager visible (!isVisibleNow()), it can still be the parent of the
             // dim, but can not create a new surface or continue a dim alone.
             Dimmer dimmer;
-            WindowContainer<?> geometryParent = task;
+            WindowContainer<?> geometryParent = null;
             if (Flags.useTasksDimOnly()) {
                 geometryParent = getDimParent();
                 dimmer = getDimController();
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index a07facf..776de2e 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -712,6 +712,12 @@
                         minOccurs="0" maxOccurs="unbounded">
                 <xs:annotation name="final"/>
             </xs:element>
+            <!-- The time after which the stylus is to be assumed to be not under use. This will
+             enable the logic of changing the brightness with ambient light changes -->
+            <xs:element name="idleStylusTimeoutMillis" type="xs:nonNegativeInteger"
+                minOccurs="0" maxOccurs="1">
+                <xs:annotation name="final"/>
+            </xs:element>
         </xs:sequence>
     </xs:complexType>
 
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 5309263..110a5a2 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -8,12 +8,14 @@
     method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
     method public final java.math.BigInteger getDarkeningLightDebounceMillis();
     method public boolean getEnabled();
+    method public final java.math.BigInteger getIdleStylusTimeoutMillis();
     method public final java.util.List<com.android.server.display.config.LuxToBrightnessMapping> getLuxToBrightnessMapping();
     method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
     method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
     method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
     method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
     method public void setEnabled(boolean);
+    method public final void setIdleStylusTimeoutMillis(java.math.BigInteger);
   }
 
   public enum AutoBrightnessModeName {
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 7d5532f..5c4716d 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -57,7 +57,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.doReturn
@@ -384,10 +383,6 @@
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
                 PackageManager.PERMISSION_GRANTED
             }
-            whenever(this.checkPermission(
-                eq(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt())) {
-                PackageManager.PERMISSION_GRANTED
-            }
         }
         val mockSharedLibrariesImpl: SharedLibrariesImpl = mock {
             whenever(this.snapshot()) { this@mock }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 8e1be9a..3976ea4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -157,6 +157,7 @@
                 .getIdleScreenRefreshRateTimeoutLuxThresholdPoint());
         assertNull(mDisplayDeviceConfig.getTempSensor().name);
         assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
+        assertEquals(0, mDisplayDeviceConfig.getIdleStylusTimeoutMillis());
     }
 
     @Test
@@ -253,6 +254,7 @@
                 .getLux().intValue());
         assertEquals(800, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(1)
                 .getTimeout().intValue());
+        assertEquals(1000, mDisplayDeviceConfig.getIdleStylusTimeoutMillis());
     }
 
     @Test
@@ -1479,6 +1481,7 @@
                 +               "</point>\n"
                 +           "</map>\n"
                 +       "</luxToBrightnessMapping>\n"
+                +       "<idleStylusTimeoutMillis>1000</idleStylusTimeoutMillis>\n"
                 +   "</autoBrightness>\n"
                 +  getPowerThrottlingConfig()
                 +   "<highBrightnessMode enabled=\"true\">\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 342c87a..6093a67 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -1309,6 +1309,38 @@
     }
 
     /**
+     * Tests that it's not allowed to create an auto-mirror virtual display without
+     * CAPTURE_VIDEO_OUTPUT permission or a virtual device that can mirror displays
+     */
+    @Test
+    public void createAutoMirrorDisplay_withoutPermissionOrAllowedVirtualDevice_throwsException()
+            throws Exception {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        registerDefaultDisplays(displayManager);
+        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
+        when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(false);
+        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
+        when(mContext.checkCallingPermission(CAPTURE_VIDEO_OUTPUT)).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+
+        final VirtualDisplayConfig.Builder builder =
+                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
+                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR)
+                        .setUniqueId("uniqueId --- mirror display");
+        assertThrows(SecurityException.class, () -> {
+            localService.createVirtualDisplay(
+                    builder.build(),
+                    mMockAppToken /* callback */,
+                    virtualDevice /* virtualDeviceToken */,
+                    mock(DisplayWindowPolicyController.class),
+                    PACKAGE_NAME);
+        });
+    }
+
+    /**
      * Tests that the virtual display is added to the default display group when created with
      * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
      */
@@ -1320,6 +1352,7 @@
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
         when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
         // Create an auto-mirror virtual display using a virtual device.
@@ -1352,6 +1385,7 @@
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
         when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
         // Create an auto-mirror virtual display using a virtual device.
@@ -1418,6 +1452,7 @@
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
         when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
         when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY))
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -1453,6 +1488,7 @@
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
         when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
         // Create an auto-mirror virtual display using a virtual device.
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index e863f15..e678acc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -39,6 +39,7 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Parcel;
 import android.os.Process;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
@@ -580,6 +581,50 @@
         assertTrue(mAppStartInfoTracker.mMonotonicClock.monotonicTime() >= originalMonotonicTime);
     }
 
+    /**
+     * Test to confirm that parcel read and write implementations match, correctly loading records
+     * with the same values and leaving no data unread.
+     */
+    @Test
+    public void testParcelReadWriteMatch() throws Exception {
+        // Create a start info records with all fields set.
+        ApplicationStartInfo startInfo = new ApplicationStartInfo(1234L);
+        startInfo.setPid(123);
+        startInfo.setRealUid(987);
+        startInfo.setPackageUid(654);
+        startInfo.setDefiningUid(321);
+        startInfo.setReason(ApplicationStartInfo.START_REASON_LAUNCHER);
+        startInfo.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN);
+        startInfo.setStartType(ApplicationStartInfo.START_TYPE_WARM);
+        startInfo.setLaunchMode(ApplicationStartInfo.LAUNCH_MODE_SINGLE_TOP);
+        startInfo.setPackageName(APP_1_PACKAGE_NAME);
+        startInfo.setProcessName(APP_1_PROCESS_NAME);
+        startInfo.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_LAUNCH, 999L);
+        startInfo.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME, 888L);
+        startInfo.setForceStopped(true);
+        startInfo.setStartComponent(ApplicationStartInfo.START_COMPONENT_OTHER);
+        startInfo.setIntent(buildIntent(COMPONENT));
+
+        // Write the start info to a parcel.
+        Parcel parcel = Parcel.obtain();
+        startInfo.writeToParcel(parcel, 0 /* flags */);
+
+        // Set the data position back to 0 so it's ready to be read.
+        parcel.setDataPosition(0);
+
+        // Now load the record from the parcel.
+        ApplicationStartInfo startInfoFromParcel = new ApplicationStartInfo(parcel);
+
+        // Make sure there is no unread data remaining in the parcel, and confirm that the loaded
+        // start info object is equal to the one it was written from. Check dataAvail first as if
+        // that check fails then the next check will fail too, but knowing the status of this check
+        // will tell us that we're missing a read or write. Check the objects are equals second as
+        // if the avail check passes and equals fails, then we know we're reading all the data just
+        // not to the correct fields.
+        assertEquals(0, parcel.dataAvail());
+        assertTrue(startInfo.equals(startInfoFromParcel));
+    }
+
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
         try {
             Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index 24e7242..54ee2a3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -33,6 +33,7 @@
 import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Process;
+import android.os.UserHandle;
 import android.util.SparseArray;
 
 import org.junit.After;
@@ -340,6 +341,24 @@
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
     }
 
+    @Test
+    public void registerPackageMonitor_callbackNotInAllowListSystemUidSecondUser_callbackIsCalled()
+            throws Exception {
+        IRemoteCallback callback = createMockPackageMonitorCallback();
+        int userId = 10;
+        int fakeAppId = 12345;
+        SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+        broadcastAllowList.put(userId, new int[]{UserHandle.getUid(userId, fakeAppId)});
+
+        mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, userId,
+                UserHandle.getUid(userId, Process.SYSTEM_UID));
+        mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+                FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{userId},
+                null /* instantUserIds */, broadcastAllowList, mHandler, null /* filterExtras */);
+
+        verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
+    }
+
     private IRemoteCallback createMockPackageMonitorCallback() {
         return spy(new IRemoteCallback.Stub() {
             @Override
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index cbe6700..6ede334 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -230,6 +230,20 @@
 }
 
 java_library {
+    name: "servicestests-utils-ravenwood",
+    srcs: [
+        "utils/**/*.java",
+        "utils/**/*.kt",
+        "utils-mockito/**/*.kt",
+    ],
+    libs: [
+        "android.test.runner.stubs.system",
+        "junit",
+        "mockito-ravenwood-prebuilt",
+    ],
+}
+
+java_library {
     name: "mockito-test-utils",
     srcs: [
         "utils-mockito/**/*.kt",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 1426d5d..c4b4afd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -69,6 +69,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
@@ -92,6 +93,8 @@
 import org.testng.Assert;
 
 import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 
 @RunWith(AndroidJUnit4.class)
 public class FullScreenMagnificationControllerTest {
@@ -1440,19 +1443,42 @@
 
     @Test
     public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
+        register(TEST_DISPLAY);
         final float persistedScale =
                 mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY);
 
         PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
-        mFullScreenMagnificationController.setScale(DISPLAY_0, 1.0f, pivotPoint.x, pivotPoint.y,
+        mFullScreenMagnificationController.setScale(TEST_DISPLAY, 1.0f, pivotPoint.x, pivotPoint.y,
                 false, SERVICE_ID_1);
         mFullScreenMagnificationController.persistScale(TEST_DISPLAY);
 
+        // persistScale may post a task to a background thread. Let's wait for it completes.
+        waitForBackgroundThread();
         Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY),
                 persistedScale);
     }
 
     @Test
+    public void persistScale_setValuesOnMultipleDisplays() {
+        register(DISPLAY_0);
+        register(DISPLAY_1);
+        final PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        mFullScreenMagnificationController.setScale(DISPLAY_0, 3.0f, pivotPoint.x, pivotPoint.y,
+                false, SERVICE_ID_1);
+        mFullScreenMagnificationController.persistScale(DISPLAY_0);
+        mFullScreenMagnificationController.setScale(DISPLAY_1, 4.0f, pivotPoint.x, pivotPoint.y,
+                false, SERVICE_ID_1);
+        mFullScreenMagnificationController.persistScale(DISPLAY_1);
+
+        // persistScale may post a task to a background thread. Let's wait for it completes.
+        waitForBackgroundThread();
+        Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(DISPLAY_0),
+                3.0f);
+        Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(DISPLAY_1),
+                4.0f);
+    }
+
+    @Test
     public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() {
         setScaleToMagnifying();
 
@@ -1494,6 +1520,15 @@
         );
     }
 
+    private static void waitForBackgroundThread() {
+        final CompletableFuture<Void> future = new CompletableFuture<>();
+        BackgroundThread.getHandler().post(() -> future.complete(null));
+        try {
+            future.get();
+        } catch (InterruptedException | ExecutionException ignore) {
+        }
+    }
+
     private void setScaleToMagnifying() {
         register(DISPLAY_0);
         float scale = 2.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 87fe6cf..6aa8a32 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -300,7 +300,8 @@
         mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
         mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
 
-        mMagnificationConnectionManager.setScale(TEST_DISPLAY, 10.0f);
+        mMagnificationConnectionManager.setScale(TEST_DISPLAY,
+                MagnificationScaleProvider.MAX_SCALE * 2.f);
 
         assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY),
                 MagnificationScaleProvider.MAX_SCALE);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index efc2d97..1bea371 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -217,7 +217,13 @@
         doNothing().when(mContext).enforceCallingOrSelfPermission(
                 eq(SET_BIOMETRIC_DIALOG_ADVANCED), any());
 
-        assertTrue(Utils.isValidAuthenticatorConfig(mContext, Authenticators.MANDATORY_BIOMETRICS));
+        if (Flags.mandatoryBiometrics()) {
+            assertTrue(Utils.isValidAuthenticatorConfig(mContext,
+                    Authenticators.MANDATORY_BIOMETRICS));
+        } else {
+            assertFalse(Utils.isValidAuthenticatorConfig(mContext,
+                    Authenticators.MANDATORY_BIOMETRICS));
+        }
 
         // The rest of the bits are not allowed to integrate with the public APIs
         for (int i = 8; i < 32; i++) {
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 ee63d5d..425bb15 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
@@ -33,6 +33,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -51,11 +52,15 @@
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
+import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions.LaunchCookie;
 import android.app.AppOpsManager;
+import android.app.Instrumentation;
 import android.app.KeyguardManager;
+import android.app.role.RoleManager;
+import android.companion.AssociationRequest;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -68,6 +73,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
@@ -88,6 +94,7 @@
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -98,6 +105,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -312,7 +320,6 @@
         assertThat(mService.getActiveProjectionInfo()).isNotNull();
     }
 
-    @SuppressLint("MissingPermission")
     @EnableFlags(android.companion.virtualdevice.flags
             .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
@@ -335,6 +342,36 @@
         assertThat(mService.getActiveProjectionInfo()).isNotNull();
     }
 
+    @EnableFlags(android.companion.virtualdevice.flags
+            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+    @Test
+    public void testCreateProjection_keyguardLocked_RoleHeld() {
+        runWithRole(AssociationRequest.DEVICE_PROFILE_APP_STREAMING, () -> {
+            try {
+                mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
+                doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
+                        any(ApplicationInfoFlags.class), any(UserHandle.class));
+                MediaProjectionManagerService.MediaProjection projection =
+                        mService.createProjectionInternal(Process.myUid(),
+                                mContext.getPackageName(),
+                                TYPE_MIRRORING, /* isPermanentGrant= */ false, UserHandle.CURRENT);
+                doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+                doReturn(PackageManager.PERMISSION_DENIED).when(
+                        mPackageManager).checkPermission(
+                        RECORD_SENSITIVE_CONTENT, projection.packageName);
+
+                projection.start(mIMediaProjectionCallback);
+                projection.notifyVirtualDisplayCreated(10);
+
+                // The projection was started because it was allowed to capture the keyguard.
+                assertWithMessage("Failed to run projection")
+                        .that(mService.getActiveProjectionInfo()).isNotNull();
+            } catch (NameNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
     @Test
     public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
             throws NameNotFoundException {
@@ -1202,6 +1239,47 @@
         return mService.getProjectionInternal(UID, PACKAGE_NAME);
     }
 
+    /**
+     * Run the provided block giving the current context's package the provided role.
+     */
+    @SuppressWarnings("SameParameterValue")
+    private void runWithRole(String role, Runnable block) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        String packageName = mContext.getPackageName();
+        UserHandle user = instrumentation.getTargetContext().getUser();
+        RoleManager roleManager = Objects.requireNonNull(
+                mContext.getSystemService(RoleManager.class));
+        try {
+            CountDownLatch latch = new CountDownLatch(1);
+            instrumentation.getUiAutomation().adoptShellPermissionIdentity(
+                    Manifest.permission.MANAGE_ROLE_HOLDERS,
+                    Manifest.permission.BYPASS_ROLE_QUALIFICATION);
+
+            roleManager.setBypassingRoleQualification(true);
+            roleManager.addRoleHolderAsUser(role, packageName, /*  flags = */ 0, user,
+                    mContext.getMainExecutor(), success -> {
+                        if (success) {
+                            latch.countDown();
+                        } else {
+                            Assert.fail("Couldn't set role for test (failure) " + role);
+                        }
+                    });
+            assertWithMessage("Couldn't set role for test (timeout) : " + role)
+                    .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
+            block.run();
+
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } finally {
+            roleManager.removeRoleHolderAsUser(role, packageName, 0, user,
+                    mContext.getMainExecutor(), (aBool) -> {
+                    });
+            roleManager.setBypassingRoleQualification(false);
+            instrumentation.getUiAutomation()
+                    .dropShellPermissionIdentity();
+        }
+    }
+
     private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
         CountDownLatch mLatch = new CountDownLatch(1);
         @Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index cf58d9b..c6cc941 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -70,6 +70,7 @@
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
 
+
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper
@@ -147,6 +148,147 @@
     }
 
     @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void testRuleDiff_toStringNoChangeAddRemove() throws Exception {
+        // Start with two identical rules
+        ZenModeConfig.ZenRule r1 = makeRule();
+        ZenModeConfig.ZenRule r2 = makeRule();
+
+        ZenModeDiff.RuleDiff d = new ZenModeDiff.RuleDiff(r1, r2);
+        assertThat(d.toString()).isEqualTo("ZenRuleDiff{no changes}");
+
+        d = new ZenModeDiff.RuleDiff(r1, null);
+        assertThat(d.toString()).isEqualTo("ZenRuleDiff{removed}");
+
+        d = new ZenModeDiff.RuleDiff(null, r2);
+        assertThat(d.toString()).isEqualTo("ZenRuleDiff{added}");
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void testRuleDiff_toString() throws Exception {
+        // Start with two identical rules
+        ZenModeConfig.ZenRule r1 = makeRule();
+        ZenModeConfig.ZenRule r2 = makeRule();
+
+        ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
+        ArrayMap<String, Object> expectedTo = new ArrayMap<>();
+        List<Field> fieldsForDiff = getFieldsForDiffCheck(
+                ZenModeConfig.ZenRule.class, getZenRuleExemptFields(), false);
+        generateFieldDiffs(r1, r2, fieldsForDiff, expectedFrom, expectedTo);
+
+        ZenModeDiff.RuleDiff d = new ZenModeDiff.RuleDiff(r1, r2);
+        assertThat(d.toString()).isEqualTo("ZenRuleDiff{"
+                + "enabled:true->false, "
+                + "conditionOverride:2->1, "
+                + "name:string1->string2, "
+                + "zenMode:2->1, "
+                + "conditionId:null->, "
+                + "condition:null->Condition["
+                + "state=STATE_TRUE,"
+                + "id=hello:,"
+                + "summary=,"
+                + "line1=,"
+                + "line2=,"
+                + "icon=-1,"
+                + "source=SOURCE_UNKNOWN,"
+                + "flags=2], "
+                + "component:null->ComponentInfo{b/b}, "
+                + "configurationActivity:null->ComponentInfo{a/a}, "
+                + "id:string1->string2, "
+                + "creationTime:200->100, "
+                + "enabler:string1->string2, "
+                + "zenPolicy:ZenPolicyDiff{"
+                + "mPriorityCategories_Reminders:1->2, "
+                + "mPriorityCategories_Events:1->2, "
+                + "mPriorityCategories_Messages:1->2, "
+                + "mPriorityCategories_Calls:1->2, "
+                + "mPriorityCategories_RepeatCallers:1->2, "
+                + "mPriorityCategories_Alarms:1->2, "
+                + "mPriorityCategories_Media:1->2, "
+                + "mPriorityCategories_System:1->2, "
+                + "mPriorityCategories_Conversations:1->2, "
+                + "mVisualEffects_FullScreenIntent:1->2, "
+                + "mVisualEffects_Lights:1->2, "
+                + "mVisualEffects_Peek:1->2, "
+                + "mVisualEffects_StatusBar:1->2, "
+                + "mVisualEffects_Badge:1->2, "
+                + "mVisualEffects_Ambient:1->2, "
+                + "mVisualEffects_NotificationList:1->2, "
+                + "mPriorityMessages:2->1, "
+                + "mPriorityCalls:2->1, "
+                + "mConversationSenders:2->1, "
+                + "mAllowChannels:2->1}, "
+                + "modified:true->false, "
+                + "pkg:string1->string2, "
+                + "zenDeviceEffects:ZenDeviceEffectsDiff{"
+                + "mGrayscale:true->false, "
+                + "mSuppressAmbientDisplay:true->false, "
+                + "mDimWallpaper:true->false, "
+                + "mNightMode:true->false, "
+                + "mDisableAutoBrightness:true->false, "
+                + "mDisableTapToWake:true->false, "
+                + "mDisableTiltToWake:true->false, "
+                + "mDisableTouch:true->false, "
+                + "mMinimizeRadioUsage:true->false, "
+                + "mMaximizeDoze:true->false, "
+                + "mExtraEffects:[effect1]->[effect2]}, "
+                + "triggerDescription:string1->string2, "
+                + "type:2->1, "
+                + "allowManualInvocation:true->false, "
+                + "iconResName:string1->string2, "
+                + "legacySuppressedEffects:2->1}");
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void testRuleDiff_toStringNullStartPolicy() throws Exception {
+        // Start with two identical rules
+        ZenModeConfig.ZenRule r1 = makeRule();
+        ZenModeConfig.ZenRule r2 = makeRule();
+
+        ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
+        ArrayMap<String, Object> expectedTo = new ArrayMap<>();
+        List<Field> fieldsForDiff = getFieldsForDiffCheck(
+                ZenModeConfig.ZenRule.class, getZenRuleExemptFields(), false);
+        generateFieldDiffs(r1, r2, fieldsForDiff, expectedFrom, expectedTo);
+
+        // Create a ZenRule with ZenDeviceEffects and ZenPolicy as null.
+        r1.zenPolicy = null;
+        r1.zenDeviceEffects = null;
+        ZenModeDiff.RuleDiff d = new ZenModeDiff.RuleDiff(r1, r2);
+        assertThat(d.toString()).isEqualTo("ZenRuleDiff{"
+                + "enabled:true->false, "
+                + "conditionOverride:2->1, "
+                + "name:string1->string2, "
+                + "zenMode:2->1, "
+                + "conditionId:null->, "
+                + "condition:null->Condition["
+                + "state=STATE_TRUE,"
+                + "id=hello:,"
+                + "summary=,"
+                + "line1=,"
+                + "line2=,"
+                + "icon=-1,"
+                + "source=SOURCE_UNKNOWN,"
+                + "flags=2], "
+                + "component:null->ComponentInfo{b/b}, "
+                + "configurationActivity:null->ComponentInfo{a/a}, "
+                + "id:string1->string2, "
+                + "creationTime:200->100, "
+                + "enabler:string1->string2, "
+                + "zenPolicy:ZenPolicyDiff{added}, "
+                + "modified:true->false, "
+                + "pkg:string1->string2, "
+                + "zenDeviceEffects:ZenDeviceEffectsDiff{added}, "
+                + "triggerDescription:string1->string2, "
+                + "type:2->1, "
+                + "allowManualInvocation:true->false, "
+                + "iconResName:string1->string2, "
+                + "legacySuppressedEffects:2->1}");
+    }
+
+    @Test
     public void testDeviceEffectsDiff_addRemoveSame() {
         // Test add, remove, and both sides same
         ZenDeviceEffects effects = new ZenDeviceEffects.Builder().build();
@@ -216,12 +358,18 @@
         generateFieldDiffs(effects1, effects2, fieldsForDiff, expectedFrom, expectedTo);
 
         d = new ZenModeDiff.DeviceEffectsDiff(effects1, effects2);
-        assertThat(d.toString()).isEqualTo("ZenDeviceEffectsDiff{mNightMode:true->false, "
-                + "mDisableTapToWake:true->false, mDisableAutoBrightness:true->false, "
-                + "mSuppressAmbientDisplay:true->false, mDisableTiltToWake:true->false, "
-                + "mGrayscale:true->false, mDisableTouch:true->false, mMaximizeDoze:true->false, "
-                + "mMinimizeRadioUsage:true->false, mExtraEffects:null->[], "
-                + "mDimWallpaper:true->false}");
+        assertThat(d.toString()).isEqualTo("ZenDeviceEffectsDiff{"
+                + "mGrayscale:true->false, "
+                + "mSuppressAmbientDisplay:true->false, "
+                + "mDimWallpaper:true->false, "
+                + "mNightMode:true->false, "
+                + "mDisableAutoBrightness:true->false, "
+                + "mDisableTapToWake:true->false, "
+                + "mDisableTiltToWake:true->false, "
+                + "mDisableTouch:true->false, "
+                + "mMinimizeRadioUsage:true->false, "
+                + "mMaximizeDoze:true->false, "
+                + "mExtraEffects:[effect1]->[effect2]}");
     }
 
 
@@ -307,17 +455,27 @@
         generateFieldDiffsForZenPolicy(policy1, policy2, fieldsForDiff, expectedFrom, expectedTo);
 
         d = new ZenModeDiff.PolicyDiff(policy1, policy2);
-        assertThat(d.toString()).isEqualTo("ZenPolicyDiff{mPriorityCalls:2->1, "
-                + "mVisualEffects_StatusBar:1->2, mPriorityCategories_RepeatCallers:1->2, "
-                + "mPriorityCategories_Calls:1->2, mPriorityCategories_Media:1->2, "
-                + "mConversationSenders:2->1, mPriorityCategories_Reminders:1->2, "
-                + "mVisualEffects_Badge:1->2, mPriorityCategories_Messages:1->2, "
-                + "mAllowChannels:2->1, mPriorityMessages:2->1, "
-                + "mVisualEffects_NotificationList:1->2, mVisualEffects_FullScreenIntent:1->2, "
-                + "mPriorityCategories_Alarms:1->2, mVisualEffects_Lights:1->2, "
-                + "mPriorityCategories_Events:1->2, mVisualEffects_Ambient:1->2, "
-                + "mPriorityCategories_System:1->2, mPriorityCategories_Conversations:1->2, "
-                + "mVisualEffects_Peek:1->2}");
+        assertThat(d.toString()).isEqualTo("ZenPolicyDiff{"
+                + "mPriorityCategories_Reminders:1->2, "
+                + "mPriorityCategories_Events:1->2, "
+                + "mPriorityCategories_Messages:1->2, "
+                + "mPriorityCategories_Calls:1->2, "
+                + "mPriorityCategories_RepeatCallers:1->2, "
+                + "mPriorityCategories_Alarms:1->2, "
+                + "mPriorityCategories_Media:1->2, "
+                + "mPriorityCategories_System:1->2, "
+                + "mPriorityCategories_Conversations:1->2, "
+                + "mVisualEffects_FullScreenIntent:1->2, "
+                + "mVisualEffects_Lights:1->2, "
+                + "mVisualEffects_Peek:1->2, "
+                + "mVisualEffects_StatusBar:1->2, "
+                + "mVisualEffects_Badge:1->2, "
+                + "mVisualEffects_Ambient:1->2, "
+                + "mVisualEffects_NotificationList:1->2, "
+                + "mPriorityMessages:2->1, "
+                + "mPriorityCalls:2->1, "
+                + "mConversationSenders:2->1, "
+                + "mAllowChannels:2->1}");
     }
 
     private static Set<String> getZenRuleExemptFields() {
@@ -701,6 +859,44 @@
                 expectedA.put(f.getName(), "string1");
                 f.set(b, "string2");
                 expectedB.put(f.getName(), "string2");
+            } else if (Set.class.equals(t)) {
+                Set<String> aSet = Set.of("effect1");
+                Set<String> bSet = Set.of("effect2");
+                f.set(a, aSet);
+                expectedA.put(f.getName(), aSet);
+                f.set(b, bSet);
+                expectedB.put(f.getName(), bSet);
+            } else if (ZenDeviceEffects.class.equals(t)) {
+                // Recurse into generating field diffs for ZenDeviceEffects.
+                ZenDeviceEffects effects1 = new ZenDeviceEffects.Builder().build();
+                ZenDeviceEffects effects2 = new ZenDeviceEffects.Builder().build();
+                // maps mapping field name -> expected output value as we set diffs
+                ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
+                ArrayMap<String, Object> expectedTo = new ArrayMap<>();
+
+                List<Field> fieldsForDiff = getFieldsForDiffCheck(
+                        ZenDeviceEffects.class, Collections.emptySet() /*no exempt fields*/, true);
+                generateFieldDiffs(effects1, effects2, fieldsForDiff, expectedFrom, expectedTo);
+                f.set(a, effects1);
+                expectedA.put(f.getName(), effects1);
+                f.set(b, effects2);
+                expectedB.put(f.getName(), effects2);
+            } else if (ZenPolicy.class.equals(t)) {
+                // Recurse into generating field diffs for ZenPolicy.
+                ZenPolicy policy1 = new ZenPolicy.Builder().build();
+                ZenPolicy policy2 = new ZenPolicy.Builder().build();
+                // maps mapping field name -> expected output value as we set diffs
+                ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
+                ArrayMap<String, Object> expectedTo = new ArrayMap<>();
+
+                List<Field> fieldsForDiff = getFieldsForDiffCheck(ZenPolicy.class,
+                        Collections.emptySet(), false);
+                generateFieldDiffsForZenPolicy(policy1, policy2, fieldsForDiff, expectedFrom,
+                        expectedTo);
+                f.set(a, policy1);
+                expectedA.put(f.getName(), policy1);
+                f.set(b, policy2);
+                expectedB.put(f.getName(), policy2);
             } else {
                 // catch-all for other types: have the field be "added"
                 f.set(a, null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index b997f5d..a49f5a8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -26,8 +26,6 @@
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
@@ -57,7 +55,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.server.UiServiceTestCase;
 
@@ -188,49 +185,6 @@
     }
 
     @Test
-    public void testSuppressDNDInfo_yes_VisEffectsAllowed() {
-        NotificationRecord r = getNotificationRecord();
-        when(r.getSbn().getPackageName()).thenReturn("android");
-        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects()
-                - SUPPRESSED_EFFECT_STATUS_BAR, 0);
-
-        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
-    }
-
-    @Test
-    public void testSuppressDNDInfo_yes_WrongId() {
-        NotificationRecord r = getNotificationRecord();
-        when(r.getSbn().getPackageName()).thenReturn("android");
-        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION);
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
-
-        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
-    }
-
-    @Test
-    public void testSuppressDNDInfo_yes_WrongPackage() {
-        NotificationRecord r = getNotificationRecord();
-        when(r.getSbn().getPackageName()).thenReturn("android2");
-        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
-
-        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
-    }
-
-    @Test
-    public void testSuppressDNDInfo_no() {
-        NotificationRecord r = getNotificationRecord();
-        when(r.getSbn().getPackageName()).thenReturn("android");
-        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
-
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_ALARMS, policy, r));
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
-    }
-
-    @Test
     public void testSuppressAnything_yes_ZenModeOff() {
         NotificationRecord r = getNotificationRecord();
         when(r.getSbn().getPackageName()).thenReturn("bananas");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index d4cba8d..294027b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -169,7 +169,6 @@
 
 import com.android.internal.R;
 import com.android.internal.config.sysui.TestableFlagResolver;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.os.AtomsProto;
@@ -747,54 +746,6 @@
     }
 
     @Test
-    public void testZenUpgradeNotification() {
-        /**
-         * Commit a485ec65b5ba947d69158ad90905abf3310655cf disabled DND status change
-         * notification on watches. So, assume that the device is not watch.
-         */
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(false);
-
-        // shows zen upgrade notification if stored settings says to shows,
-        // zen has not been updated, boot is completed
-        // and we're setting zen mode on
-        Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
-        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
-        mZenModeHelper.mIsSystemServicesReady = true;
-        mZenModeHelper.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
-        mZenModeHelper.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-
-        verify(mNotificationManager, times(1)).notify(eq(ZenModeHelper.TAG),
-                eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
-        assertEquals(0, Settings.Secure.getInt(mContentResolver,
-                Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, -1));
-    }
-
-    @Test
-    public void testNoZenUpgradeNotification() {
-        // doesn't show upgrade notification if stored settings says don't show
-        Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
-        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
-        mZenModeHelper.mIsSystemServicesReady = true;
-        mZenModeHelper.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-
-        verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
-                eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
-    }
-
-    @Test
-    public void testNoZenUpgradeNotificationZenUpdated() {
-        // doesn't show upgrade notification since zen was already updated
-        Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
-        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 1);
-        mZenModeHelper.mIsSystemServicesReady = true;
-        mZenModeHelper.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-
-        verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
-                eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
-    }
-
-    @Test
     public void testZenSetInternalRinger_AllPriorityNotificationSoundsMuted() {
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelper.mAudioManager = mAudioManager;
@@ -3032,6 +2983,33 @@
     }
 
     @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void updateAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() {
+        ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
+        sleepingRule.enabled = false;
+        sleepingRule.userModifiedFields = 0;
+        sleepingRule.name = "ZZZZZZZ...";
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.mConfig.automaticRules.put(sleepingRule.id, sleepingRule);
+
+        AutomaticZenRule futureBedtime = new AutomaticZenRule.Builder("Bedtime (?)", CONDITION_ID)
+                .build();
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(mPkg, futureBedtime,
+                ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.mConfig.automaticRules.keySet())
+                .containsExactly(sleepingRule.id, bedtimeRuleId);
+
+        AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime (!)", CONDITION_ID)
+                .setType(TYPE_BEDTIME)
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(bedtimeRuleId, bedtime, ORIGIN_APP, "reason",
+                CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(bedtimeRuleId);
+    }
+
+    @Test
     @EnableFlags(FLAG_MODES_API)
     public void testSetManualZenMode() {
         setupZenConfig();
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index 9b92ff4..3ea3235 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -23,6 +23,7 @@
 import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.policy.PhoneWindowManager.DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP;
 import static com.android.server.policy.PhoneWindowManager.DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
 import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
@@ -32,6 +33,7 @@
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.ComponentName;
+import android.hardware.input.KeyGestureEvent;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.view.Display;
@@ -236,6 +238,19 @@
     }
 
     @Test
+    public void stemDoubleKey_behaviorIsLaunchFitness_gestureEventFired() {
+        overrideBehavior(
+                STEM_PRIMARY_BUTTON_DOUBLE_PRESS, DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP);
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+
+        sendKey(KEYCODE_STEM_PRIMARY);
+        sendKey(KEYCODE_STEM_PRIMARY);
+
+        mPhoneWindowManager.assertKeyGestureEventSentToKeyGestureController(
+                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS);
+    }
+
+    @Test
     public void stemTripleKey_EarlyShortPress_AllAppsThenBackToOriginalThenToggleA11y()
             throws RemoteException {
         overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 1aa9087..3dc893a 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -700,7 +700,7 @@
     void assertPowerWakeUp() {
         mTestLooper.dispatchAll();
         verify(mWindowWakeUpPolicy)
-                .wakeUpFromKey(anyLong(), eq(KeyEvent.KEYCODE_POWER), anyBoolean());
+                .wakeUpFromKey(anyInt(), anyLong(), eq(KeyEvent.KEYCODE_POWER), anyBoolean());
     }
 
     void assertNoPowerSleep() {
@@ -919,4 +919,9 @@
         mTestLooper.dispatchAll();
         Assert.assertEquals(expectEnabled, mIsTalkBackEnabled);
     }
+
+    void assertKeyGestureEventSentToKeyGestureController(int gestureType) {
+        verify(mInputManagerInternal)
+                .handleKeyGestureInKeyGestureController(anyInt(), any(), anyInt(), eq(gestureType));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
index 7322e5a..4119ad3 100644
--- a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
@@ -35,6 +35,7 @@
 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch;
 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture;
 import static com.android.server.policy.Flags.FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE;
+import static com.android.server.power.feature.flags.Flags.FLAG_PER_DISPLAY_WAKE_BY_TOUCH;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -43,6 +44,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -52,6 +54,8 @@
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.os.PowerManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.view.Display;
@@ -125,6 +129,7 @@
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testMotionWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
         setTheaterModeEnabled(false);
         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
@@ -136,7 +141,8 @@
 
         // Verify the policy wake up call succeeds because of the call on the delegate, and not
         // because of a PowerManager wake up.
-        assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isTrue();
+        assertThat(mPolicy.wakeUpFromMotion(
+                mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isTrue();
         verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true);
         verifyNoPowerManagerWakeUp();
 
@@ -144,12 +150,14 @@
 
         // Verify the policy wake up call succeeds because of the PowerManager wake up, since the
         // delegate would not handle the wake up request.
-        assertThat(mPolicy.wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false)).isTrue();
+        assertThat(mPolicy.wakeUpFromMotion(
+                mDefaultDisplay.getDisplayId(), 300, SOURCE_ROTARY_ENCODER, false)).isTrue();
         verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false);
         verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testKeyWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
         setTheaterModeEnabled(false);
         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
@@ -161,7 +169,8 @@
 
         // Verify the policy wake up call succeeds because of the call on the delegate, and not
         // because of a PowerManager wake up.
-        assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isTrue();
+        assertThat(mPolicy.wakeUpFromKey(
+                mDefaultDisplay.getDisplayId(), 200, KEYCODE_POWER, true)).isTrue();
         verify(mInputWakeUpDelegate).wakeUpFromKey(200, KEYCODE_POWER, true);
         verifyNoPowerManagerWakeUp();
 
@@ -169,7 +178,8 @@
 
         // Verify the policy wake up call succeeds because of the PowerManager wake up, since the
         // delegate would not handle the wake up request.
-        assertThat(mPolicy.wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false)).isTrue();
+        assertThat(mPolicy.wakeUpFromKey(
+                mDefaultDisplay.getDisplayId(), 300, KEYCODE_STEM_PRIMARY, false)).isTrue();
         verify(mInputWakeUpDelegate).wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false);
         verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_KEY, "android.policy:KEY");
     }
@@ -186,7 +196,8 @@
                 .setInputWakeUpDelegate(mInputWakeUpDelegate);
 
         // Check that the wake up does not happen because the theater mode policy check fails.
-        assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isFalse();
+        assertThat(mPolicy.wakeUpFromKey(
+                mDefaultDisplay.getDisplayId(), 200, KEYCODE_POWER, true)).isFalse();
         verify(mInputWakeUpDelegate, never()).wakeUpFromKey(anyLong(), anyInt(), anyBoolean());
     }
 
@@ -201,11 +212,13 @@
                 .setInputWakeUpDelegate(mInputWakeUpDelegate);
 
         // Check that the wake up does not happen because the theater mode policy check fails.
-        assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isFalse();
+        assertThat(mPolicy.wakeUpFromMotion(
+                mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isFalse();
         verify(mInputWakeUpDelegate, never()).wakeUpFromMotion(anyLong(), anyInt(), anyBoolean());
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testTheaterModeChecksNotAppliedWhenScreenIsOn() {
         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
         setDefaultDisplayState(Display.STATE_ON);
@@ -213,30 +226,69 @@
         setBooleanRes(config_allowTheaterModeWakeFromMotion, false);
         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
 
-        mPolicy.wakeUpFromMotion(200L, SOURCE_TOUCHSCREEN, true);
+        mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(), 200L, SOURCE_TOUCHSCREEN, true);
 
         verify(mPowerManager).wakeUp(200L, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testWakeUpFromMotion() {
         runPowerManagerUpChecks(
-                () -> mPolicy.wakeUpFromMotion(mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
+                () -> mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(),
+                        mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
                 config_allowTheaterModeWakeFromMotion,
                 WAKE_REASON_WAKE_MOTION,
                 "android.policy:MOTION");
     }
 
     @Test
+    @EnableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
+    public void testWakeUpFromMotion_perDisplayWakeByTouchEnabled() {
+        setTheaterModeEnabled(false);
+        final int displayId = 555;
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+
+        boolean displayWokeUp = mPolicy.wakeUpFromMotion(
+                displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+
+        // Verify that display is woken up
+        assertThat(displayWokeUp).isTrue();
+        verify(mPowerManager).wakeUp(anyLong(), eq(WAKE_REASON_WAKE_MOTION),
+                eq("android.policy:MOTION"), eq(displayId));
+    }
+
+    @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
+    public void testWakeUpFromMotion_perDisplayWakeByTouchDisabled() {
+        setTheaterModeEnabled(false);
+        final int displayId = 555;
+        mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+
+        boolean displayWokeUp = mPolicy.wakeUpFromMotion(
+                displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+
+        // Verify that power is woken up and display isn't woken up individually
+        assertThat(displayWokeUp).isTrue();
+        verify(mPowerManager).wakeUp(
+                anyLong(), eq(WAKE_REASON_WAKE_MOTION), eq("android.policy:MOTION"));
+        verify(mPowerManager, never()).wakeUp(anyLong(), eq(WAKE_REASON_WAKE_MOTION),
+                eq("android.policy:MOTION"), eq(displayId));
+    }
+
+    @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testWakeUpFromKey_nonPowerKey() {
         runPowerManagerUpChecks(
-                () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_HOME, true),
+                () -> mPolicy.wakeUpFromKey(
+                        mDefaultDisplay.getDisplayId(), mClock.uptimeMillis(), KEYCODE_HOME, true),
                 config_allowTheaterModeWakeFromKey,
                 WAKE_REASON_WAKE_KEY,
                 "android.policy:KEY");
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testWakeUpFromKey_powerKey() {
         // Disable the resource affecting all wake keys because it affects power key as well.
         // That way, power key wake during theater mode will solely be controlled by
@@ -245,7 +297,8 @@
 
         // Test with power key
         runPowerManagerUpChecks(
-                () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, true),
+                () -> mPolicy.wakeUpFromKey(
+                        mDefaultDisplay.getDisplayId(), mClock.uptimeMillis(), KEYCODE_POWER, true),
                 config_allowTheaterModeWakeFromPowerKey,
                 WAKE_REASON_POWER_BUTTON,
                 "android.policy:POWER");
@@ -254,13 +307,15 @@
         // even if the power-key specific theater mode config is disabled.
         setBooleanRes(config_allowTheaterModeWakeFromPowerKey, false);
         runPowerManagerUpChecks(
-                () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, false),
+                () -> mPolicy.wakeUpFromKey(mDefaultDisplay.getDisplayId(), mClock.uptimeMillis(),
+                        KEYCODE_POWER, false),
                 config_allowTheaterModeWakeFromKey,
                 WAKE_REASON_POWER_BUTTON,
                 "android.policy:POWER");
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testWakeUpFromLid() {
         runPowerManagerUpChecks(
                 () -> mPolicy.wakeUpFromLid(),
@@ -270,6 +325,7 @@
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testWakeUpFromWakeGesture() {
         runPowerManagerUpChecks(
                 () -> mPolicy.wakeUpFromWakeGesture(),
@@ -279,6 +335,7 @@
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testwakeUpFromCameraCover() {
         runPowerManagerUpChecks(
                 () -> mPolicy.wakeUpFromCameraCover(mClock.uptimeMillis()),
@@ -288,6 +345,7 @@
     }
 
     @Test
+    @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
     public void testWakeUpFromPowerKeyCameraGesture() {
         // Disable the resource affecting all wake keys because it affects power key as well.
         // That way, power key wake during theater mode will solely be controlled by
@@ -295,7 +353,7 @@
         setBooleanRes(config_allowTheaterModeWakeFromKey, false);
 
         runPowerManagerUpChecks(
-                () -> mPolicy.wakeUpFromPowerKeyCameraGesture(),
+                () -> mPolicy.wakeUpFromPowerKeyCameraGesture(mDefaultDisplay.getDisplayId()),
                 config_allowTheaterModeWakeFromPowerKey,
                 WAKE_REASON_CAMERA_LAUNCH,
                 "android.policy:CAMERA_GESTURE_PREVENT_LOCK");
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index adc969c..72f4fa91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4881,6 +4881,25 @@
         assertNotEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOverrideOrientation());
     }
 
+
+    @Test
+    @EnableCompatChanges({ActivityRecord.UNIVERSAL_RESIZABLE_BY_DEFAULT})
+    public void testUniversalResizeableByDefault() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT);
+        mDisplayContent.setIgnoreOrientationRequest(false);
+        setUpApp(mDisplayContent);
+        assertFalse(mActivity.isUniversalResizeable());
+
+        mDisplayContent.setIgnoreOrientationRequest(true);
+        final int swDp = mDisplayContent.getConfiguration().smallestScreenWidthDp;
+        if (swDp < WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP) {
+            final int height = 100 + (int) (mDisplayContent.getDisplayMetrics().density
+                    * WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP);
+            resizeDisplay(mDisplayContent, 100 + height, height);
+        }
+        assertTrue(mActivity.isUniversalResizeable());
+    }
+
     @Test
     public void testClearSizeCompat_resetOverrideConfig() {
         final int origDensity = 480;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ff302f6..ff966ae 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2298,13 +2298,9 @@
      *
      * See {@link #getImei(int)} for details on the required permissions and behavior
      * when the caller does not hold sufficient permissions.
-     *
-     * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     public String getImei() {
         return getImei(getSlotIndex());
     }
@@ -2343,13 +2339,9 @@
      * </ul>
      *
      * @param slotIndex of which IMEI is returned
-     *
-     * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     public String getImei(int slotIndex) {
         ITelephony telephony = getITelephony();
         if (telephony == null) return null;
@@ -2366,11 +2358,7 @@
     /**
      * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
      * available.
-     *
-     * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     @Nullable
     public String getTypeAllocationCode() {
         return getTypeAllocationCode(getSlotIndex());
@@ -2381,11 +2369,7 @@
      * available.
      *
      * @param slotIndex of which Type Allocation Code is returned
-     *
-     * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     @Nullable
     public String getTypeAllocationCode(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -19378,12 +19362,9 @@
      * </ul>
      *
      * @return Primary IMEI of type string
-     * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
     @NonNull
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     public String getPrimaryImei() {
         try {
             ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index bd5c759..49ca6f3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -249,6 +249,13 @@
     public static final String KEY_PROVISION_SATELLITE_TOKENS = "provision_satellite";
 
     /**
+     * Bundle key to get the response from
+     * {@link #deprovisionSatellite(List, Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_DEPROVISION_SATELLITE_TOKENS = "deprovision_satellite";
+
+    /**
      * The request was successfully processed.
      */
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@@ -2791,6 +2798,61 @@
         }
     }
 
+    /**
+     * Deliver the list of deprovisioned satellite subscriber infos.
+     *
+     * @param list The list of deprovisioned satellite subscriber infos.
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void deprovisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_DEPROVISION_SATELLITE_TOKENS)) {
+                                boolean isUpdated =
+                                        resultData.getBoolean(KEY_DEPROVISION_SATELLITE_TOKENS);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isUpdated)));
+                            } else {
+                                loge("KEY_DEPROVISION_SATELLITE_TOKENS does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.deprovisionSatellite(list, receiver);
+            } else {
+                loge("deprovisionSatellite() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("deprovisionSatellite() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
     @Nullable
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3161d17..61f0146 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3444,4 +3444,15 @@
      */
     boolean overrideCarrierRoamingNtnEligibilityChanged(
             in boolean status, in boolean resetRequired);
+
+    /**
+     * Deliver the list of deprovisioned satellite subscriber infos.
+     *
+     * @param list The list of deprovisioned satellite subscriber infos.
+     * @param result The result receiver that returns whether deliver success or fail.
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void deprovisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result);
 }
diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java
index 9d56a92..8ecddaa 100644
--- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.protolog;
 
+import static org.junit.Assert.assertThrows;
+
 import android.platform.test.annotations.Presubmit;
 
 import com.android.internal.protolog.common.IProtoLogGroup;
@@ -44,8 +46,29 @@
                 .containsExactly(TEST_GROUP_1, TEST_GROUP_2);
     }
 
+    @Test
+    public void throwOnRegisteringDuplicateGroup() {
+        final var assertion = assertThrows(RuntimeException.class,
+                () -> ProtoLog.init(TEST_GROUP_1, TEST_GROUP_1, TEST_GROUP_2));
+
+        Truth.assertThat(assertion).hasMessageThat().contains("" + TEST_GROUP_1.getId());
+        Truth.assertThat(assertion).hasMessageThat().contains("duplicate");
+    }
+
+    @Test
+    public void throwOnRegisteringGroupsWithIdCollisions() {
+        final var assertion = assertThrows(RuntimeException.class,
+                () -> ProtoLog.init(TEST_GROUP_1, TEST_GROUP_WITH_COLLISION, TEST_GROUP_2));
+
+        Truth.assertThat(assertion).hasMessageThat()
+            .contains("" + TEST_GROUP_WITH_COLLISION.getId());
+        Truth.assertThat(assertion).hasMessageThat().contains("collision");
+    }
+
     private static final IProtoLogGroup TEST_GROUP_1 = new ProtoLogGroup("TEST_TAG_1", 1);
     private static final IProtoLogGroup TEST_GROUP_2 = new ProtoLogGroup("TEST_TAG_2", 2);
+    private static final IProtoLogGroup TEST_GROUP_WITH_COLLISION =
+            new ProtoLogGroup("TEST_TAG_WITH_COLLISION", 1);
 
     private static class ProtoLogGroup implements IProtoLogGroup {
         private final boolean mEnabled;
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 81814b6..7bc9970 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 
 import android.net.NetworkCapabilities;
 import android.net.wifi.WifiConfiguration;
@@ -39,6 +40,7 @@
     private static final int SUB_ID = 1;
     private static final int NETWORK_ID = 5;
     private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120;
+    private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID = 119;
     private static final WifiInfo WIFI_INFO =
             new WifiInfo.Builder().setNetworkId(NETWORK_ID).build();
 
@@ -48,6 +50,27 @@
             new VcnTransportInfo(WIFI_INFO, MIN_UDP_PORT_4500_NAT_TIMEOUT);
 
     @Test
+    public void testBuilder() {
+        final VcnTransportInfo transportInfo =
+                new VcnTransportInfo.Builder()
+                        .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT)
+                        .build();
+
+        assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT, transportInfo.getMinUdpPort4500NatTimeoutSeconds());
+    }
+
+    @Test
+    public void testBuilder_withInvalidNatTimeout() {
+        try {
+            new VcnTransportInfo.Builder()
+                    .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID);
+            fail("Expected to fail due to invalid NAT timeout");
+        } catch (Exception expected) {
+        }
+    }
+
+    @Test
     public void testGetWifiInfo() {
         assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
 
diff --git a/tests/vcn/java/android/net/vcn/VcnUtilsTest.java b/tests/vcn/java/android/net/vcn/VcnUtilsTest.java
new file mode 100644
index 0000000..3ce6c8f
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUtilsTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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 android.net.vcn;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.wifi.WifiInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+public class VcnUtilsTest {
+    private static final int SUB_ID = 1;
+
+    private static final WifiInfo WIFI_INFO = new WifiInfo.Builder().build();
+    private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
+            new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build();
+    private static final VcnTransportInfo VCN_TRANSPORT_INFO =
+            new VcnTransportInfo.Builder().build();
+
+    private ConnectivityManager mMockConnectivityManager;
+    private Network mMockWifiNetwork;
+    private Network mMockCellNetwork;
+
+    private NetworkCapabilities mVcnCapsWithUnderlyingWifi;
+    private NetworkCapabilities mVcnCapsWithUnderlyingCell;
+
+    @Before
+    public void setUp() {
+        mMockConnectivityManager = mock(ConnectivityManager.class);
+
+        mMockWifiNetwork = mock(Network.class);
+        mVcnCapsWithUnderlyingWifi = newVcnCaps(VCN_TRANSPORT_INFO, mMockWifiNetwork);
+        final NetworkCapabilities wifiCaps =
+                new NetworkCapabilities.Builder()
+                        .addTransportType(TRANSPORT_WIFI)
+                        .setTransportInfo(WIFI_INFO)
+                        .build();
+        when(mMockConnectivityManager.getNetworkCapabilities(mMockWifiNetwork))
+                .thenReturn(wifiCaps);
+
+        mMockCellNetwork = mock(Network.class);
+        mVcnCapsWithUnderlyingCell = newVcnCaps(VCN_TRANSPORT_INFO, mMockCellNetwork);
+        final NetworkCapabilities cellCaps =
+                new NetworkCapabilities.Builder()
+                        .addTransportType(TRANSPORT_CELLULAR)
+                        .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+                        .build();
+        when(mMockConnectivityManager.getNetworkCapabilities(mMockCellNetwork))
+                .thenReturn(cellCaps);
+    }
+
+    private static NetworkCapabilities newVcnCaps(
+            VcnTransportInfo vcnTransportInfo, Network underlyingNetwork) {
+        return new NetworkCapabilities.Builder()
+                .setTransportInfo(vcnTransportInfo)
+                .setUnderlyingNetworks(Collections.singletonList(underlyingNetwork))
+                .build();
+    }
+
+    @Test
+    public void getWifiInfoFromVcnCaps() {
+        assertEquals(
+                WIFI_INFO,
+                VcnUtils.getWifiInfoFromVcnCaps(
+                        mMockConnectivityManager, mVcnCapsWithUnderlyingWifi));
+    }
+
+    @Test
+    public void getWifiInfoFromVcnCaps_onVcnWithUnderlyingCell() {
+        assertNull(
+                VcnUtils.getWifiInfoFromVcnCaps(
+                        mMockConnectivityManager, mVcnCapsWithUnderlyingCell));
+    }
+
+    @Test
+    public void getSubIdFromVcnCaps() {
+        assertEquals(
+                SUB_ID,
+                VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingCell));
+    }
+
+    @Test
+    public void getSubIdFromVcnCaps_onVcnWithUnderlyingWifi() {
+        assertEquals(
+                INVALID_SUBSCRIPTION_ID,
+                VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingWifi));
+    }
+
+    @Test
+    public void getSubIdFromVcnCaps_onNonVcnNetwork() {
+        assertEquals(
+                INVALID_SUBSCRIPTION_ID,
+                VcnUtils.getSubIdFromVcnCaps(
+                        mMockConnectivityManager, new NetworkCapabilities.Builder().build()));
+    }
+
+    @Test
+    public void getSubIdFromVcnCaps_withMultipleUnderlyingNetworks() {
+        final NetworkCapabilities vcnCaps =
+                new NetworkCapabilities.Builder(mVcnCapsWithUnderlyingCell)
+                        .setUnderlyingNetworks(
+                                Arrays.asList(
+                                        new Network[] {mMockCellNetwork, mock(Network.class)}))
+                        .build();
+        assertEquals(SUB_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, vcnCaps));
+    }
+}