Merge "Add first widget Preference for SPA"
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 40a3c92..448ee61 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -316,8 +316,6 @@
                 mIam.startUserInBackground(userId);
             }, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
 
-            waitForBroadcastIdle();
-
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -680,8 +678,6 @@
     }
 
     private void stopUser(int userId, boolean force) throws RemoteException {
-        waitForBroadcastIdle();
-
         final CountDownLatch latch = new CountDownLatch(1);
         mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
             @Override
@@ -886,8 +882,4 @@
         assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
         return oldValue;
     }
-
-    private void waitForBroadcastIdle() {
-        ShellHelper.runShellCommand("am wait-for-broadcast-idle");
-    }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index cd93551..d8e25b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5218,13 +5218,15 @@
                                         + TareBill.getName(bill) + " changed to " + canAfford);
                     }
 
-                    ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
-                            mAffordabilityCache.get(userId, packageName);
-                    if (actionAffordability == null) {
-                        actionAffordability = new ArrayMap<>();
-                        mAffordabilityCache.add(userId, packageName, actionAffordability);
+                    synchronized (mLock) {
+                        ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
+                                mAffordabilityCache.get(userId, packageName);
+                        if (actionAffordability == null) {
+                            actionAffordability = new ArrayMap<>();
+                            mAffordabilityCache.add(userId, packageName, actionAffordability);
+                        }
+                        actionAffordability.put(bill, canAfford);
                     }
-                    actionAffordability.put(bill, canAfford);
 
                     mHandler.obtainMessage(AlarmHandler.TARE_AFFORDABILITY_CHANGED, userId,
                             canAfford ? 1 : 0, packageName)
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 29cd996..52882fe 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -546,6 +546,9 @@
         if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
             requiredConstraints |= CONSTRAINT_DEADLINE;
         }
+        if (job.isPrefetch()) {
+            requiredConstraints |= CONSTRAINT_PREFETCH;
+        }
         boolean exemptedMediaUrisOnly = false;
         if (job.getTriggerContentUris() != null) {
             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
@@ -1135,11 +1138,6 @@
         return hasConstraint(CONSTRAINT_IDLE);
     }
 
-    /** Returns true if the job has a prefetch constraint */
-    public boolean hasPrefetchConstraint() {
-        return hasConstraint(CONSTRAINT_PREFETCH);
-    }
-
     public boolean hasContentTriggerConstraint() {
         // No need to check mDynamicConstraints since content trigger will only be in that list if
         // it's already in the requiredConstraints list.
diff --git a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
index dbc3b51..f5184e7 100644
--- a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
+++ b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
@@ -39,7 +39,6 @@
 Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
 Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
 Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
 Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
diff --git a/core/api/current.txt b/core/api/current.txt
index af18792..1cd8253 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18904,6 +18904,7 @@
     method public void onUnbindInput();
     method @Deprecated public void onUpdateCursor(android.graphics.Rect);
     method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
+    method public void onUpdateEditorToolType(int);
     method public void onUpdateExtractedText(int, android.view.inputmethod.ExtractedText);
     method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo);
     method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo);
@@ -45246,6 +45247,8 @@
     method public static boolean extendRight(android.text.Spannable, android.text.Layout);
     method public static final void extendSelection(android.text.Spannable, int);
     method public static boolean extendToLeftEdge(android.text.Spannable, android.text.Layout);
+    method public static boolean extendToParagraphEnd(@NonNull android.text.Spannable);
+    method public static boolean extendToParagraphStart(@NonNull android.text.Spannable);
     method public static boolean extendToRightEdge(android.text.Spannable, android.text.Layout);
     method public static boolean extendUp(android.text.Spannable, android.text.Layout);
     method public static final int getSelectionEnd(CharSequence);
@@ -45254,6 +45257,8 @@
     method public static boolean moveLeft(android.text.Spannable, android.text.Layout);
     method public static boolean moveRight(android.text.Spannable, android.text.Layout);
     method public static boolean moveToLeftEdge(android.text.Spannable, android.text.Layout);
+    method public static boolean moveToParagraphEnd(@NonNull android.text.Spannable, @NonNull android.text.Layout);
+    method public static boolean moveToParagraphStart(@NonNull android.text.Spannable, @NonNull android.text.Layout);
     method public static boolean moveToRightEdge(android.text.Spannable, android.text.Layout);
     method public static boolean moveUp(android.text.Spannable, android.text.Layout);
     method public static final void removeSelection(android.text.Spannable);
@@ -45702,6 +45707,7 @@
     method protected boolean left(android.widget.TextView, android.text.Spannable);
     method protected boolean lineEnd(android.widget.TextView, android.text.Spannable);
     method protected boolean lineStart(android.widget.TextView, android.text.Spannable);
+    method public boolean nextParagraph(@NonNull android.widget.TextView, @NonNull android.text.Spannable);
     method public boolean onGenericMotionEvent(android.widget.TextView, android.text.Spannable, android.view.MotionEvent);
     method public boolean onKeyDown(android.widget.TextView, android.text.Spannable, int, android.view.KeyEvent);
     method public boolean onKeyOther(android.widget.TextView, android.text.Spannable, android.view.KeyEvent);
@@ -45711,6 +45717,7 @@
     method public boolean onTrackballEvent(android.widget.TextView, android.text.Spannable, android.view.MotionEvent);
     method protected boolean pageDown(android.widget.TextView, android.text.Spannable);
     method protected boolean pageUp(android.widget.TextView, android.text.Spannable);
+    method public boolean previousParagraph(@NonNull android.widget.TextView, @NonNull android.text.Spannable);
     method protected boolean right(android.widget.TextView, android.text.Spannable);
     method protected boolean top(android.widget.TextView, android.text.Spannable);
     method protected boolean up(android.widget.TextView, android.text.Spannable);
@@ -52968,9 +52975,11 @@
     method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
     method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
     method @Nullable public CharSequence getInitialTextBeforeCursor(@IntRange(from=0) int, int);
+    method public int getInitialToolType();
     method public final void makeCompatible(int);
     method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
     method public void setInitialSurroundingText(@NonNull CharSequence);
+    method public void setInitialToolType(int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
     field public static final int IME_ACTION_DONE = 6; // 0x6
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fc0c26..f17d5b7 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -369,7 +371,8 @@
      * @hide
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getTasks(maxNum, false /* filterForVisibleRecents */);
+        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
@@ -378,7 +381,8 @@
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(
             int maxNum, boolean filterOnlyVisibleRecents) {
-        return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
+        return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
@@ -388,8 +392,20 @@
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(
             int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+        return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+    }
+
+    /**
+     * @return List of running tasks that can be filtered by visibility and displayId in recents
+     * and keep intent extra.
+     * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+     * @hide
+     */
+    public List<ActivityManager.RunningTaskInfo> getTasks(
+            int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
         try {
-            return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
+            return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra,
+                    displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0bf16a0..6576a1a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,7 +158,7 @@
     boolean removeTask(int taskId);
     void removeAllVisibleRecentTasks();
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
-            boolean keepIntentExtra);
+            boolean keepIntentExtra, int displayId);
     void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
             int flags, in Bundle options);
     ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
@@ -231,7 +231,7 @@
             in IBinder activityToken, int flags);
     boolean isAssistDataAllowedOnCurrentActivity();
     boolean requestAssistDataForTask(in IAssistDataReceiver receiver, int taskId,
-            in String callingPackageName);
+            in String callingPackageName, String callingAttributionTag);
 
     /**
      * Notify the system that the keyguard is going away.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 3dedc41..f47c1e0 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1974,10 +1974,10 @@
             return true;
         }
         int diff = other.seq - seq;
-        if (diff > 0x10000) {
+        if (Math.abs(diff) > 0x10000000) {
             // If there has been a sufficiently large jump, assume the
             // sequence has wrapped around.
-            return false;
+            return diff < 0;
         }
         return diff > 0;
     }
diff --git a/core/java/android/database/TEST_MAPPING b/core/java/android/database/TEST_MAPPING
new file mode 100644
index 0000000..4a7fa66
--- /dev/null
+++ b/core/java/android/database/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsDatabaseTestCases"
+    }
+  ]
+}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index e960df1..3d59387 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -80,6 +80,7 @@
     private static final int DO_START_STYLUS_HANDWRITING = 110;
     private static final int DO_INIT_INK_WINDOW = 120;
     private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
+    private static final int DO_UPDATE_TOOL_TYPE = 140;
 
     final WeakReference<InputMethodServiceInternal> mTarget;
     final Context mContext;
@@ -234,6 +235,10 @@
                 inputMethod.canStartStylusHandwriting(msg.arg1);
                 return;
             }
+            case DO_UPDATE_TOOL_TYPE: {
+                inputMethod.updateEditorToolType(msg.arg1);
+                return;
+            }
             case DO_START_STYLUS_HANDWRITING: {
                 final SomeArgs args = (SomeArgs) msg.obj;
                 inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
@@ -402,6 +407,14 @@
 
     @BinderThread
     @Override
+    public void updateEditorToolType(int toolType)
+            throws RemoteException {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageI(DO_UPDATE_TOOL_TYPE, toolType));
+    }
+
+    @BinderThread
+    @Override
     public void startStylusHandwriting(int requestId, @NonNull InputChannel channel,
             @Nullable List<MotionEvent> stylusEvents)
             throws RemoteException {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b108490..c2027b1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -953,6 +953,15 @@
          * @hide
          */
         @Override
+        public void updateEditorToolType(int toolType) {
+            onUpdateEditorToolType(toolType);
+        }
+
+        /**
+         * {@inheritDoc}
+         * @hide
+         */
+        @Override
         public void canStartStylusHandwriting(int requestId) {
             if (DEBUG) Log.v(TAG, "canStartStylusHandwriting()");
             if (mHandwritingRequestId.isPresent()) {
@@ -3012,11 +3021,14 @@
      * not call to inform the IME of this interaction.
      * @param focusChanged true if the user changed the focused view by this click.
      * @see InputMethodManager#viewClicked(View)
+     * @see #onUpdateEditorToolType(int)
      * @deprecated The method may not be called for composite {@link View} that works as a giant
      *             "Canvas", which can host its own UI hierarchy and sub focus state.
      *             {@link android.webkit.WebView} is a good example. Application / IME developers
      *             should not rely on this method. If your goal is just being notified when an
      *             on-going input is interrupted, simply monitor {@link #onFinishInput()}.
+     *             If your goal is to know what {@link MotionEvent#getToolType(int)} clicked on
+     *             editor, use {@link #onUpdateEditorToolType(int)} instead.
      */
     @Deprecated
     public void onViewClicked(boolean focusChanged) {
@@ -3024,6 +3036,19 @@
     }
 
     /**
+     * Called when the user tapped or clicked an {@link android.widget.Editor}.
+     * This can be useful when IME makes a decision of showing Virtual keyboard based on what
+     * {@link MotionEvent#getToolType(int)} was used to click the editor.
+     * e.g. when toolType is {@link MotionEvent#TOOL_TYPE_STYLUS}, IME may choose to show a
+     * companion widget instead of normal virtual keyboard.
+     * <p> Default implementation does nothing. </p>
+     * @param toolType what {@link MotionEvent#getToolType(int)} was used to click on editor.
+     */
+    public void onUpdateEditorToolType(int toolType) {
+        // Intentionally empty
+    }
+
+    /**
      * Called when the application has reported a new location of its text
      * cursor.  This is only called if explicitly requested by the input method.
      * The default implementation does nothing.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6fc2811..9000899 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14257,7 +14257,7 @@
          *
          * @hide
          */
-        public static final int DEFAULT_ENABLE_TARE = 0;
+        public static final int DEFAULT_ENABLE_TARE = 1;
 
         /**
          * Whether to enable the TARE AlarmManager economic policy or not.
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index b20dccc..c47fdd3 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -16,7 +16,9 @@
 
 package android.service.voice;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.IBinder;
 
@@ -32,9 +34,12 @@
      * Start a new voice interaction session when requested from within an activity
      * by Activity.startLocalVoiceInteraction()
      * @param callingActivity The binder token representing the calling activity.
-     * @param options 
+     * @param attributionTag The attribution tag of the calling context or {@code null} for default
+     *                       attribution
+     * @param options A Bundle of private arguments to the current voice interaction service
      */
-    public abstract void startLocalVoiceInteraction(IBinder callingActivity, Bundle options);
+    public abstract void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+            @Nullable String attributionTag, @NonNull Bundle options);
 
     /**
      * Returns whether the currently selected voice interaction service supports local voice
@@ -65,6 +70,13 @@
     public abstract HotwordDetectionServiceIdentity getHotwordDetectionServiceIdentity();
 
     /**
+     * Called by {@code UMS.convertPreCreatedUserIfPossible()} when a new user is not created from
+     * scratched, but converted from the pool of existing pre-created users.
+     */
+    // TODO(b/226201975): remove method once RoleService supports pre-created users
+    public abstract void onPreCreatedUserConversion(@UserIdInt int userId);
+
+    /**
      * Provides the uids of the currently active
      * {@link android.service.voice.HotwordDetectionService} and its owning package. The
      * HotwordDetectionService is an isolated service, so it has a separate uid.
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1170237..1b46107 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -242,7 +242,7 @@
             throw new IllegalStateException("Not available until onReady() is called");
         }
         try {
-            mSystemService.showSession(args, flags);
+            mSystemService.showSession(args, flags, getAttributionTag());
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index b7ec486..d08a67e 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1384,7 +1384,8 @@
             throw new IllegalStateException("Can't call before onCreate()");
         }
         try {
-            mSystemService.showSessionFromSession(mToken, args, flags);
+            mSystemService.showSessionFromSession(mToken, args, flags,
+                    mContext.getAttributionTag());
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 57c1d23..6a54d23 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
@@ -346,6 +347,100 @@
         return false;
     }
 
+    private static final char PARAGRAPH_SEPARATOR = '\n';
+
+    /**
+     * Move the cusrot to the closest paragraph start offset.
+     *
+     * @param text the spannable text
+     * @param layout layout to be used for drawing.
+     * @return true if the cursor is moved, otherwise false.
+     */
+    public static boolean moveToParagraphStart(@NonNull Spannable text, @NonNull Layout layout) {
+        int start = getSelectionStart(text);
+        int end = getSelectionEnd(text);
+
+        if (start != end) {
+            setSelection(text, chooseHorizontal(layout, -1, start, end));
+            return true;
+        } else {
+            int to = TextUtils.lastIndexOf(text, PARAGRAPH_SEPARATOR, start - 1);
+            if (to == -1) {
+                to = 0;  // If not found, use the document start offset as a paragraph start.
+            }
+            if (to != end) {
+                setSelection(text, to);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Move the cursor to the closest paragraph end offset.
+     *
+     * @param text the spannable text
+     * @param layout layout to be used for drawing.
+     * @return true if the cursor is moved, otherwise false.
+     */
+    public static boolean moveToParagraphEnd(@NonNull Spannable text, @NonNull Layout layout) {
+        int start = getSelectionStart(text);
+        int end = getSelectionEnd(text);
+
+        if (start != end) {
+            setSelection(text, chooseHorizontal(layout, 1, start, end));
+            return true;
+        } else {
+            int to = TextUtils.indexOf(text, PARAGRAPH_SEPARATOR, end + 1);
+            if (to == -1) {
+                to = text.length();
+            }
+            if (to != end) {
+                setSelection(text, to);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Extend the selection to the closest paragraph start offset.
+     *
+     * @param text the spannable text
+     * @return true if the selection is extended, otherwise false
+     */
+    public static boolean extendToParagraphStart(@NonNull Spannable text) {
+        int end = getSelectionEnd(text);
+        int to = TextUtils.lastIndexOf(text, PARAGRAPH_SEPARATOR, end - 1);
+        if (to == -1) {
+            to = 0;  // If not found, use the document start offset as a paragraph start.
+        }
+        if (to != end) {
+            extendSelection(text, to);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Extend the selection to the closest paragraph end offset.
+     *
+     * @param text the spannable text
+     * @return true if the selection is extended, otherwise false
+     */
+    public static boolean extendToParagraphEnd(@NonNull Spannable text) {
+        int end = getSelectionEnd(text);
+        int to = TextUtils.indexOf(text, PARAGRAPH_SEPARATOR, end + 1);
+        if (to == -1) {
+            to = text.length();
+        }
+        if (to != end) {
+            extendSelection(text, to);
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Move the selection end to the buffer offset physically above
      * the current selection end.
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 57fe131..d3367d0 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -16,6 +16,7 @@
 
 package android.text.method;
 
+import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.text.Layout;
 import android.text.Selection;
@@ -222,6 +223,26 @@
     }
 
     @Override
+    public boolean previousParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+        final Layout layout = widget.getLayout();
+        if (isSelecting(buffer)) {
+            return Selection.extendToParagraphStart(buffer);
+        } else {
+            return Selection.moveToParagraphStart(buffer, layout);
+        }
+    }
+
+    @Override
+    public boolean nextParagraph(@NonNull TextView widget, @NonNull  Spannable buffer) {
+        final Layout layout = widget.getLayout();
+        if (isSelecting(buffer)) {
+            return Selection.extendToParagraphEnd(buffer);
+        } else {
+            return Selection.moveToParagraphEnd(buffer, layout);
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
         int initialScrollX = -1;
         int initialScrollY = -1;
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index 155a2c4..7a4b3a0 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -16,6 +16,7 @@
 
 package android.text.method;
 
+import android.annotation.NonNull;
 import android.text.Layout;
 import android.text.Spannable;
 import android.view.InputDevice;
@@ -190,6 +191,9 @@
                 } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
                         KeyEvent.META_ALT_ON)) {
                     return top(widget, buffer);
+                } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+                        KeyEvent.META_CTRL_ON)) {
+                    return previousParagraph(widget, buffer);
                 }
                 break;
 
@@ -199,6 +203,9 @@
                 } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
                         KeyEvent.META_ALT_ON)) {
                     return bottom(widget, buffer);
+                } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+                        KeyEvent.META_CTRL_ON)) {
+                    return nextParagraph(widget, buffer);
                 }
                 break;
 
@@ -399,6 +406,28 @@
         return false;
     }
 
+    /**
+     * Performs a previous paragraph movement action.
+     *
+     * @param widget the text view
+     * @param buffer the text buffer
+     * @return true if the event was handled, otherwise false.
+     */
+    public boolean previousParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+        return false;
+    }
+
+    /**
+     * Performs a next paragraph movement action.
+     *
+     * @param widget the text view
+     * @param buffer the text buffer
+     * @return true if the event was handled, otherwise false.
+     */
+    public boolean nextParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+        return false;
+    }
+
     private int getTopLine(TextView widget) {
         return widget.getLayout().getLineForVertical(widget.getScrollY());
     }
diff --git a/core/java/android/view/IWindowFocusObserver.aidl b/core/java/android/view/IWindowFocusObserver.aidl
index d14bb48..3b23c77 100644
--- a/core/java/android/view/IWindowFocusObserver.aidl
+++ b/core/java/android/view/IWindowFocusObserver.aidl
@@ -16,7 +16,7 @@
 package android.view;
 
 /** {@hide} */
-interface IWindowFocusObserver
+oneway interface IWindowFocusObserver
 {
     void focusGained(IBinder inputToken);
     void focusLost(IBinder inputToken);
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 44f419a..0db28d4 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -363,11 +363,11 @@
         pw.print(prefix); pw.print("leash="); pw.println(leash);
         pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
         pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
-        pw.print(prefix); pw.print("windowType="); pw.print(windowType);
-        pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
-        pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
-        pw.print(prefix); pw.print("showBackdrop="); pw.print(showBackdrop);
-        pw.print(prefix); pw.print("willShowImeOnTarget="); pw.print(willShowImeOnTarget);
+        pw.print(prefix); pw.print("windowType="); pw.println(windowType);
+        pw.print(prefix); pw.print("hasAnimatingParent="); pw.println(hasAnimatingParent);
+        pw.print(prefix); pw.print("backgroundColor="); pw.println(backgroundColor);
+        pw.print(prefix); pw.print("showBackdrop="); pw.println(showBackdrop);
+        pw.print(prefix); pw.print("willShowImeOnTarget="); pw.println(willShowImeOnTarget);
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1bedbfc..e1966a0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -734,6 +734,8 @@
     final PointF mDragPoint = new PointF();
     final PointF mLastTouchPoint = new PointF();
     int mLastTouchSource;
+    /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/
+    private int mLastClickToolType;
 
     private boolean mProfileRendering;
     private Choreographer.FrameCallback mRenderProfiler;
@@ -6417,11 +6419,16 @@
                 event.offsetLocation(0, mCurScrollY);
             }
 
-            // Remember the touch position for possible drag-initiation.
             if (event.isTouchEvent()) {
+                // Remember the touch position for possible drag-initiation.
                 mLastTouchPoint.x = event.getRawX();
                 mLastTouchPoint.y = event.getRawY();
                 mLastTouchSource = event.getSource();
+
+                // Register last ACTION_UP. This will be propagated to IME.
+                if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+                    mLastClickToolType = event.getToolType(event.getActionIndex());
+                }
             }
             return FORWARD;
         }
@@ -8017,6 +8024,14 @@
         return mLastTouchSource;
     }
 
+    /**
+     * Used by InputMethodManager.
+     * @hide
+     */
+    public int getLastClickToolType() {
+        return mLastClickToolType;
+    }
+
     public void setDragFocus(View newDragTarget, DragEvent event) {
         if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) {
             // Send EXITED and ENTERED notifications to the old and new drag focus views.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e56c43e..6f69361 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -834,8 +834,8 @@
      *
      * <p>Default is {@code false}.
      *
-     * <p>The system enforcement will be added in Android 14, but some devices may start following
-     * the requirement before that. The best practice for apps is to always explicitly set this
+     * <p>The system enforcement is added in Android 14, but some devices may start following the
+     * requirement before that. The best practice for apps is to always explicitly set this
      * property in AndroidManifest instead of relying on the default value.
      *
      * <p>Example usage:
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index fdff7a3..55a2d22 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -30,6 +30,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.content.res.Configuration;
+import android.inputmethodservice.InputMethodService;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -40,6 +41,7 @@
 import android.text.TextUtils;
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.autofill.AutofillId;
 
@@ -564,6 +566,12 @@
     @Nullable
     private SurroundingText mInitialSurroundingText = null;
 
+    /**
+     * Initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)} that
+     * was used to focus this editor.
+     */
+    private int mInitialToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+
 
     /**
      * Editors may use this method to provide initial input text to IMEs. As the surrounding text
@@ -937,6 +945,31 @@
     }
 
     /**
+     * Returns the initial {@link MotionEvent#ACTION_UP} tool type
+     * {@link MotionEvent#getToolType(int)} responsible for focus on the current editor.
+     *
+     * @see #setInitialToolType(int)
+     * @see MotionEvent#getToolType(int)
+     * @see InputMethodService#onUpdateEditorToolType(int)
+     * @return toolType {@link MotionEvent#getToolType(int)}.
+     */
+    public int getInitialToolType() {
+        return mInitialToolType;
+    }
+
+    /**
+     * Set the initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)}.
+     * that brought focus to the view.
+     *
+     * @see #getInitialToolType()
+     * @see MotionEvent#getToolType(int)
+     * @see InputMethodService#onUpdateEditorToolType(int)
+     */
+    public void setInitialToolType(int toolType) {
+        mInitialToolType = toolType;
+    }
+
+    /**
      * Export the state of {@link EditorInfo} into a protocol buffer output stream.
      *
      * @param proto Stream to write the state to
@@ -972,6 +1005,7 @@
                 + " actionId=" + actionId);
         pw.println(prefix + "initialSelStart=" + initialSelStart
                 + " initialSelEnd=" + initialSelEnd
+                + " initialToolType=" + mInitialToolType
                 + " initialCapsMode=0x"
                 + Integer.toHexString(initialCapsMode));
         pw.println(prefix + "hintText=" + hintText
@@ -1006,6 +1040,7 @@
         newEditorInfo.initialSelStart = initialSelStart;
         newEditorInfo.initialSelEnd = initialSelEnd;
         newEditorInfo.initialCapsMode = initialCapsMode;
+        newEditorInfo.mInitialToolType = mInitialToolType;
         newEditorInfo.hintText = TextUtils.stringOrSpannedString(hintText);
         newEditorInfo.label = TextUtils.stringOrSpannedString(label);
         newEditorInfo.packageName = packageName;
@@ -1036,6 +1071,7 @@
         dest.writeInt(initialSelStart);
         dest.writeInt(initialSelEnd);
         dest.writeInt(initialCapsMode);
+        dest.writeInt(mInitialToolType);
         TextUtils.writeToParcel(hintText, dest, flags);
         TextUtils.writeToParcel(label, dest, flags);
         dest.writeString(packageName);
@@ -1072,6 +1108,7 @@
                     res.initialSelStart = source.readInt();
                     res.initialSelEnd = source.readInt();
                     res.initialCapsMode = source.readInt();
+                    res.mInitialToolType = source.readInt();
                     res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
                     res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
                     res.packageName = source.readString();
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
index 78a90d9..429b0b8 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
@@ -111,10 +111,11 @@
 
     @AnyThread
     boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
-            int flags, @Nullable ResultReceiver resultReceiver,
+            int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
         try {
-            return mTarget.showSoftInput(client, windowToken, flags, resultReceiver, reason);
+            return mTarget.showSoftInput(
+                    client, windowToken, flags, lastClickToolType, resultReceiver, reason);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 95add29..bfe6ae6 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -377,6 +377,15 @@
     }
 
     /**
+     * This method is called when the user tapped or clicked an {@link android.widget.Editor}.
+     * @param toolType {@link android.view.MotionEvent#getToolType(int)} used for clicking editor.
+     * @hide
+     */
+    default void updateEditorToolType(int toolType) {
+        // intentionally empty
+    }
+
+    /**
      * Start stylus handwriting session.
      * @hide
      */
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 899e5fc..0b4a298 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1929,6 +1929,7 @@
                     mClient,
                     view.getWindowToken(),
                     flags,
+                    mCurRootView.getLastClickToolType(),
                     resultReceiver,
                     reason);
         }
@@ -1960,6 +1961,7 @@
                     mClient,
                     mCurRootView.getView().getWindowToken(),
                     flags,
+                    mCurRootView.getLastClickToolType(),
                     resultReceiver,
                     SoftInputShowHideReason.SHOW_SOFT_INPUT);
         }
@@ -2339,6 +2341,9 @@
         editorInfo.packageName = view.getContext().getOpPackageName();
         editorInfo.autofillId = view.getAutofillId();
         editorInfo.fieldId = view.getId();
+        synchronized (mH) {
+            editorInfo.setInitialToolType(mCurRootView.getLastClickToolType());
+        }
         InputConnection ic = view.onCreateInputConnection(editorInfo);
         if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);
 
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 3335c9c..79ddadb 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -16,54 +16,9 @@
 
 package android.window;
 
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
 
 /** @hide */
 oneway interface ITaskFragmentOrganizer {
-    void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
-    void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
-    void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
-
-    /**
-     * Called when the parent leaf Task of organized TaskFragments is changed.
-     * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
-     * transaction.
-     *
-     * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
-     * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
-     * bounds.
-     */
-    void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
-
-    /**
-     * Called when the {@link WindowContainerTransaction} created with
-     * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
-     *
-     * @param errorCallbackToken    Token set through {@link
-     *                              WindowContainerTransaction#setErrorCallbackToken(IBinder)}
-     * @param errorBundle       Bundle containing the exception, operation type and TaskFragmentInfo
-     *                          if any. Should be created with
-     *                          {@link TaskFragmentOrganizer#putErrorInfoInBundle}.
-     */
-    void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle errorBundle);
-
-    /**
-     * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
-     * when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
-     * orginial Task. In this case, we need to notify the organizer so that it can check if the
-     * Activity matches any split rule.
-     *
-     * @param taskId            The Task that the activity is reparented to.
-     * @param activityIntent    The intent that the activity is original launched with.
-     * @param activityToken     If the activity belongs to the same process as the organizer, this
-     *                          will be the actual activity token; if the activity belongs to a
-     *                          different process, the server will generate a temporary token that
-     *                          the organizer can use to reparent the activity through
-     *                          {@link WindowContainerTransaction} if needed.
-     */
-    void onActivityReparentToTask(int taskId, in Intent activityIntent, in IBinder activityToken);
+    void onTransactionReady(in TaskFragmentTransaction transaction);
 }
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 765cd81..cc90aaf 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -16,6 +16,13 @@
 
 package android.window;
 
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +34,7 @@
 import android.os.RemoteException;
 import android.view.RemoteAnimationDefinition;
 
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -186,6 +194,67 @@
     public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
             @NonNull IBinder activityToken) {}
 
+    /**
+     * Called when the transaction is ready so that the organizer can update the TaskFragments based
+     * on the changes in transaction.
+     * @hide
+     */
+    public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+        final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
+        for (TaskFragmentTransaction.Change change : changes) {
+            // TODO(b/240519866): apply all changes in one WCT.
+            switch (change.getType()) {
+                case TYPE_TASK_FRAGMENT_APPEARED:
+                    onTaskFragmentAppeared(change.getTaskFragmentInfo());
+                    if (change.getTaskConfiguration() != null) {
+                        // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
+                        // same Task
+                        onTaskFragmentParentInfoChanged(
+                                change.getTaskFragmentToken(),
+                                change.getTaskConfiguration());
+                    }
+                    break;
+                case TYPE_TASK_FRAGMENT_INFO_CHANGED:
+                    if (change.getTaskConfiguration() != null) {
+                        // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
+                        // same Task
+                        onTaskFragmentParentInfoChanged(
+                                change.getTaskFragmentToken(),
+                                change.getTaskConfiguration());
+                    }
+                    onTaskFragmentInfoChanged(change.getTaskFragmentInfo());
+                    break;
+                case TYPE_TASK_FRAGMENT_VANISHED:
+                    onTaskFragmentVanished(change.getTaskFragmentInfo());
+                    break;
+                case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
+                    onTaskFragmentParentInfoChanged(
+                            change.getTaskFragmentToken(),
+                            change.getTaskConfiguration());
+                    break;
+                case TYPE_TASK_FRAGMENT_ERROR:
+                    final Bundle errorBundle = change.getErrorBundle();
+                    onTaskFragmentError(
+                            change.getErrorCallbackToken(),
+                            errorBundle.getParcelable(
+                                    KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class),
+                            errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
+                            errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
+                                    java.lang.Throwable.class));
+                    break;
+                case TYPE_ACTIVITY_REPARENT_TO_TASK:
+                    onActivityReparentToTask(
+                            change.getTaskId(),
+                            change.getActivityIntent(),
+                            change.getActivityToken());
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown TaskFragmentEvent=" + change.getType());
+            }
+        }
+    }
+
     @Override
     public void applyTransaction(@NonNull WindowContainerTransaction t) {
         t.setTaskFragmentOrganizer(mInterface);
@@ -203,51 +272,8 @@
 
     private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
         @Override
-        public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
-        }
-
-        @Override
-        public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
-        }
-
-        @Override
-        public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
-        }
-
-        @Override
-        public void onTaskFragmentParentInfoChanged(
-                @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
-                            fragmentToken, parentConfig));
-        }
-
-        @Override
-        public void onTaskFragmentError(
-                @NonNull IBinder errorCallbackToken, @NonNull Bundle errorBundle) {
-            mExecutor.execute(() -> {
-                final TaskFragmentInfo info = errorBundle.getParcelable(
-                        KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class);
-                TaskFragmentOrganizer.this.onTaskFragmentError(
-                        errorCallbackToken, info,
-                        errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
-                        (Throwable) errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
-                                java.lang.Throwable.class));
-            });
-        }
-
-        @Override
-        public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
-                @NonNull IBinder activityToken) {
-            mExecutor.execute(
-                    () -> TaskFragmentOrganizer.this.onActivityReparentToTask(
-                            taskId, activityIntent, activityToken));
+        public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+            mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
         }
     };
 
diff --git a/core/java/android/window/TaskFragmentTransaction.aidl b/core/java/android/window/TaskFragmentTransaction.aidl
new file mode 100644
index 0000000..aaa2db4
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+parcelable TaskFragmentTransaction;
+parcelable TaskFragmentTransaction.Change;
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
new file mode 100644
index 0000000..755864f
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to communicate information about what are changing on embedded TaskFragments belonging to
+ * the same TaskFragmentOrganizer. A transaction can contain multiple changes.
+ * @see TaskFragmentTransaction.Change
+ * @hide
+ */
+public final class TaskFragmentTransaction implements Parcelable {
+
+    private final ArrayList<Change> mChanges = new ArrayList<>();
+
+    public TaskFragmentTransaction() {}
+
+    private TaskFragmentTransaction(Parcel in) {
+        in.readTypedList(mChanges, Change.CREATOR);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedList(mChanges);
+    }
+
+    /** Adds a {@link Change} to this transaction. */
+    public void addChange(@Nullable Change change) {
+        if (change != null) {
+            mChanges.add(change);
+        }
+    }
+
+    /** Whether this transaction contains any {@link Change}. */
+    public boolean isEmpty() {
+        return mChanges.isEmpty();
+    }
+
+    @NonNull
+    public List<Change> getChanges() {
+        return mChanges;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("TaskFragmentTransaction{changes=[");
+        for (int i = 0; i < mChanges.size(); ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(mChanges.get(i));
+        }
+        sb.append("]}");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
+        @Override
+        public TaskFragmentTransaction createFromParcel(Parcel in) {
+            return new TaskFragmentTransaction(in);
+        }
+
+        @Override
+        public TaskFragmentTransaction[] newArray(int size) {
+            return new TaskFragmentTransaction[size];
+        }
+    };
+
+    /** Change type: the TaskFragment is attached to the hierarchy. */
+    public static final int TYPE_TASK_FRAGMENT_APPEARED = 1;
+
+    /** Change type: the status of the TaskFragment is changed. */
+    public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2;
+
+    /** Change type: the TaskFragment is removed form the hierarchy. */
+    public static final int TYPE_TASK_FRAGMENT_VANISHED = 3;
+
+    /** Change type: the status of the parent leaf Task is changed. */
+    public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4;
+
+    /** Change type: the TaskFragment related operation failed on the server side. */
+    public static final int TYPE_TASK_FRAGMENT_ERROR = 5;
+
+    /**
+     * Change type: an Activity is reparented to the Task. For example, when an Activity enters and
+     * then exits Picture-in-picture, it will be reparented back to its original Task. In this case,
+     * we need to notify the organizer so that it can check if the Activity matches any split rule.
+     */
+    public static final int TYPE_ACTIVITY_REPARENT_TO_TASK = 6;
+
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_TASK_FRAGMENT_APPEARED,
+            TYPE_TASK_FRAGMENT_INFO_CHANGED,
+            TYPE_TASK_FRAGMENT_VANISHED,
+            TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED,
+            TYPE_TASK_FRAGMENT_ERROR,
+            TYPE_ACTIVITY_REPARENT_TO_TASK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ChangeType {}
+
+    /** Represents the change an embedded TaskFragment undergoes. */
+    public static final class Change implements Parcelable {
+
+        /** @see ChangeType */
+        @ChangeType
+        private final int mType;
+
+        /** @see #setTaskFragmentToken(IBinder) */
+        @Nullable
+        private IBinder mTaskFragmentToken;
+
+        /** @see #setTaskFragmentInfo(TaskFragmentInfo) */
+        @Nullable
+        private TaskFragmentInfo mTaskFragmentInfo;
+
+        /** @see #setTaskId(int) */
+        private int mTaskId;
+
+        /** @see #setTaskConfiguration(Configuration) */
+        @Nullable
+        private Configuration mTaskConfiguration;
+
+        /** @see #setErrorCallbackToken(IBinder) */
+        @Nullable
+        private IBinder mErrorCallbackToken;
+
+        /** @see #setErrorBundle(Bundle) */
+        @Nullable
+        private Bundle mErrorBundle;
+
+        /** @see #setActivityIntent(Intent) */
+        @Nullable
+        private Intent mActivityIntent;
+
+        /** @see #setActivityToken(IBinder) */
+        @Nullable
+        private IBinder mActivityToken;
+
+        public Change(@ChangeType int type) {
+            mType = type;
+        }
+
+        private Change(Parcel in) {
+            mType = in.readInt();
+            mTaskFragmentToken = in.readStrongBinder();
+            mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+            mTaskId = in.readInt();
+            mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
+            mErrorCallbackToken = in.readStrongBinder();
+            mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
+            mActivityIntent = in.readTypedObject(Intent.CREATOR);
+            mActivityToken = in.readStrongBinder();
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mType);
+            dest.writeStrongBinder(mTaskFragmentToken);
+            dest.writeTypedObject(mTaskFragmentInfo, flags);
+            dest.writeInt(mTaskId);
+            dest.writeTypedObject(mTaskConfiguration, flags);
+            dest.writeStrongBinder(mErrorCallbackToken);
+            dest.writeBundle(mErrorBundle);
+            dest.writeTypedObject(mActivityIntent, flags);
+            dest.writeStrongBinder(mActivityToken);
+        }
+
+        /** The change is related to the TaskFragment created with this unique token. */
+        public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
+            mTaskFragmentToken = requireNonNull(taskFragmentToken);
+            return this;
+        }
+
+        /** Info of the embedded TaskFragment. */
+        public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
+            mTaskFragmentInfo = requireNonNull(info);
+            return this;
+        }
+
+        /** Task id the parent Task. */
+        public Change setTaskId(int taskId) {
+            mTaskId = taskId;
+            return this;
+        }
+
+        /** Configuration of the parent Task. */
+        public Change setTaskConfiguration(@NonNull Configuration configuration) {
+            mTaskConfiguration = requireNonNull(configuration);
+            return this;
+        }
+
+        /**
+         * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
+         * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
+         * report back.
+         */
+        public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
+            mErrorCallbackToken = errorCallbackToken;
+            return this;
+        }
+
+        /**
+         * Bundle with necessary info about the failure operation of
+         * {@link #TYPE_TASK_FRAGMENT_ERROR}.
+         */
+        public Change setErrorBundle(@NonNull Bundle errorBundle) {
+            mErrorBundle = requireNonNull(errorBundle);
+            return this;
+        }
+
+        /**
+         * Intent of the activity that is reparented to the Task for
+         * {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+         */
+        public Change setActivityIntent(@NonNull Intent intent) {
+            mActivityIntent = requireNonNull(intent);
+            return this;
+        }
+
+        /**
+         * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+         * If the activity belongs to the same process as the organizer, this will be the actual
+         * activity token; if the activity belongs to a different process, the server will generate
+         * a temporary token that the organizer can use to reparent the activity through
+         * {@link WindowContainerTransaction} if needed.
+         */
+        public Change setActivityToken(@NonNull IBinder activityToken) {
+            mActivityToken = requireNonNull(activityToken);
+            return this;
+        }
+
+        @ChangeType
+        public int getType() {
+            return mType;
+        }
+
+        @Nullable
+        public IBinder getTaskFragmentToken() {
+            return mTaskFragmentToken;
+        }
+
+        @Nullable
+        public TaskFragmentInfo getTaskFragmentInfo() {
+            return mTaskFragmentInfo;
+        }
+
+        public int getTaskId() {
+            return mTaskId;
+        }
+
+        @Nullable
+        public Configuration getTaskConfiguration() {
+            return mTaskConfiguration;
+        }
+
+        @Nullable
+        public IBinder getErrorCallbackToken() {
+            return mErrorCallbackToken;
+        }
+
+        @Nullable
+        public Bundle getErrorBundle() {
+            return mErrorBundle;
+        }
+
+        @Nullable
+        public Intent getActivityIntent() {
+            return mActivityIntent;
+        }
+
+        @Nullable
+        public IBinder getActivityToken() {
+            return mActivityToken;
+        }
+
+        @Override
+        public String toString() {
+            return "Change{ type=" + mType + " }";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Change> CREATOR = new Creator<>() {
+            @Override
+            public Change createFromParcel(Parcel in) {
+                return new Change(in);
+            }
+
+            @Override
+            public Change[] newArray(int size) {
+                return new Change[size];
+            }
+        };
+    }
+}
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index c056e26..f843318 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,6 +17,7 @@
 package com.android.internal.app;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -67,12 +68,53 @@
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
     }
 
-    public boolean showSessionForActiveService(Bundle args, int sourceFlags,
-            IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+    /**
+     * Shows the session for the currently active service. Used to start a new session from system
+     * affordances.
+     *
+     * @param args the bundle to pass as arguments to the voice interaction session
+     * @param sourceFlags flags indicating the source of this show
+     * @param showCallback optional callback to be notified when the session was shown
+     * @param activityToken optional token of activity that needs to be on top
+     *
+     * @deprecated Use {@link #showSessionForActiveService(Bundle, int, String,
+     *             IVoiceInteractionSessionShowCallback, IBinder)} instead
+     */
+    @Deprecated
+    public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
+        return showSessionForActiveServiceInternal(args, sourceFlags, /* attributionTag */ null,
+                showCallback, activityToken);
+    }
+
+    /**
+     * Shows the session for the currently active service. Used to start a new session from system
+     * affordances.
+     *
+     * @param args the bundle to pass as arguments to the voice interaction session
+     * @param sourceFlags flags indicating the source of this show
+     * @param attributionTag the attribution tag of the calling context or {@code null} for default
+     *                       attribution
+     * @param showCallback optional callback to be notified when the session was shown
+     * @param activityToken optional token of activity that needs to be on top
+     */
+    public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+            @Nullable String attributionTag,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
+        return showSessionForActiveServiceInternal(args, sourceFlags, attributionTag, showCallback,
+                activityToken);
+    }
+
+    private boolean showSessionForActiveServiceInternal(@NonNull Bundle args, int sourceFlags,
+            @Nullable String attributionTag,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
         try {
             if (mVoiceInteractionManagerService != null) {
                 return mVoiceInteractionManagerService.showSessionForActiveService(args,
-                        sourceFlags, showCallback, activityToken);
+                        sourceFlags, attributionTag, showCallback, activityToken);
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call showSessionForActiveService", e);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ff1afdd..f6c64732 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -254,7 +254,7 @@
                     SystemUiDeviceConfigFlags.IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP,
                     DEFAULT_IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP);
 
-    private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 125;
+    private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 0;
 
     private static final int URI_PERMISSION_INTENT_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION
             | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 4b01357..83bf801 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -39,15 +39,16 @@
 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
 
 interface IVoiceInteractionManagerService {
-    void showSession(in Bundle sessionArgs, int flags);
+    void showSession(in Bundle sessionArgs, int flags, String attributionTag);
     boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
             IVoiceInteractor interactor);
-    boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
+    boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags,
+            String attributionTag);
     boolean hideSessionFromSession(IBinder token);
     int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
-            String callingFeatureId);
+            String attributionTag);
     int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
-            String callingFeatureId);
+            String attributionTag);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
@@ -125,12 +126,14 @@
      *
      * @param args the bundle to pass as arguments to the voice interaction session
      * @param sourceFlags flags indicating the source of this show
+     * @param attributionTag the attribution tag of the calling context or {@code null} for default
+     *                       attribution
      * @param showCallback optional callback to be notified when the session was shown
      * @param activityToken optional token of activity that needs to be on top
      * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
      */
     @EnforcePermission("ACCESS_VOICE_INTERACTION_SERVICE")
-    boolean showSessionForActiveService(in Bundle args, int sourceFlags,
+    boolean showSessionForActiveService(in Bundle args, int sourceFlags, String attributionTag,
             IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken);
 
     /**
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index b32afb4..66fff5c 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -951,7 +951,7 @@
         protected void onPostExecute(Drawable d) {
             if (getOtherProfile() == mDisplayResolveInfo) {
                 mResolverListCommunicator.updateProfileViewButton();
-            } else {
+            } else if (!mDisplayResolveInfo.hasDisplayIcon()) {
                 mDisplayResolveInfo.setDisplayIcon(d);
                 mHolder.bindIcon(mDisplayResolveInfo);
                 // Notify in case view is already bound to resolve the race conditions on
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index 8bab5c3..5db2e84 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -74,6 +74,8 @@
 
     void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
 
+    void updateEditorToolType(int toolType);
+
     void changeInputMethodSubtype(in InputMethodSubtype subtype);
 
     void canStartStylusHandwriting(int requestId);
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 8f10a5e..e625b31 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -55,7 +55,6 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
@@ -155,7 +154,6 @@
 
     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
-    public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
     public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
     public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
     public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
@@ -226,7 +224,7 @@
     public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
             // This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE.
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
-            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK,
+            NO_STATSD_LOGGING, // This is deprecated.
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE,
@@ -310,7 +308,6 @@
     /** @hide */
     @IntDef({
             CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
-            CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK,
             CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
             CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
             CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
@@ -738,8 +735,6 @@
         switch (cujType) {
             case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
                 return "SHADE_EXPAND_COLLAPSE";
-            case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK:
-                return "SHADE_EXPAND_COLLAPSE_LOCK";
             case CUJ_NOTIFICATION_SHADE_SCROLL_FLING:
                 return "SHADE_SCROLL_FLING";
             case CUJ_NOTIFICATION_SHADE_ROW_EXPAND:
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 8e54746..b9243ec 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -219,8 +219,9 @@
                 throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
             }
 
-            HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, android.hardware.HardwareBuffer.class);
-            ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE, android.graphics.ParcelableColorSpace.class);
+            HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, HardwareBuffer.class);
+            ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE,
+                    ParcelableColorSpace.class);
 
             return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
                     colorSpace.getColorSpace());
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5f5886e..9471fae 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -55,7 +55,7 @@
     InputMethodSubtype getLastInputMethodSubtype(int userId);
 
     boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
-            in @nullable ResultReceiver resultReceiver, int reason);
+            int lastClickToolType, in @nullable ResultReceiver resultReceiver, int reason);
     boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
             in @nullable ResultReceiver resultReceiver, int reason);
 
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 09ff4e0..9ee9b82 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -828,6 +828,8 @@
                     mSystemGestureExclusionListener, mContext.getDisplayId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Failed to unregister window manager callbacks", e);
         }
     }
 
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index d686dd2..34b6a54 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,7 +51,5 @@
 
     <!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
     <bool name="config_showUserSwitcherByDefault">true</bool>
-
-    <integer name="config_chooser_max_targets_per_row">6</integer>
 </resources>
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 8587a35..25bf93f 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -58,7 +58,7 @@
 public class StartProgramListUpdatesFanoutTest {
     private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
 
-    private static final VerificationWithTimeout CB_TIMEOUT = timeout(100);
+    private static final VerificationWithTimeout CB_TIMEOUT = timeout(500);
 
     // Mocks
     @Mock IBroadcastRadio mBroadcastRadioMock;
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 669138c..0d5cd72 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -157,6 +157,22 @@
         assertFalse(config.isNightModeActive());
     }
 
+    @Test
+    public void testSequenceNumberWrapAround() {
+        Configuration config = new Configuration();
+        Configuration otherConfig = new Configuration();
+
+        // Verify the other configuration is not newer when this sequence number warp around.
+        config.seq = 1000;
+        otherConfig.seq = Integer.MAX_VALUE - 1000;
+        assertFalse(config.isOtherSeqNewer(otherConfig));
+
+        // Verify the other configuration is newer when the other sequence number warp around.
+        config.seq = Integer.MAX_VALUE - 1000;
+        otherConfig.seq = 1000;
+        assertTrue(config.isOtherSeqNewer(otherConfig));
+    }
+
     private void dumpDebug(File f, Configuration config) throws Exception {
         final AtomicFile af = new AtomicFile(f);
         FileOutputStream fos = af.startWrite();
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index c1e72fe..32c3a26 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -58,10 +58,10 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import libcore.io.Streams;
-
 import com.google.android.collect.Sets;
 
+import libcore.io.Streams;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -477,6 +477,14 @@
         new File(mTarget, "test (1).jpg").createNewFile();
         assertNameEquals("test (2).jpg",
                 FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+
+        assertNameEquals("test.mp3", FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+        new File(mTarget, "test.mp3").createNewFile();
+        assertNameEquals("test (1).mp3",
+                FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+        new File(mTarget, "test (1).mp3").createNewFile();
+        assertNameEquals("test (2).mp3",
+                FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index b867e44..9637de8 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -42,6 +42,7 @@
 import android.text.style.MaskFilterSpan;
 import android.text.style.UnderlineSpan;
 import android.util.StringBuilderPrinter;
+import android.view.MotionEvent;
 import android.view.autofill.AutofillId;
 
 import androidx.test.filters.SmallTest;
@@ -490,7 +491,8 @@
         assertThat(sb.toString()).isEqualTo(
                 "prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
                 + "prefix: actionLabel=null actionId=0\n"
-                + "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+                + "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+                        + " initialCapsMode=0x0\n"
                 + "prefix: hintText=null label=null\n"
                 + "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
                 + "prefix: extras=null\n"
@@ -509,6 +511,7 @@
         info.initialCapsMode = TextUtils.CAP_MODE_CHARACTERS; // 0x1000
         info.hintText = "testHintText";
         info.label = "testLabel";
+        info.setInitialToolType(MotionEvent.TOOL_TYPE_STYLUS);
         info.packageName = "android.view.inputmethod";
         info.autofillId = new AutofillId(123);
         info.fieldId = 456;
@@ -523,7 +526,8 @@
         assertThat(sb.toString()).isEqualTo(
                 "prefix2: inputType=0x1 imeOptions=0x2 privateImeOptions=testOptions\n"
                         + "prefix2: actionLabel=null actionId=0\n"
-                        + "prefix2: initialSelStart=0 initialSelEnd=1 initialCapsMode=0x1000\n"
+                        + "prefix2: initialSelStart=0 initialSelEnd=1 initialToolType=2"
+                                + " initialCapsMode=0x1000\n"
                         + "prefix2: hintText=testHintText label=testLabel\n"
                         + "prefix2: packageName=android.view.inputmethod autofillId=123"
                         + " fieldId=456 fieldName=testField\n"
@@ -543,7 +547,8 @@
         assertThat(sb.toString()).isEqualTo(
                 "prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
                         + "prefix: actionLabel=null actionId=0\n"
-                        + "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+                        + "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+                                + " initialCapsMode=0x0\n"
                         + "prefix: hintText=null label=null\n"
                         + "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
                         + "prefix: hintLocales=null\n"
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
deleted file mode 100644
index e8793a9..0000000
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-
-public class ArrayUtilsTest extends TestCase {
-
-    @SmallTest
-    public void testUnstableRemoveIf() throws Exception {
-        java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() {
-            @Override
-            public boolean test(Object o) {
-                return o == null;
-            }
-        };
-
-        final Object a = new Object();
-        final Object b = new Object();
-        final Object c = new Object();
-
-        ArrayList<Object> collection = null;
-        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-
-        collection = new ArrayList<>();
-        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-
-        collection = new ArrayList<>(Collections.singletonList(a));
-        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(1, collection.size());
-        assertTrue(collection.contains(a));
-
-        collection = new ArrayList<>(Collections.singletonList(null));
-        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(0, collection.size());
-
-        collection = new ArrayList<>(Arrays.asList(a, b));
-        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(2, collection.size());
-        assertTrue(collection.contains(a));
-        assertTrue(collection.contains(b));
-
-        collection = new ArrayList<>(Arrays.asList(a, null));
-        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(1, collection.size());
-        assertTrue(collection.contains(a));
-
-        collection = new ArrayList<>(Arrays.asList(null, a));
-        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(1, collection.size());
-        assertTrue(collection.contains(a));
-
-        collection = new ArrayList<>(Arrays.asList(null, null));
-        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(0, collection.size());
-
-        collection = new ArrayList<>(Arrays.asList(a, b, c));
-        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(3, collection.size());
-        assertTrue(collection.contains(a));
-        assertTrue(collection.contains(b));
-        assertTrue(collection.contains(c));
-
-        collection = new ArrayList<>(Arrays.asList(a, b, null));
-        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(2, collection.size());
-        assertTrue(collection.contains(a));
-        assertTrue(collection.contains(b));
-
-        collection = new ArrayList<>(Arrays.asList(a, null, b));
-        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(2, collection.size());
-        assertTrue(collection.contains(a));
-        assertTrue(collection.contains(b));
-
-        collection = new ArrayList<>(Arrays.asList(null, a, b));
-        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(2, collection.size());
-        assertTrue(collection.contains(a));
-        assertTrue(collection.contains(b));
-
-        collection = new ArrayList<>(Arrays.asList(a, null, null));
-        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(1, collection.size());
-        assertTrue(collection.contains(a));
-
-        collection = new ArrayList<>(Arrays.asList(null, null, a));
-        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(1, collection.size());
-        assertTrue(collection.contains(a));
-
-        collection = new ArrayList<>(Arrays.asList(null, a, null));
-        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(1, collection.size());
-        assertTrue(collection.contains(a));
-
-        collection = new ArrayList<>(Arrays.asList(null, null, null));
-        assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
-        assertEquals(0, collection.size());
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() {
-        ArrayUtils.throwsIfOutOfBounds(10, 2, 6);
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() {
-        ArrayUtils.throwsIfOutOfBounds(10, 0, 10);
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() {
-        ArrayUtils.throwsIfOutOfBounds(10, 0, 0);
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() {
-        ArrayUtils.throwsIfOutOfBounds(10, 10, 0);
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_passesWhenEmptyArray() {
-        ArrayUtils.throwsIfOutOfBounds(0, 0, 0);
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() {
-        try {
-            ArrayUtils.throwsIfOutOfBounds(10, -1, 5);
-            fail();
-        } catch (ArrayIndexOutOfBoundsException expected) {
-            // expected
-        }
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_failsWhenCountNegative() {
-        try {
-            ArrayUtils.throwsIfOutOfBounds(10, 5, -1);
-            fail();
-        } catch (ArrayIndexOutOfBoundsException expected) {
-            // expected
-        }
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() {
-        try {
-            ArrayUtils.throwsIfOutOfBounds(10, 11, 0);
-            fail();
-        } catch (ArrayIndexOutOfBoundsException expected) {
-            // expected
-        }
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() {
-        try {
-            ArrayUtils.throwsIfOutOfBounds(10, 5, 6);
-            fail();
-        } catch (ArrayIndexOutOfBoundsException expected) {
-            // expected
-        }
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_failsWhenLengthNegative() {
-        try {
-            ArrayUtils.throwsIfOutOfBounds(-1, 0, 0);
-            fail();
-        } catch (ArrayIndexOutOfBoundsException expected) {
-            // expected
-        }
-    }
-
-    @SmallTest
-    public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() {
-        try {
-            ArrayUtils.throwsIfOutOfBounds(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
-            fail();
-        } catch (ArrayIndexOutOfBoundsException expected) {
-            // expected
-        }
-    }
-}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index c66a743..72f3af6 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -18,6 +18,12 @@
 
 import static org.junit.Assert.assertArrayEquals;
 
+import androidx.test.filters.SmallTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
 import junit.framework.TestCase;
 
 /**
@@ -257,4 +263,182 @@
 
         assertArrayEquals(expectation, ArrayUtils.concat(array1, array2, array3));
     }
+
+    @SmallTest
+    public void testUnstableRemoveIf() throws Exception {
+        java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() {
+            @Override
+            public boolean test(Object o) {
+                return o == null;
+            }
+        };
+
+        final Object a = new Object();
+        final Object b = new Object();
+        final Object c = new Object();
+
+        ArrayList<Object> collection = null;
+        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+        collection = new ArrayList<>();
+        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+        collection = new ArrayList<>(Collections.singletonList(a));
+        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(1, collection.size());
+        assertTrue(collection.contains(a));
+
+        collection = new ArrayList<>(Collections.singletonList(null));
+        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(0, collection.size());
+
+        collection = new ArrayList<>(Arrays.asList(a, b));
+        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(2, collection.size());
+        assertTrue(collection.contains(a));
+        assertTrue(collection.contains(b));
+
+        collection = new ArrayList<>(Arrays.asList(a, null));
+        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(1, collection.size());
+        assertTrue(collection.contains(a));
+
+        collection = new ArrayList<>(Arrays.asList(null, a));
+        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(1, collection.size());
+        assertTrue(collection.contains(a));
+
+        collection = new ArrayList<>(Arrays.asList(null, null));
+        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(0, collection.size());
+
+        collection = new ArrayList<>(Arrays.asList(a, b, c));
+        assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(3, collection.size());
+        assertTrue(collection.contains(a));
+        assertTrue(collection.contains(b));
+        assertTrue(collection.contains(c));
+
+        collection = new ArrayList<>(Arrays.asList(a, b, null));
+        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(2, collection.size());
+        assertTrue(collection.contains(a));
+        assertTrue(collection.contains(b));
+
+        collection = new ArrayList<>(Arrays.asList(a, null, b));
+        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(2, collection.size());
+        assertTrue(collection.contains(a));
+        assertTrue(collection.contains(b));
+
+        collection = new ArrayList<>(Arrays.asList(null, a, b));
+        assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(2, collection.size());
+        assertTrue(collection.contains(a));
+        assertTrue(collection.contains(b));
+
+        collection = new ArrayList<>(Arrays.asList(a, null, null));
+        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(1, collection.size());
+        assertTrue(collection.contains(a));
+
+        collection = new ArrayList<>(Arrays.asList(null, null, a));
+        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(1, collection.size());
+        assertTrue(collection.contains(a));
+
+        collection = new ArrayList<>(Arrays.asList(null, a, null));
+        assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(1, collection.size());
+        assertTrue(collection.contains(a));
+
+        collection = new ArrayList<>(Arrays.asList(null, null, null));
+        assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
+        assertEquals(0, collection.size());
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() {
+        ArrayUtils.throwsIfOutOfBounds(10, 2, 6);
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() {
+        ArrayUtils.throwsIfOutOfBounds(10, 0, 10);
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() {
+        ArrayUtils.throwsIfOutOfBounds(10, 0, 0);
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() {
+        ArrayUtils.throwsIfOutOfBounds(10, 10, 0);
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_passesWhenEmptyArray() {
+        ArrayUtils.throwsIfOutOfBounds(0, 0, 0);
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() {
+        try {
+            ArrayUtils.throwsIfOutOfBounds(10, -1, 5);
+            fail();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            // expected
+        }
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_failsWhenCountNegative() {
+        try {
+            ArrayUtils.throwsIfOutOfBounds(10, 5, -1);
+            fail();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            // expected
+        }
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() {
+        try {
+            ArrayUtils.throwsIfOutOfBounds(10, 11, 0);
+            fail();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            // expected
+        }
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() {
+        try {
+            ArrayUtils.throwsIfOutOfBounds(10, 5, 6);
+            fail();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            // expected
+        }
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_failsWhenLengthNegative() {
+        try {
+            ArrayUtils.throwsIfOutOfBounds(-1, 0, 0);
+            fail();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            // expected
+        }
+    }
+
+    @SmallTest
+    public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() {
+        try {
+            ArrayUtils.throwsIfOutOfBounds(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+            fail();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            // expected
+        }
+    }
 }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ae1c9461..505d2a3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1075,6 +1075,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1075136930": {
+      "message": "startLockTaskMode: Can't lock due to auth",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "-1069336896": {
       "message": "onRootTaskOrderChanged(): rootTask=%s",
       "level": "DEBUG",
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f4f5e1e..8ae0d3d 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1244,14 +1244,16 @@
             nativePtrs[i++] = entry.getValue().native_instance;
             writeString(namesBytes, entry.getKey());
         }
-        int typefacesBytesCount = nativeWriteTypefaces(null, nativePtrs);
         // int (typefacesBytesCount), typefaces, namesBytes
+        final int typefaceBytesCountSize = Integer.BYTES;
+        int typefacesBytesCount = nativeWriteTypefaces(null, typefaceBytesCountSize, nativePtrs);
         SharedMemory sharedMemory = SharedMemory.create(
-                "fontMap", Integer.BYTES + typefacesBytesCount + namesBytes.size());
+                "fontMap", typefaceBytesCountSize + typefacesBytesCount + namesBytes.size());
         ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN);
         try {
             writableBuffer.putInt(typefacesBytesCount);
-            int writtenBytesCount = nativeWriteTypefaces(writableBuffer.slice(), nativePtrs);
+            int writtenBytesCount =
+                    nativeWriteTypefaces(writableBuffer, writableBuffer.position(), nativePtrs);
             if (writtenBytesCount != typefacesBytesCount) {
                 throw new IOException(String.format("Unexpected bytes written: %d, expected: %d",
                         writtenBytesCount, typefacesBytesCount));
@@ -1276,7 +1278,9 @@
             @NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out)
             throws IOException {
         int typefacesBytesCount = buffer.getInt();
-        long[] nativePtrs = nativeReadTypefaces(buffer.slice());
+        // Note: Do not call buffer.slice(), as nativeReadTypefaces() expects
+        // that buffer.address() is page-aligned.
+        long[] nativePtrs = nativeReadTypefaces(buffer, buffer.position());
         if (nativePtrs == null) {
             throw new IOException("Could not read typefaces");
         }
@@ -1598,9 +1602,10 @@
     private static native void nativeRegisterGenericFamily(String str, long nativePtr);
 
     private static native int nativeWriteTypefaces(
-            @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs);
+            @Nullable ByteBuffer buffer, int position, @NonNull long[] nativePtrs);
 
-    private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer);
+    private static native
+            @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer, int position);
 
     private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
 
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 6939a72..47de37d 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -359,10 +359,10 @@
     /** @hide */
     @TestApi
     public Region getSafeZone() {
-        mMaskMatrix.reset();
+        Path mask = getIconMask();
         mMaskMatrix.setScale(SAFEZONE_SCALE, SAFEZONE_SCALE, getBounds().centerX(), getBounds().centerY());
         Path p = new Path();
-        mMask.transform(mMaskMatrix, p);
+        mask.transform(mMaskMatrix, p);
         Region safezoneRegion = new Region(getBounds());
         safezoneRegion.setPath(p, safezoneRegion);
         return safezoneRegion;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 420d606..586e3a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -495,14 +495,15 @@
                                         mPipBoundsState.getBounds(),
                                         mPipBoundsState.getAspectRatio());
                         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
-                        mPipTaskOrganizer.scheduleAnimateResizePip(destinationBounds,
-                                mEnterAnimationDuration,
-                                null /* updateBoundsCallback */);
-
-                        mTouchHandler.onAspectRatioChanged();
-                        updateMovementBounds(null /* toBounds */, false /* fromRotation */,
-                                false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
-                                null /* windowContainerTransaction */);
+                        if (!destinationBounds.equals(mPipBoundsState.getBounds())) {
+                            mPipTaskOrganizer.scheduleAnimateResizePip(destinationBounds,
+                                    mEnterAnimationDuration,
+                                    null /* updateBoundsCallback */);
+                            mTouchHandler.onAspectRatioChanged();
+                            updateMovementBounds(null /* toBounds */, false /* fromRotation */,
+                                    false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
+                                    null /* windowContainerTransaction */);
+                        }
                     }
 
                     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 3a6ce81..b212ea9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -122,9 +122,9 @@
             cancelScheduledPlacement();
             applyPlacementBounds(placement.getUnstashedBounds(), animationDuration);
         } else if (immediate) {
+            boolean shouldStash = mUnstashRunnable != null || placement.getTriggerStash();
             cancelScheduledPlacement();
-            applyPlacementBounds(placement.getBounds(), animationDuration);
-            scheduleUnstashIfNeeded(placement);
+            applyPlacement(placement, shouldStash, animationDuration);
         } else {
             applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
             schedulePinnedStackPlacement(placement, animationDuration);
@@ -176,22 +176,21 @@
                     "%s: applyPendingPlacement()", TAG);
         }
         if (mPendingPlacement != null) {
-            if (mPendingStash) {
-                mPendingStash = false;
-                scheduleUnstashIfNeeded(mPendingPlacement);
-            }
+            applyPlacement(mPendingPlacement, mPendingStash, mPendingPlacementAnimationDuration);
+            mPendingStash = false;
+            mPendingPlacement = null;
+        }
+    }
 
-            if (mUnstashRunnable != null) {
-                // currently stashed, use stashed pos
-                applyPlacementBounds(mPendingPlacement.getBounds(),
-                        mPendingPlacementAnimationDuration);
-            } else {
-                applyPlacementBounds(mPendingPlacement.getUnstashedBounds(),
-                        mPendingPlacementAnimationDuration);
-            }
+    private void applyPlacement(@NonNull final Placement placement, boolean shouldStash,
+            int animationDuration) {
+        if (placement.getStashType() != STASH_TYPE_NONE && shouldStash) {
+            scheduleUnstashIfNeeded(placement);
         }
 
-        mPendingPlacement = null;
+        Rect bounds =
+                mUnstashRunnable != null ? placement.getBounds() : placement.getUnstashedBounds();
+        applyPlacementBounds(bounds, animationDuration);
     }
 
     void onPipDismissed() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 1be17f9..de7e7bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -21,7 +21,6 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -86,7 +85,6 @@
 import com.android.wm.shell.sysui.KeyguardChangeListener;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.LegacyTransitions;
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
@@ -411,62 +409,13 @@
         }
 
         if (!ENABLE_SHELL_TRANSITIONS) {
-            startIntentLegacy(intent, fillInIntent, position, options);
+            mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
             return;
         }
 
         mStageCoordinator.startIntent(intent, fillInIntent, position, options);
     }
 
-    private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
-            @SplitPosition int position, @Nullable Bundle options) {
-        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
-        mStageCoordinator.prepareEvictChildTasks(position, evictWct);
-
-        LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
-            @Override
-            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
-                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-                    IRemoteAnimationFinishedCallback finishedCallback,
-                    SurfaceControl.Transaction t) {
-                if (apps == null || apps.length == 0) {
-                    // Switch the split position if launching as MULTIPLE_TASK failed.
-                    if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
-                        setSideStagePosition(SplitLayout.reversePosition(
-                                mStageCoordinator.getSideStagePosition()));
-                    }
-
-                    // Do nothing when the animation was cancelled.
-                    t.apply();
-                    return;
-                }
-
-                for (int i = 0; i < apps.length; ++i) {
-                    if (apps[i].mode == MODE_OPENING) {
-                        t.show(apps[i].leash);
-                    }
-                }
-                t.apply();
-
-                if (finishedCallback != null) {
-                    try {
-                        finishedCallback.onAnimationFinished();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Error finishing legacy transition: ", e);
-                    }
-                }
-
-                mSyncQueue.queue(evictWct);
-            }
-        };
-
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-        options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
-
-        wct.sendPendingIntent(intent, fillInIntent, options);
-        mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
-    }
-
     /** Returns {@code true} if it's launching the same component on both sides of the split. */
     @VisibleForTesting
     boolean isLaunchingAdjacently(@Nullable Intent startIntent,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2b3b61b..f2340d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -26,6 +26,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -118,6 +119,7 @@
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.transition.LegacyTransitions;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.util.SplitBounds;
 
@@ -195,7 +197,6 @@
     private boolean mExitSplitScreenOnHide;
     private boolean mIsDividerRemoteAnimating;
     private boolean mIsExiting;
-    private boolean mResizingSplits;
 
     /** The target stage to dismiss to when unlock after folded. */
     @StageType
@@ -210,7 +211,11 @@
 
                 @Override
                 public void onLeashReady(SurfaceControl leash) {
-                    mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+                    // This is for avoiding divider invisible due to delay of creating so only need
+                    // to do when divider should visible case.
+                    if (mDividerVisible) {
+                        mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+                    }
                 }
             };
 
@@ -433,6 +438,63 @@
                 });
     }
 
+    /** Launches an activity into split by legacy transition. */
+    void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
+            @SplitPosition int position, @androidx.annotation.Nullable Bundle options) {
+        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+        prepareEvictChildTasks(position, evictWct);
+
+        LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
+            @Override
+            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+                    IRemoteAnimationFinishedCallback finishedCallback,
+                    SurfaceControl.Transaction t) {
+                if (apps == null || apps.length == 0) {
+                    // Switch the split position if launching as MULTIPLE_TASK failed.
+                    if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+                        setSideStagePosition(SplitLayout.reversePosition(
+                                getSideStagePosition()), null);
+                    }
+
+                    // Do nothing when the animation was cancelled.
+                    t.apply();
+                    return;
+                }
+
+                for (int i = 0; i < apps.length; ++i) {
+                    if (apps[i].mode == MODE_OPENING) {
+                        t.show(apps[i].leash);
+                    }
+                }
+                t.apply();
+
+                if (finishedCallback != null) {
+                    try {
+                        finishedCallback.onAnimationFinished();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Error finishing legacy transition: ", e);
+                    }
+                }
+
+                mSyncQueue.queue(evictWct);
+            }
+        };
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
+
+        // If split still not active, apply windows bounds first to avoid surface reset to
+        // wrong pos by SurfaceAnimator from wms.
+        // TODO(b/223325631): check  is it still necessary after improve enter transition done.
+        if (!mMainStage.isActive()) {
+            updateWindowBounds(mSplitLayout, wct);
+        }
+
+        wct.sendPendingIntent(intent, fillInIntent, options);
+        mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+    }
+
     /** Starts 2 tasks in one transition. */
     void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
             @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@@ -849,6 +911,7 @@
                     .setWindowCrop(mSideStage.mRootLeash, null);
             t.setPosition(mMainStage.mRootLeash, 0, 0)
                     .setPosition(mSideStage.mRootLeash, 0, 0);
+            t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
             setDividerVisibility(false, t);
 
             // In this case, exit still under progress, fade out the split decor after first WCT
@@ -858,7 +921,7 @@
                     WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
                     mIsExiting = false;
                     childrenToTop.dismiss(finishedWCT, true /* toTop */);
-                    wct.reorder(mRootTaskInfo.token, false /* toTop */);
+                    finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
                     mTaskOrganizer.applyTransaction(finishedWCT);
                     onTransitionAnimationComplete();
                 });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index afc70a44..08eb2c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -154,8 +154,6 @@
 
     private final int mCurrentUserId;
 
-    private ScreenRotationAnimation mRotationAnimation;
-
     private Drawable mEnterpriseThumbnailDrawable;
 
     private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() {
@@ -340,12 +338,6 @@
 
         final Runnable onAnimFinish = () -> {
             if (!animations.isEmpty()) return;
-
-            if (mRotationAnimation != null) {
-                mRotationAnimation.kill();
-                mRotationAnimation = null;
-            }
-
             mAnimations.remove(transition);
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
         };
@@ -365,11 +357,8 @@
                     isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
                     final int anim = getRotationAnimation(info);
                     if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
-                        mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
-                                mTransactionPool, startTransaction, change, info.getRootLeash(),
-                                anim);
-                        mRotationAnimation.startAnimation(animations, onAnimFinish,
-                                mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+                        startRotationAnimation(startTransaction, change, info, anim, animations,
+                                onAnimFinish);
                         continue;
                     }
                 } else {
@@ -413,6 +402,13 @@
                     startTransaction.setWindowCrop(change.getLeash(),
                             change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
                 }
+                // Rotation change of independent non display window container.
+                if (change.getParent() == null
+                        && change.getStartRotation() != change.getEndRotation()) {
+                    startRotationAnimation(startTransaction, change, info,
+                            ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
+                    continue;
+                }
             }
 
             // Don't animate anything that isn't independent.
@@ -543,6 +539,31 @@
         }
     }
 
+    private void startRotationAnimation(SurfaceControl.Transaction startTransaction,
+            TransitionInfo.Change change, TransitionInfo info, int animHint,
+            ArrayList<Animator> animations, Runnable onAnimFinish) {
+        final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mSurfaceSession,
+                mTransactionPool, startTransaction, change, info.getRootLeash(), animHint);
+        // The rotation animation may consist of 3 animations: fade-out screenshot, fade-in real
+        // content, and background color. The item of "animGroup" will be removed if the sub
+        // animation is finished. Then if the list becomes empty, the rotation animation is done.
+        final ArrayList<Animator> animGroup = new ArrayList<>(3);
+        final ArrayList<Animator> animGroupStore = new ArrayList<>(3);
+        final Runnable finishCallback = () -> {
+            if (!animGroup.isEmpty()) return;
+            anim.kill();
+            animations.removeAll(animGroupStore);
+            onAnimFinish.run();
+        };
+        anim.startAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
+                mMainExecutor, mAnimExecutor);
+        for (int i = animGroup.size() - 1; i >= 0; i--) {
+            final Animator animator = animGroup.get(i);
+            animGroupStore.add(animator);
+            animations.add(animator);
+        }
+    }
+
     private void edgeExtendWindow(TransitionInfo.Change change,
             Animation a, SurfaceControl.Transaction startTransaction,
             SurfaceControl.Transaction finishTransaction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 1005ef1..a843b2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -84,7 +84,7 @@
     private final Context mContext;
     private final TransactionPool mTransactionPool;
     private final float[] mTmpFloats = new float[9];
-    /** The leash of display. */
+    /** The leash of the changing window container. */
     private final SurfaceControl mSurfaceControl;
     private final Rect mStartBounds = new Rect();
     private final Rect mEndBounds = new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e6006c4..cd29741 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -356,7 +356,7 @@
             // Put all the OPEN/SHOW on top
             if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
                 // Wallpaper is always at the bottom.
-                layer = 0;
+                layer = -zSplitLine;
             } else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
                 if (isOpening) {
                     // put on top
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 6c9ea7b..4877442 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -21,6 +21,7 @@
 import android.os.SystemClock
 import android.view.InputDevice
 import android.view.MotionEvent
+import android.view.ViewConfiguration
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.BySelector
 import androidx.test.uiautomator.UiDevice
@@ -43,12 +44,15 @@
         const val TIMEOUT_MS = 3_000L
         const val DRAG_DURATION_MS = 1_000L
         const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
+        const val DIVIDER_BAR = "docked_divider_handle"
         const val GESTURE_STEP_MS = 16L
 
         private val notificationScrollerSelector: BySelector
             get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
         private val notificationContentSelector: BySelector
             get() = By.text("Notification content")
+        private val dividerBarSelector: BySelector
+            get() = By.res(SYSTEM_UI_PACKAGE_NAME, DIVIDER_BAR)
 
         fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
             SplitScreenHelper(
@@ -79,16 +83,16 @@
             )
 
         fun waitForSplitComplete(
-                wmHelper: WindowManagerStateHelper,
-                primaryApp: IComponentMatcher,
-                secondaryApp: IComponentMatcher,
+            wmHelper: WindowManagerStateHelper,
+            primaryApp: IComponentMatcher,
+            secondaryApp: IComponentMatcher,
         ) {
             wmHelper.StateSyncBuilder()
-                    .withAppTransitionIdle()
-                    .withWindowSurfaceAppeared(primaryApp)
-                    .withWindowSurfaceAppeared(secondaryApp)
-                    .withSplitDividerVisible()
-                    .waitForAndVerify()
+                .withAppTransitionIdle()
+                .withWindowSurfaceAppeared(primaryApp)
+                .withWindowSurfaceAppeared(secondaryApp)
+                .withSplitDividerVisible()
+                .waitForAndVerify()
         }
 
         fun dragFromNotificationToSplit(
@@ -215,5 +219,26 @@
                 allApps.unfreeze()
             }
         }
+
+        fun dragDividerToDismissSplit(
+            device: UiDevice,
+            wmHelper: WindowManagerStateHelper
+        ) {
+            val displayBounds = wmHelper.currentState.layerState
+                .displays.firstOrNull { !it.isVirtual }
+                ?.layerStackSpace
+                ?: error("Display not found")
+            val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+            dividerBar.drag(Point(displayBounds.width * 4 / 5, displayBounds.height * 4 / 5))
+        }
+
+        fun doubleTapDividerToSwitch(device: UiDevice) {
+            val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+            val interval = (ViewConfiguration.getDoubleTapTimeout() +
+                ViewConfiguration.getDoubleTapMinTime()) / 2
+            dividerBar.click()
+            SystemClock.sleep(interval.toLong())
+            dividerBar.click()
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 77dcbe0..b71a9d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -69,7 +69,7 @@
      */
     @Presubmit
     @Test
-    fun focusDoesNotChange() {
+    fun focusChanges() {
         testSpec.assertEventLog {
             this.focusChanges("PipMenuView", "NexusLauncherActivity")
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
new file mode 100644
index 0000000..cd92db7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.wm.shell.flicker.appWindowBecomesInvisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesInvisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dismiss split screen by dragging the divider bar.
+ *
+ * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+    // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+    @Before
+    open fun before() {
+        Assume.assumeTrue(tapl.isTablet)
+    }
+
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            super.transition(this)
+            setup {
+                eachRun {
+                    tapl.goHome()
+                    primaryApp.launchViaIntent(wmHelper)
+                    // TODO(b/231399940): Use recent shortcut to enter split.
+                    tapl.launchedAppState.taskbar
+                        .openAllApps()
+                        .getAppIcon(secondaryApp.appName)
+                        .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+                    SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+                }
+            }
+            transitions {
+                SplitScreenHelper.dragDividerToDismissSplit(device, wmHelper)
+                wmHelper.StateSyncBuilder()
+                    .withAppTransitionIdle()
+                    .withFullScreenApp(secondaryApp)
+                    .waitForAndVerify()
+            }
+        }
+
+    @Presubmit
+    @Test
+    fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
+
+    @Presubmit
+    @Test
+    fun primaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
+
+    @Presubmit
+    @Test
+    fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
+
+    @Presubmit
+    @Test
+    fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
+        primaryApp, splitLeftTop = false)
+
+    @Presubmit
+    @Test
+    fun secondaryAppBoundsIsFullscreenAtEnd() {
+        testSpec.assertLayers {
+            this.isVisible(secondaryApp)
+                .then()
+                .isInvisible(secondaryApp)
+                .then()
+                .isVisible(secondaryApp)
+                .invoke("secondaryAppBoundsIsFullscreenAtEnd") {
+                    val displayBounds = WindowUtils.getDisplayBounds(testSpec.endRotation)
+                    it.visibleRegion(secondaryApp).coversExactly(displayBounds)
+                }
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun primaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(primaryApp)
+
+    @Presubmit
+    @Test
+    fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun entireScreenCovered() =
+        super.entireScreenCovered()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisibleAtStartAndEnd() =
+        super.navBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerPositionAtStartAndEnd() =
+        super.navBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() =
+        super.navBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsVisibleAtStartAndEnd() =
+        super.statusBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() =
+        super.statusBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() =
+        super.statusBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarLayerIsVisibleAtStartAndEnd() =
+        super.taskBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarWindowIsAlwaysVisible() =
+        super.taskBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+                supportedNavigationModes =
+                    listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 4f48ff0..127ac1e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -66,24 +66,24 @@
                     primaryApp.launchViaIntent(wmHelper)
                     // TODO(b/231399940): Use recent shortcut to enter split.
                     tapl.launchedAppState.taskbar
-                            .openAllApps()
-                            .getAppIcon(secondaryApp.appName)
-                            .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+                        .openAllApps()
+                        .getAppIcon(secondaryApp.appName)
+                        .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
                     SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
                 }
             }
             transitions {
                 tapl.goHome()
                 wmHelper.StateSyncBuilder()
-                        .withAppTransitionIdle()
-                        .withHomeActivityVisible()
-                        .waitForAndVerify()
+                    .withAppTransitionIdle()
+                    .withHomeActivityVisible()
+                    .waitForAndVerify()
             }
         }
 
     @Presubmit
     @Test
-    fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesInvisible()
+    fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
 
     @Presubmit
     @Test
@@ -115,67 +115,67 @@
     @Postsubmit
     @Test
     override fun entireScreenCovered() =
-            super.entireScreenCovered()
+        super.entireScreenCovered()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun navBarLayerIsVisibleAtStartAndEnd() =
-            super.navBarLayerIsVisibleAtStartAndEnd()
+        super.navBarLayerIsVisibleAtStartAndEnd()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun navBarLayerPositionAtStartAndEnd() =
-            super.navBarLayerPositionAtStartAndEnd()
+        super.navBarLayerPositionAtStartAndEnd()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun navBarWindowIsAlwaysVisible() =
-            super.navBarWindowIsAlwaysVisible()
+        super.navBarWindowIsAlwaysVisible()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun statusBarLayerIsVisibleAtStartAndEnd() =
-            super.statusBarLayerIsVisibleAtStartAndEnd()
+        super.statusBarLayerIsVisibleAtStartAndEnd()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun statusBarLayerPositionAtStartAndEnd() =
-            super.statusBarLayerPositionAtStartAndEnd()
+        super.statusBarLayerPositionAtStartAndEnd()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun statusBarWindowIsAlwaysVisible() =
-            super.statusBarWindowIsAlwaysVisible()
+        super.statusBarWindowIsAlwaysVisible()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun taskBarLayerIsVisibleAtStartAndEnd() =
-            super.taskBarLayerIsVisibleAtStartAndEnd()
+        super.taskBarLayerIsVisibleAtStartAndEnd()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun taskBarWindowIsAlwaysVisible() =
-            super.taskBarWindowIsAlwaysVisible()
+        super.taskBarWindowIsAlwaysVisible()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     /** {@inheritDoc} */
     @Postsubmit
     @Test
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 0000000..38279a3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test double tap the divider bar to switch the two apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+    // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+    @Before
+    open fun before() {
+        Assume.assumeTrue(tapl.isTablet)
+    }
+
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            super.transition(this)
+            setup {
+                eachRun {
+                    primaryApp.launchViaIntent(wmHelper)
+                    // TODO(b/231399940): Use recent shortcut to enter split.
+                    tapl.launchedAppState.taskbar
+                        .openAllApps()
+                        .getAppIcon(secondaryApp.appName)
+                        .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+                    SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+                }
+            }
+            transitions {
+                SplitScreenHelper.doubleTapDividerToSwitch(device)
+                wmHelper.StateSyncBuilder()
+                    .withAppTransitionIdle()
+                    .waitForAndVerify()
+            }
+        }
+
+    @Presubmit
+    @Test
+    fun splitScreenDividerKeepVisible() {
+        testSpec.assertLayers {
+            this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+    @Presubmit
+    @Test
+    fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
+
+    @Presubmit
+    @Test
+    fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+        primaryApp, splitLeftTop = true)
+
+    @Presubmit
+    @Test
+    fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+        secondaryApp, splitLeftTop = false)
+
+    @Presubmit
+    @Test
+    fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
+
+    @Presubmit
+    @Test
+    fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun entireScreenCovered() =
+        super.entireScreenCovered()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisibleAtStartAndEnd() =
+        super.navBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerPositionAtStartAndEnd() =
+        super.navBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() =
+        super.navBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsVisibleAtStartAndEnd() =
+        super.statusBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() =
+        super.statusBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() =
+        super.statusBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarLayerIsVisibleAtStartAndEnd() =
+        super.taskBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarWindowIsAlwaysVisible() =
+        super.taskBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+                supportedNavigationModes =
+                    listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
new file mode 100644
index 0000000..c48f3f7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test switch back to split pair after go home
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+    // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+    @Before
+    open fun before() {
+        Assume.assumeTrue(tapl.isTablet)
+    }
+
+    override val transition: FlickerBuilder.() -> Unit
+    get() = {
+        super.transition(this)
+        setup {
+            eachRun {
+                primaryApp.launchViaIntent(wmHelper)
+                // TODO(b/231399940): Use recent shortcut to enter split.
+                tapl.launchedAppState.taskbar
+                    .openAllApps()
+                    .getAppIcon(secondaryApp.appName)
+                    .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+                SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+                tapl.goHome()
+                wmHelper.StateSyncBuilder()
+                    .withAppTransitionIdle()
+                    .withHomeActivityVisible()
+                    .waitForAndVerify()
+            }
+        }
+        transitions {
+            tapl.workspace.quickSwitchToPreviousApp()
+            SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+    @Presubmit
+    @Test
+    fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+    @Presubmit
+    @Test
+    fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
+
+    @Presubmit
+    @Test
+    fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+        primaryApp, splitLeftTop = false)
+
+    @Presubmit
+    @Test
+    fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+        secondaryApp, splitLeftTop = true)
+
+    @Presubmit
+    @Test
+    fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
+
+    @Presubmit
+    @Test
+    fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun entireScreenCovered() =
+        super.entireScreenCovered()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisibleAtStartAndEnd() =
+        super.navBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerPositionAtStartAndEnd() =
+        super.navBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() =
+        super.navBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsVisibleAtStartAndEnd() =
+        super.statusBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() =
+        super.statusBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() =
+        super.statusBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarLayerIsVisibleAtStartAndEnd() =
+        super.taskBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarWindowIsAlwaysVisible() =
+        super.taskBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+                supportedNavigationModes =
+                    listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 05e4722..cc51efd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -179,6 +179,16 @@
     }
 
     @Test
+    fun testImmediatePlacement_DoNotStashIfAlreadyUnstashed() {
+        triggerImmediatePlacement(STASHED_PLACEMENT_RESTASH)
+        assertMovement(STASHED_BOUNDS)
+        assertMovementAt(time + STASH_DURATION, ANCHOR_BOUNDS)
+
+        triggerImmediatePlacement(STASHED_PLACEMENT)
+        assertNoMovementUpTo(time + FAR_FUTURE)
+    }
+
+    @Test
     fun testInMoveMode_KeepAtAnchor() {
         startMoveMode()
         triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 19efc5f..6a7119b 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -140,15 +140,13 @@
 
 static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
     Typeface* face = toTypeface(faceHandle);
-    const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags();
-    const size_t length = tagSet.size();
+    const size_t length = face->fFontCollection->getSupportedAxesCount();
     if (length == 0) {
         return nullptr;
     }
     std::vector<jint> tagVec(length);
-    int index = 0;
-    for (const auto& tag : tagSet) {
-        tagVec[index++] = tag;
+    for (size_t i = 0; i < length; i++) {
+        tagVec[i] = face->fFontCollection->getSupportedAxisAt(i);
     }
     std::sort(tagVec.begin(), tagVec.end());
     const jintArray result = env->NewIntArray(length);
@@ -305,7 +303,8 @@
     }
 }
 
-static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
+static jint Typeface_writeTypefaces(JNIEnv* env, jobject, jobject buffer, jint position,
+                                    jlongArray faceHandles) {
     MinikinFontSkiaFactory::init();
     ScopedLongArrayRO faces(env, faceHandles);
     std::vector<Typeface*> typefaces;
@@ -314,7 +313,12 @@
         typefaces.push_back(toTypeface(faces[i]));
     }
     void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
-    minikin::BufferWriter writer(addr);
+    if (addr != nullptr &&
+        reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+        ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+        return 0;
+    }
+    minikin::BufferWriter writer(addr, position);
     std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections;
     std::unordered_map<std::shared_ptr<minikin::FontCollection>, size_t> fcToIndex;
     for (Typeface* typeface : typefaces) {
@@ -334,11 +338,18 @@
     return static_cast<jint>(writer.size());
 }
 
-static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
+static jlongArray Typeface_readTypefaces(JNIEnv* env, jobject, jobject buffer, jint position) {
     MinikinFontSkiaFactory::init();
     void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
-    if (addr == nullptr) return nullptr;
-    minikin::BufferReader reader(addr);
+    if (addr == nullptr) {
+        ALOGE("Passed a null buffer.");
+        return nullptr;
+    }
+    if (reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+        ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+        return nullptr;
+    }
+    minikin::BufferReader reader(addr, position);
     std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
             minikin::FontCollection::readVector(&reader);
     uint32_t typefaceCount = reader.read<uint32_t>();
@@ -357,7 +368,6 @@
     return result;
 }
 
-
 static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName,
         jobject typeface) {
     ScopedUtfChars fieldNameChars(env, fieldName);
@@ -417,8 +427,8 @@
         {"nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes},
         {"nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
          (void*)Typeface_registerGenericFamily},
-        {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
-        {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+        {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;I[J)I", (void*)Typeface_writeTypefaces},
+        {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;I)[J", (void*)Typeface_readTypefaces},
         {"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
          (void*)Typeface_forceSetStaticFinalField},
         {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 2c421f8..f17129c 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -228,7 +228,7 @@
 static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
     FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
     minikin::BufferReader reader = font->font->typefaceMetadataReader();
-    if (reader.data() != nullptr) {
+    if (reader.current() != nullptr) {
         std::string path = std::string(reader.readString());
         if (path.empty()) {
             return nullptr;
@@ -270,7 +270,7 @@
 static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
     FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
     minikin::BufferReader reader = font->font->typefaceMetadataReader();
-    if (reader.data() != nullptr) {
+    if (reader.current() != nullptr) {
         reader.skipString();  // fontPath
         return reader.read<int>();
     } else {
@@ -283,7 +283,7 @@
 static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
     FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
     minikin::BufferReader reader = font->font->typefaceMetadataReader();
-    if (reader.data() != nullptr) {
+    if (reader.current() != nullptr) {
         reader.skipString();  // fontPath
         reader.skip<int>();   // fontIndex
         return reader.readArray<minikin::FontVariation>().second;
@@ -298,7 +298,7 @@
     FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
     minikin::BufferReader reader = font->font->typefaceMetadataReader();
     minikin::FontVariation var;
-    if (reader.data() != nullptr) {
+    if (reader.current() != nullptr) {
         reader.skipString();  // fontPath
         reader.skip<int>();   // fontIndex
         var = reader.readArray<minikin::FontVariation>().first[index];
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 955bfcc..444366a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -65,6 +65,8 @@
 
     private static final String TAG = "AudioSystem";
 
+    private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
     // private constructor to prevent instantiating AudioSystem
     private AudioSystem() {
         throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
@@ -243,6 +245,8 @@
     public static final int AUDIO_FORMAT_LDAC           = 0x23000000;
     /** @hide */
     public static final int AUDIO_FORMAT_LC3            = 0x2B000000;
+    /** @hide */
+    public static final int AUDIO_FORMAT_OPUS           = 0x08000000;
 
 
     /** @hide */
@@ -254,7 +258,9 @@
             AUDIO_FORMAT_APTX,
             AUDIO_FORMAT_APTX_HD,
             AUDIO_FORMAT_LDAC,
-            AUDIO_FORMAT_LC3}
+            AUDIO_FORMAT_LC3,
+            AUDIO_FORMAT_OPUS
+           }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioFormatNativeEnumForBtCodec {}
@@ -287,6 +293,7 @@
             case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
             case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
             case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
+            case AUDIO_FORMAT_OPUS: return SOURCE_CODEC_TYPE_OPUS; // TODO update in U
             default:
                 Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
                         + " for conversion to BT codec");
@@ -329,6 +336,8 @@
                 return AudioSystem.AUDIO_FORMAT_LDAC;
             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
                 return AudioSystem.AUDIO_FORMAT_LC3;
+            case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+                return AudioSystem.AUDIO_FORMAT_OPUS;
             default:
                 Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
                         + " for conversion to audio format");
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 472586b..fde7afd 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -18,7 +18,11 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.graphics.GraphicBuffer;
 import android.graphics.ImageFormat;
 import android.graphics.ImageFormat.Format;
@@ -29,6 +33,7 @@
 import android.hardware.HardwareBuffer.Usage;
 import android.hardware.SyncFence;
 import android.hardware.camera2.MultiResolutionImageReader;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
@@ -87,6 +92,38 @@
 
     /**
      * <p>
+     * Flag to gate correct exception thrown by {@code #detachImage}.
+     * </p>
+     * <p>
+     * {@code #detachImage} is documented as throwing {@link java.lang.IllegalStateException} in
+     * the event of an error; a native helper method to this threw
+     * {@link java.lang.RuntimeException} if the surface was abandoned while detaching the
+     * {@code Image}.
+     * <p>
+     * This previously undocumented exception behavior continues through Android T.
+     * </p>
+     * <p>
+     * After Android T, the native helper method only throws {@code IllegalStateExceptions} in
+     * accordance with the documentation.
+     * </p>
+     * <p>
+     * {@code #detachImage} will now throw only ISEs if it runs into errors while detaching
+     * the image. Behavior on apps targeting API levels <= T remains unchanged.
+     * </p>
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    private static final long DETACH_THROWS_ISE_ONLY = 236825255L;
+
+    /**
+     * Cached value of {@link #DETACH_THROWS_ISE_ONLY} flag to prevent repeated calls when
+     * detaching image.
+     */
+    private final boolean mDetachThrowsIseOnly =
+            CompatChanges.isChangeEnabled(DETACH_THROWS_ISE_ONLY);
+
+    /**
+     * <p>
      * Create a new reader for images of the desired size and format.
      * </p>
      * <p>
@@ -825,10 +862,10 @@
      * </p>
      * <p>
      * After this call, the ImageReader no longer owns this image, and the image
-     * ownership can be transfered to another entity like {@link ImageWriter}
+     * ownership can be transferred to another entity like {@link ImageWriter}
      * via {@link ImageWriter#queueInputImage}. It's up to the new owner to
      * release the resources held by this image. For example, if the ownership
-     * of this image is transfered to an {@link ImageWriter}, the image will be
+     * of this image is transferred to an {@link ImageWriter}, the image will be
      * freed by the ImageWriter after the image data consumption is done.
      * </p>
      * <p>
@@ -849,16 +886,22 @@
      * @throws IllegalStateException If the ImageReader or image have been
      *             closed, or the has been detached, or has not yet been
      *             acquired.
+     * @throws RuntimeException If there is an error detaching {@code Image} from {@code Surface}.
+     *              {@code RuntimeException} is only thrown for applications targeting SDK <=
+     *              {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+     *              For applications targeting SDK >
+     *              {@link android.os.Build.VERSION_CODES#TIRAMISU},
+     *              this method only throws {@code IllegalStateException}.
      * @hide
      */
-     public void detachImage(Image image) {
-       if (image == null) {
+    public void detachImage(@Nullable Image image) {
+        if (image == null) {
            throw new IllegalArgumentException("input image must not be null");
-       }
-       if (!isImageOwnedbyMe(image)) {
+        }
+        if (!isImageOwnedbyMe(image)) {
            throw new IllegalArgumentException("Trying to detach an image that is not owned by"
                    + " this ImageReader");
-       }
+        }
 
         SurfaceImage si = (SurfaceImage) image;
         si.throwISEIfImageIsInvalid();
@@ -867,7 +910,7 @@
             throw new IllegalStateException("Image was already detached from this ImageReader");
         }
 
-        nativeDetachImage(image);
+        nativeDetachImage(image, mDetachThrowsIseOnly);
         si.clearSurfacePlanes();
         si.mPlanes = null;
         si.setDetached(true);
@@ -1408,7 +1451,7 @@
     private synchronized native void nativeClose();
     private synchronized native void nativeReleaseImage(Image i);
     private synchronized native Surface nativeGetSurface();
-    private synchronized native int nativeDetachImage(Image i);
+    private synchronized native int nativeDetachImage(Image i, boolean throwISEOnly);
     private synchronized native void nativeDiscardFreeBuffers();
 
     /**
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 9bf126b..31fb8d0 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@
     ISessionController getController();
     void setFlags(int flags);
     void setActive(boolean active);
-    void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
+    void setMediaButtonReceiver(in PendingIntent mbr);
     void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
     void setLaunchPendingIntent(in PendingIntent pi);
     void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 9e265d8..1bd12af 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@
     @Deprecated
     public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
         try {
-            mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
+            mBinder.setMediaButtonReceiver(mbr);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
         }
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 62c0d55..556f98c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -643,7 +643,8 @@
     return ACQUIRE_SUCCESS;
 }
 
-static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
+static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image,
+                                    jboolean throwISEOnly) {
     ALOGV("%s:", __FUNCTION__);
     JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
     if (ctx == NULL) {
@@ -666,8 +667,12 @@
     res = bufferConsumer->detachBuffer(buffer->mSlot);
     if (res != OK) {
         ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
-        jniThrowRuntimeException(env,
-                "nativeDetachImage failed for image!!!");
+        if ((bool) throwISEOnly) {
+            jniThrowException(env, "java/lang/IllegalStateException",
+                    "nativeDetachImage failed for image!!!");
+        } else {
+             jniThrowRuntimeException(env, "nativeDetachImage failed for image!!!");
+        }
         return res;
     }
     return OK;
@@ -965,7 +970,7 @@
     {"nativeReleaseImage",     "(Landroid/media/Image;)V",   (void*)ImageReader_imageRelease },
     {"nativeImageSetup",       "(Landroid/media/Image;Z)I",   (void*)ImageReader_imageSetup },
     {"nativeGetSurface",       "()Landroid/view/Surface;",   (void*)ImageReader_getSurface },
-    {"nativeDetachImage",      "(Landroid/media/Image;)I",   (void*)ImageReader_detachImage },
+    {"nativeDetachImage",      "(Landroid/media/Image;Z)I",   (void*)ImageReader_detachImage },
     {"nativeCreateImagePlanes",
         "(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
                                                              (void*)ImageReader_createImagePlanes },
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index 60f0e9e..cb74cfc 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -86,6 +86,7 @@
 ?audio/x-matroska mka
 ?audio/x-pn-realaudio ra
 ?audio/x-mpeg mp3
+?audio/mp3 mp3
 
 ?image/bmp bmp
 ?image/gif gif
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
index a596a9a..7c3f5a5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -28,8 +28,7 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 
     <View
         android:id="@+id/divider1"
@@ -44,8 +43,7 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 
     <View
         android:id="@+id/divider2"
@@ -60,8 +58,7 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 
     <View
         android:id="@+id/divider3"
@@ -76,6 +73,5 @@
         style="@style/SettingsLibActionButton"
         android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:hyphenationFrequency="normalFast"/>
+        android:layout_weight="1"/>
 </LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
new file mode 100644
index 0000000..b4640ee
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="8dp"
+    android:paddingHorizontal="8dp"
+    android:orientation="horizontal">
+
+    <Button
+        android:id="@+id/button1"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+
+    <View
+        android:id="@+id/divider1"
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="4dp"
+        android:visibility="gone"
+        android:background="?android:colorBackground" />
+
+    <Button
+        android:id="@+id/button2"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+
+    <View
+        android:id="@+id/divider2"
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="4dp"
+        android:visibility="gone"
+        android:background="?android:colorBackground" />
+
+    <Button
+        android:id="@+id/button3"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+
+    <View
+        android:id="@+id/divider3"
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="4dp"
+        android:visibility="gone"
+        android:background="?android:colorBackground" />
+
+    <Button
+        android:id="@+id/button4"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:lineBreakWordStyle="phrase"
+        android:hyphenationFrequency="normalFast"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
new file mode 100644
index 0000000..8975857
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="start|center_vertical"
+        android:minWidth="@dimen/secondary_app_icon_size"
+        android:orientation="horizontal"
+        android:paddingEnd="16dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="@dimen/secondary_app_icon_size"
+            android:layout_height="@dimen/secondary_app_icon_size"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textDirection="locale"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"/>
+
+        <TextView
+            android:id="@+id/appendix"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:visibility="gone"/>
+
+        <ProgressBar
+            android:id="@android:id/progress"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:max="100"
+            android:visibility="gone"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical|end"
+        android:minWidth="@dimen/two_target_min_width"
+        android:orientation="vertical"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
new file mode 100644
index 0000000..3a219b9
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingBottom="16dp"
+    android:paddingTop="8dp"
+    android:clickable="false">
+
+    <TextView
+        android:id="@+id/apps_top_intro_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textDirection="locale"
+        android:clickable="false"
+        android:longClickable="false"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"
+        android:textAppearance="@style/TextAppearance.TopIntroText" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 12db901..e65f7de 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -55,7 +55,6 @@
             android:ellipsize="marquee"
             android:fadingEdge="horizontal"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"/>
 
         <TextView
@@ -63,7 +62,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:textDirection="locale"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:textColor="?android:attr/textColorSecondary"/>
 
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index ec091bf..f4af9c7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -19,6 +19,7 @@
 import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
 
 import android.app.ActionBar;
+import android.graphics.text.LineBreakConfig;
 import android.os.Build;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -88,6 +89,12 @@
             mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                 mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+                mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+                        builder.setLineBreakConfig(
+                                new LineBreakConfig.Builder()
+                                        .setLineBreakWordStyle(
+                                                LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+                                        .build()));
             }
         }
         disableCollapsingToolbarLayoutScrollingBehavior();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index a8c7a3f..522de93 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -22,6 +22,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.text.LineBreakConfig;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -110,6 +111,12 @@
             mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                 mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+                mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+                        builder.setLineBreakConfig(
+                                new LineBreakConfig.Builder()
+                                        .setLineBreakWordStyle(
+                                                LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+                                        .build()));
             }
             if (!TextUtils.isEmpty(mToolbarTitle)) {
                 mCollapsingToolbarLayout.setTitle(mToolbarTitle);
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 2c1fdd4..42700b3 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -53,7 +53,6 @@
             android:paddingTop="16dp"
             android:paddingBottom="8dp"
             android:textColor="?android:attr/textColorSecondary"
-            android:hyphenationFrequency="normalFast"
             android:ellipsize="marquee" />
 
         <com.android.settingslib.widget.LinkTextView
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
new file mode 100644
index 0000000..a2f2510
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:orientation="vertical"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="56dp"
+        android:gravity="start|top"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="16dp"
+        android:paddingBottom="4dp">
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="16dp"
+            android:paddingBottom="8dp"
+            android:textColor="?android:attr/textColorSecondary"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:ellipsize="marquee" />
+
+        <com.android.settingslib.widget.LinkTextView
+            android:id="@+id/settingslib_learn_more"
+            android:text="@string/settingslib_learn_more_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingBottom="8dp"
+            android:clickable="true"
+            android:visibility="gone"
+            style="@style/TextAppearance.Footer.Title.SettingsLib"/>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
new file mode 100644
index 0000000..35d1323
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="?android:attr/colorBackground"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/frame"
+        android:minHeight="@dimen/settingslib_min_switch_bar_height"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_margin="@dimen/settingslib_switchbar_margin"
+        android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+        android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+        <TextView
+            android:id="@+id/switch_text"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_marginEnd="@dimen/settingslib_switch_title_margin"
+            android:layout_marginVertical="@dimen/settingslib_switch_title_margin"
+            android:layout_gravity="center_vertical"
+            android:ellipsize="end"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            style="@style/MainSwitchText.Settingslib" />
+
+        <ImageView
+            android:id="@+id/restricted_icon"
+            android:layout_width="@dimen/settingslib_restricted_icon_size"
+            android:layout_height="@dimen/settingslib_restricted_icon_size"
+            android:tint="?android:attr/colorAccent"
+            android:layout_gravity="center_vertical"
+            android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
+            android:src="@drawable/settingslib_ic_info"
+            android:visibility="gone" />
+
+        <Switch
+            android:id="@android:id/switch_widget"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:layout_gravity="center_vertical"
+            android:focusable="false"
+            android:clickable="false"
+            android:theme="@style/Switch.SettingsLib"/>
+    </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
new file mode 100644
index 0000000..bb8ac28
--- /dev/null
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="20dp"
+        android:gravity="center"
+        android:minWidth="56dp"
+        android:orientation="vertical"/>
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:minWidth="32dp"
+        android:orientation="horizontal"
+        android:layout_marginEnd="16dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            settings:maxWidth="@dimen/secondary_app_icon_size"
+            settings:maxHeight="@dimen/secondary_app_icon_size"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <LinearLayout
+            android:id="@+id/summary_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone">
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewStart"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textColor="?android:attr/textColorSecondary"/>
+
+            <TextView
+                android:id="@+id/appendix"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewEnd"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="1"
+                android:visibility="gone"
+                android:ellipsize="end"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/radio_extra_widget_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+        <View
+            android:layout_width=".75dp"
+            android:layout_height="32dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            android:background="?android:attr/dividerVertical" />
+        <ImageView
+            android:id="@+id/radio_extra_widget"
+            android:layout_width="match_parent"
+            android:minWidth="@dimen/two_target_min_width"
+            android:layout_height="fill_parent"
+            android:src="@drawable/ic_settings_accent"
+            android:contentDescription="@string/settings_label"
+            android:paddingStart="24dp"
+            android:paddingEnd="24dp"
+            android:layout_gravity="center"
+            android:background="?android:attr/selectableItemBackground" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index 64d100a..906ff2c 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -66,7 +66,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"/>
 
         <LinearLayout
@@ -81,7 +80,6 @@
                 android:layout_weight="1"
                 android:textAppearance="?android:attr/textAppearanceSmall"
                 android:textAlignment="viewStart"
-                android:hyphenationFrequency="normalFast"
                 android:textColor="?android:attr/textColorSecondary"/>
 
             <TextView
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
new file mode 100644
index 0000000..0b27464
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="20dp"
+        android:gravity="center"
+        android:minWidth="56dp"
+        android:orientation="vertical"/>
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:minWidth="32dp"
+        android:orientation="horizontal"
+        android:layout_marginEnd="16dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            settings:maxWidth="@dimen/secondary_app_icon_size"
+            settings:maxHeight="@dimen/secondary_app_icon_size"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <LinearLayout
+            android:id="@+id/summary_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone">
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewStart"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textColor="?android:attr/textColorSecondary"/>
+
+            <TextView
+                android:id="@+id/appendix"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewEnd"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="1"
+                android:visibility="gone"
+                android:ellipsize="end"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/selector_extra_widget_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+        <View
+            android:layout_width=".75dp"
+            android:layout_height="32dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            android:background="?android:attr/dividerVertical" />
+        <ImageView
+            android:id="@+id/selector_extra_widget"
+            android:layout_width="match_parent"
+            android:minWidth="@dimen/two_target_min_width"
+            android:layout_height="fill_parent"
+            android:src="@drawable/ic_settings_accent"
+            android:contentDescription="@string/settings_label"
+            android:paddingStart="24dp"
+            android:paddingEnd="24dp"
+            android:layout_gravity="center"
+            android:background="?android:attr/selectableItemBackground" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
index 2a550ef..8bb56ff 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
@@ -66,7 +66,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"/>
 
         <LinearLayout
@@ -81,7 +80,6 @@
                 android:layout_weight="1"
                 android:textAppearance="?android:attr/textAppearanceSmall"
                 android:textAlignment="viewStart"
-                android:hyphenationFrequency="normalFast"
                 android:textColor="?android:attr/textColorSecondary"/>
 
             <TextView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index d4d466a..23aa993 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -43,7 +43,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"/>
 
@@ -58,7 +57,6 @@
             android:textAlignment="viewStart"
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10"
-            android:hyphenationFrequency="normalFast"
             style="@style/PreferenceSummaryTextStyle"/>
 
     </RelativeLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
new file mode 100644
index 0000000..70ce374
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:baselineAligned="false">
+
+    <include layout="@layout/settingslib_icon_frame"/>
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignLeft="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            style="@style/PreferenceSummaryTextStyle"/>
+
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingLeft="16dp"
+        android:paddingStart="16dp"
+        android:paddingRight="0dp"
+        android:paddingEnd="0dp"
+        android:orientation="vertical"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
new file mode 100644
index 0000000..6046d91
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingBottom="16dp"
+    android:paddingTop="8dp"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:clickable="false"
+        android:longClickable="false"
+        android:maxLines="10"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"
+        android:textAppearance="@style/TextAppearance.TopIntroText"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index b2a9037..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -33,6 +33,5 @@
         android:clickable="false"
         android:longClickable="false"
         android:maxLines="10"
-        android:hyphenationFrequency="normalFast"
         android:textAppearance="@style/TextAppearance.TopIntroText"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
index ac5807d..2c35772 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
@@ -41,7 +41,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"/>
 
@@ -53,7 +52,6 @@
             android:layout_alignStart="@android:id/title"
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
-            android:hyphenationFrequency="normalFast"
             android:maxLines="10"/>
 
     </RelativeLayout>
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
new file mode 100644
index 0000000..0bca9ab
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipToPadding="false">
+
+    <include layout="@layout/settingslib_icon_frame"/>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:maxLines="10"/>
+
+    </RelativeLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_access_point.xml b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
new file mode 100644
index 0000000..81bfeffd
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<!-- Based off preference_two_target.xml with Material ripple moved to parent for full ripple. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="start|center_vertical"
+        android:clipToPadding="false">
+
+        <LinearLayout
+            android:id="@+id/icon_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:minWidth="48dp"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp">
+            <androidx.preference.internal.PreferenceImageView
+                android:id="@android:id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                settings:maxWidth="48dp"
+                settings:maxHeight="48dp" />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:ellipsize="marquee" />
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_alignStart="@android:id/title"
+                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:maxLines="10" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+    <ImageButton
+        android:id="@+id/icon_button"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:minHeight="@dimen/min_tap_target_size"
+        android:layout_gravity="center"
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone">
+    </ImageButton>
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
new file mode 100644
index 0000000..7ad018c
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="@android:color/transparent"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="?android:attr/selectableItemBackground"
+        android:gravity="start|center_vertical"
+        android:clipToPadding="false">
+
+        <LinearLayout
+            android:id="@+id/checkbox_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:minWidth="48dp"
+            android:minHeight="48dp"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp">
+            <include layout="@layout/preference_widget_checkbox" />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:ellipsize="marquee" />
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_alignStart="@android:id/title"
+                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:maxLines="10" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
new file mode 100644
index 0000000..31e9696
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="56dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <com.android.internal.widget.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxWidth="48dp"
+            android:maxHeight="48dp" />
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee" />
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:maxLines="10" />
+
+        <TextView
+            android:id="@+id/additional_summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/summary"
+            android:layout_alignStart="@android:id/summary"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:visibility="gone" />
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingStart="16dp"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/preference_access_point.xml b/packages/SettingsLib/res/layout/preference_access_point.xml
index 4ad9d80..802d604 100644
--- a/packages/SettingsLib/res/layout/preference_access_point.xml
+++ b/packages/SettingsLib/res/layout/preference_access_point.xml
@@ -65,7 +65,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
-                android:hyphenationFrequency="normalFast"
                 android:textAppearance="?android:attr/textAppearanceListItem"
                 android:ellipsize="marquee" />
 
@@ -77,7 +76,6 @@
                 android:layout_alignStart="@android:id/title"
                 android:textAppearance="?android:attr/textAppearanceListItemSecondary"
                 android:textColor="?android:attr/textColorSecondary"
-                android:hyphenationFrequency="normalFast"
                 android:maxLines="10" />
 
         </RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
index cbe49cd..f512f9b 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -62,7 +62,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
-                android:hyphenationFrequency="normalFast"
                 android:textAppearance="?android:attr/textAppearanceListItem"
                 android:ellipsize="marquee" />
 
@@ -74,7 +73,6 @@
                 android:layout_alignStart="@android:id/title"
                 android:textAppearance="?android:attr/textAppearanceListItemSecondary"
                 android:textColor="?android:attr/textColorSecondary"
-                android:hyphenationFrequency="normalFast"
                 android:maxLines="10" />
 
         </RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_switch_preference.xml b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
index edea144..169ae97 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -52,7 +52,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee" />
 
@@ -63,7 +62,6 @@
             android:layout_alignStart="@android:id/title"
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
-                  android:hyphenationFrequency="normalFast"
             android:maxLines="10" />
 
         <TextView android:id="@+id/additional_summary"
@@ -74,7 +72,6 @@
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10"
-                  android:hyphenationFrequency="normalFast"
             android:visibility="gone" />
     </RelativeLayout>
 
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 29a1831..663e8e4 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -154,6 +154,8 @@
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
         <item>LDAC</item>
+        <item>LC3</item>
+        <item>Opus</item>
     </string-array>
 
     <!-- Values for Bluetooth Audio Codec selection preference. -->
@@ -164,6 +166,8 @@
         <item>2</item>
         <item>3</item>
         <item>4</item>
+        <item>5</item>
+        <item>6</item>
     </string-array>
 
     <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
@@ -174,6 +178,8 @@
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
         <item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
         <item>LDAC</item>
+        <item>LC3</item>
+        <item>Opus</item>
     </string-array>
 
     <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index df19c67..1940986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -43,6 +43,8 @@
 public class A2dpProfile implements LocalBluetoothProfile {
     private static final String TAG = "A2dpProfile";
 
+    private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
     private Context mContext;
 
     private BluetoothA2dp mService;
@@ -328,6 +330,12 @@
            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
                index = 5;
                break;
+            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+                index = 6;
+                break;
+            case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+                index = 7;
+                break;
            }
 
         if (index < 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 1a08366..b416738 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -44,6 +44,8 @@
     private final Handler mReceiverHandler;
     private final MobileTelephonyCallback mTelephonyCallback;
 
+    private boolean mListening = false;
+
     /**
      * MobileStatusTracker constructors
      *
@@ -76,6 +78,7 @@
      * Config the MobileStatusTracker to start or stop monitoring platform signals.
      */
     public void setListening(boolean listening) {
+        mListening = listening;
         if (listening) {
             mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
         } else {
@@ -83,6 +86,10 @@
         }
     }
 
+    public boolean isListening() {
+        return mListening;
+    }
+
     private void updateDataSim() {
         int activeDataSubId = mDefaults.getActiveDataSubId();
         if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
diff --git a/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..3dd997f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..80aba01
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..b3f89ed
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..efa2cb9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/edit_text_filled.xml b/packages/SystemUI/res/drawable/edit_text_filled.xml
new file mode 100644
index 0000000..cca34d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/edit_text_filled.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="4dp"
+       android:insetRight="4dp"
+       android:insetTop="10dp"
+       android:insetBottom="10dp">
+    <selector>
+        <item android:state_enabled="false">
+            <nine-patch android:src="@drawable/textfield_default_filled"
+                android:tint="?android:attr/colorControlNormal" />
+        </item>
+        <item android:state_pressed="false" android:state_focused="false">
+            <nine-patch android:src="@drawable/textfield_default_filled"
+                android:tint="?android:attr/colorControlNormal" />
+        </item>
+        <item>
+            <nine-patch android:src="@drawable/textfield_default_filled"
+                android:tint="?android:attr/colorControlActivated" />
+        </item>
+    </selector>
+</inset>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index da76c8d..bc8e540 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -18,44 +18,70 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:gravity="center_horizontal"
+    android:orientation="horizontal"
     android:elevation="@dimen/biometric_dialog_elevation">
 
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Title"/>
+    <RelativeLayout
+        android:id="@+id/auth_credential_header"
+        style="@style/AuthCredentialHeaderStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent">
 
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Subtitle"/>
+        <ImageView
+            android:id="@+id/icon"
+            style="@style/TextAppearance.AuthNonBioCredential.Icon"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:contentDescription="@null"/>
 
-    <TextView
-        android:id="@+id/description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Description"/>
+        <TextView
+            android:id="@+id/title"
+            style="@style/TextAppearance.AuthNonBioCredential.Title"
+            android:layout_below="@id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
 
-    <ImeAwareEditText
-        android:id="@+id/lockPassword"
-        android:layout_width="208dp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:minHeight="48dp"
-        android:gravity="center"
-        android:inputType="textPassword"
-        android:maxLength="500"
-        android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
-        style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
+        <TextView
+            android:id="@+id/subtitle"
+            style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+            android:layout_below="@id/title"
+            android:layout_alignParentLeft="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
 
-    <TextView
-        android:id="@+id/error"
-        android:layout_width="match_parent"
+        <TextView
+            android:id="@+id/description"
+            style="@style/TextAppearance.AuthNonBioCredential.Description"
+            android:layout_below="@id/subtitle"
+            android:layout_alignParentLeft="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/auth_credential_input"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Error"/>
+        android:orientation="vertical">
+
+        <ImeAwareEditText
+            android:id="@+id/lockPassword"
+            style="@style/TextAppearance.AuthCredential.PasswordEntry"
+            android:layout_width="208dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+            android:inputType="textPassword"
+            android:minHeight="48dp" />
+
+        <TextView
+            android:id="@+id/error"
+            style="@style/TextAppearance.AuthNonBioCredential.Error"
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
 
 </com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 0ff1db2..75a80bc 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -22,68 +22,63 @@
     android:orientation="vertical">
 
     <RelativeLayout
+        android:id="@+id/auth_credential_header"
+        style="@style/AuthCredentialHeaderStyle"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="match_parent">
+
+        <ImageView
+            android:id="@+id/icon"
+            style="@style/TextAppearance.AuthNonBioCredential.Icon"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:contentDescription="@null"/>
+
+        <TextView
+            android:id="@+id/title"
+            style="@style/TextAppearance.AuthNonBioCredential.Title"
+            android:layout_below="@id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+
+        <TextView
+            android:id="@+id/subtitle"
+            style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+            android:layout_below="@id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+
+        <TextView
+            android:id="@+id/description"
+            style="@style/TextAppearance.AuthNonBioCredential.Description"
+            android:layout_below="@id/subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/auth_credential_input"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:orientation="vertical">
 
-        <LinearLayout
-            android:id="@+id/auth_credential_header"
-            style="@style/AuthCredentialHeaderStyle"
-            android:layout_width="match_parent"
+        <ImeAwareEditText
+            android:id="@+id/lockPassword"
+            style="@style/TextAppearance.AuthCredential.PasswordEntry"
+            android:layout_width="208dp"
             android:layout_height="wrap_content"
-            android:layout_alignParentTop="true">
+            android:layout_gravity="center_horizontal"
+            android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+            android:inputType="textPassword"
+            android:minHeight="48dp" />
 
-            <ImageView
-                android:id="@+id/icon"
-                android:layout_width="48dp"
-                android:layout_height="48dp"
-                android:contentDescription="@null" />
-
-            <TextView
-                android:id="@+id/title"
-                style="@style/TextAppearance.AuthNonBioCredential.Title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content" />
-
-            <TextView
-                android:id="@+id/subtitle"
-                style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content" />
-
-            <TextView
-                android:id="@+id/description"
-                style="@style/TextAppearance.AuthNonBioCredential.Description"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content" />
-
-        </LinearLayout>
-
-        <LinearLayout
+        <TextView
+            android:id="@+id/error"
+            style="@style/TextAppearance.AuthNonBioCredential.Error"
+            android:layout_gravity="center_horizontal"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:gravity="center"
-            android:orientation="vertical"
-            android:layout_alignParentBottom="true">
+            android:layout_height="wrap_content" />
 
-            <ImeAwareEditText
-                android:id="@+id/lockPassword"
-                style="@style/TextAppearance.AuthCredential.PasswordEntry"
-                android:layout_width="208dp"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
-                android:inputType="textPassword"
-                android:minHeight="48dp" />
-
-            <TextView
-                android:id="@+id/error"
-                style="@style/TextAppearance.AuthNonBioCredential.Error"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-
-        </LinearLayout>
-
-    </RelativeLayout>
+    </LinearLayout>
 
 </com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index 8919198..ac9a947 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -18,4 +18,13 @@
     <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
         <item name="android:layout_width">360dp</item>
     </style>
+
+    <style name="AuthCredentialHeaderStyle">
+        <item name="android:paddingStart">48dp</item>
+        <item name="android:paddingEnd">24dp</item>
+        <item name="android:paddingTop">48dp</item>
+        <item name="android:paddingBottom">10dp</item>
+        <item name="android:gravity">top|center_horizontal</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ddbe6d6..60932a7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -940,6 +940,9 @@
     <!-- Y translation for credential contents when animating in -->
     <dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
 
+    <!-- Biometric Auth Credential values -->
+    <dimen name="biometric_auth_icon_size">48dp</dimen>
+
     <!-- Starting text size in sp of batteryLevel for wireless charging animation -->
     <item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">
         0
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2227978..f7acf06 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -198,6 +198,11 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
+    <style name="TextAppearance.AuthNonBioCredential.Icon">
+        <item name="android:layout_width">@dimen/biometric_auth_icon_size</item>
+        <item name="android:layout_height">@dimen/biometric_auth_icon_size</item>
+    </style>
+
     <style name="TextAppearance.AuthNonBioCredential.Title">
         <item name="android:fontFamily">google-sans</item>
         <item name="android:layout_marginTop">20dp</item>
@@ -227,14 +232,16 @@
 
     <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:gravity">center</item>
+        <item name="android:paddingTop">28dp</item>
         <item name="android:singleLine">true</item>
         <item name="android:textColor">?android:attr/colorForeground</item>
         <item name="android:textSize">24sp</item>
+        <item name="android:background">@drawable/edit_text_filled</item>
     </style>
 
     <style name="AuthCredentialHeaderStyle">
         <item name="android:paddingStart">48dp</item>
-        <item name="android:paddingEnd">24dp</item>
+        <item name="android:paddingEnd">48dp</item>
         <item name="android:paddingTop">28dp</item>
         <item name="android:paddingBottom">20dp</item>
         <item name="android:orientation">vertical</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 916526d..ee30972 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -19,7 +19,6 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 import static android.app.ActivityTaskManager.getService;
 
 import android.annotation.NonNull;
@@ -27,7 +26,6 @@
 import android.app.Activity;
 import android.app.ActivityClient;
 import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -337,7 +335,8 @@
      * Shows a voice session identified by {@code token}
      * @return true if the session was shown, false otherwise
      */
-    public boolean showVoiceSession(IBinder token, Bundle args, int flags) {
+    public boolean showVoiceSession(@NonNull IBinder token, @NonNull Bundle args, int flags,
+            @Nullable String attributionTag) {
         IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface(
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
         if (service == null) {
@@ -346,7 +345,7 @@
         args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime());
 
         try {
-            return service.showSessionFromSession(token, args, flags);
+            return service.showSessionFromSession(token, args, flags, attributionTag);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 7c2673c..57ffdab 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -310,7 +310,8 @@
 
     private void startVoiceInteractor(Bundle args) {
         mAssistUtils.showSessionForActiveService(args,
-                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, null, null);
+                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mContext.getAttributionTag(),
+                null, null);
     }
 
     public void launchVoiceAssistFromKeyguard() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 0892612..5ed8986 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -16,12 +16,20 @@
 
 package com.android.systemui.biometrics;
 
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowInsets.Type.ime;
+
 import android.annotation.NonNull;
 import android.content.Context;
+import android.graphics.Insets;
 import android.os.UserHandle;
 import android.text.InputType;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.ImeAwareEditText;
@@ -31,18 +39,24 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 
+import java.io.PrintWriter;
+
 /**
  * Pin and Password UI
  */
 public class AuthCredentialPasswordView extends AuthCredentialView
-        implements TextView.OnEditorActionListener {
+        implements TextView.OnEditorActionListener, OnApplyWindowInsetsListener, Dumpable {
 
     private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView";
 
     private final InputMethodManager mImm;
     private ImeAwareEditText mPasswordField;
+    private ViewGroup mAuthCredentialHeader;
+    private ViewGroup mAuthCredentialInput;
+    private int mBottomInset = 0;
 
     public AuthCredentialPasswordView(Context context,
             AttributeSet attrs) {
@@ -53,6 +67,9 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+
+        mAuthCredentialHeader = findViewById(R.id.auth_credential_header);
+        mAuthCredentialInput = findViewById(R.id.auth_credential_input);
         mPasswordField = findViewById(R.id.lockPassword);
         mPasswordField.setOnEditorActionListener(this);
         // TODO: De-dupe the logic with AuthContainerView
@@ -66,6 +83,8 @@
             }
             return true;
         });
+
+        setOnApplyWindowInsetsListener(this);
     }
 
     @Override
@@ -127,4 +146,65 @@
             mPasswordField.setText("");
         }
     }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        if (mAuthCredentialInput == null || mAuthCredentialHeader == null
+                || mSubtitleView == null || mPasswordField == null || mErrorView == null) {
+            return;
+        }
+
+        // b/157910732 In AuthContainerView#getLayoutParams() we used to prevent jank risk when
+        // resizing by IME show or hide, we used to setFitInsetsTypes `~WindowInsets.Type.ime()` to
+        // LP. As a result this view needs to listen onApplyWindowInsets() and handle onLayout.
+        int inputLeftBound;
+        int inputTopBound;
+        int headerRightBound = right;
+        if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
+            inputTopBound = (bottom - (mPasswordField.getHeight() + mErrorView.getHeight())) / 2;
+            inputLeftBound = (right - left) / 2;
+            headerRightBound = inputLeftBound;
+        } else {
+            inputTopBound = mSubtitleView.getBottom() + (bottom - mSubtitleView.getBottom()) / 2;
+            inputLeftBound = (right - left - mAuthCredentialInput.getWidth()) / 2;
+        }
+
+        mAuthCredentialHeader.layout(left, top, headerRightBound, bottom);
+        mAuthCredentialInput.layout(inputLeftBound, inputTopBound, right, bottom);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int newHeight = MeasureSpec.getSize(heightMeasureSpec) - mBottomInset;
+
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), newHeight);
+
+        measureChildren(widthMeasureSpec,
+                MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.AT_MOST));
+    }
+
+    @NonNull
+    @Override
+    public WindowInsets onApplyWindowInsets(@NonNull View v, WindowInsets insets) {
+
+        final Insets bottomInset = insets.getInsets(ime());
+        if (v instanceof AuthCredentialPasswordView && mBottomInset != bottomInset.bottom) {
+            mBottomInset = bottomInset.bottom;
+            requestLayout();
+        }
+        return insets;
+    }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println(TAG + "State:");
+        pw.println("  mBottomInset=" + mBottomInset);
+        pw.println("  mAuthCredentialHeader size=(" + mAuthCredentialHeader.getWidth() + ","
+                + mAuthCredentialHeader.getHeight());
+        pw.println("  mAuthCredentialInput size=(" + mAuthCredentialInput.getWidth() + ","
+                + mAuthCredentialInput.getHeight());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 4fa835e..d4176ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -87,7 +87,7 @@
     private boolean mShouldAnimateContents;
 
     private TextView mTitleView;
-    private TextView mSubtitleView;
+    protected TextView mSubtitleView;
     private TextView mDescriptionView;
     private ImageView mIconView;
     protected TextView mErrorView;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index fef7383..1c57480 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -75,12 +75,12 @@
         }
     private var radius: Float = 0f
         set(value) {
-            rippleShader.radius = value
+            rippleShader.setMaxSize(value * 2f, value * 2f)
             field = value
         }
     private var origin: PointF = PointF()
         set(value) {
-            rippleShader.origin = value
+            rippleShader.setCenter(value.x, value.y)
             field = value
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 8292e52..da675de 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.graphics.PixelFormat
-import android.graphics.PointF
 import android.os.SystemProperties
 import android.util.DisplayMetrics
 import android.view.View
@@ -85,7 +84,7 @@
     private var debounceLevel = 0
 
     @VisibleForTesting
-    var rippleView: RippleView = RippleView(context, attrs = null)
+    var rippleView: RippleView = RippleView(context, attrs = null).also { it.setupShader() }
 
     init {
         pluggedIn = batteryController.isPluggedIn
@@ -177,20 +176,25 @@
         context.display.getRealMetrics(displayMetrics)
         val width = displayMetrics.widthPixels
         val height = displayMetrics.heightPixels
-        rippleView.radius = Integer.max(width, height).toFloat()
-        rippleView.origin = when (RotationUtils.getExactRotation(context)) {
+        val maxDiameter = Integer.max(width, height) * 2f
+        rippleView.setMaxSize(maxDiameter, maxDiameter)
+        when (RotationUtils.getExactRotation(context)) {
             RotationUtils.ROTATION_LANDSCAPE -> {
-                PointF(width * normalizedPortPosY, height * (1 - normalizedPortPosX))
+                rippleView.setCenter(
+                        width * normalizedPortPosY, height * (1 - normalizedPortPosX))
             }
             RotationUtils.ROTATION_UPSIDE_DOWN -> {
-                PointF(width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
+                rippleView.setCenter(
+                        width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
             }
             RotationUtils.ROTATION_SEASCAPE -> {
-                PointF(width * (1 - normalizedPortPosY), height * normalizedPortPosX)
+                rippleView.setCenter(
+                        width * (1 - normalizedPortPosY), height * normalizedPortPosX)
             }
             else -> {
                 // ROTATION_NONE
-                PointF(width * normalizedPortPosX, height * normalizedPortPosY)
+                rippleView.setCenter(
+                        width * normalizedPortPosX, height * normalizedPortPosY)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index f6368ee..65400c2 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -21,7 +21,6 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.PointF;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
@@ -34,6 +33,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.ripple.RippleShader;
 import com.android.systemui.ripple.RippleView;
 
 import java.text.NumberFormat;
@@ -138,6 +138,8 @@
         animatorSetScrim.start();
 
         mRippleView = findViewById(R.id.wireless_charging_ripple);
+        // TODO: Make rounded box shape if the device is tablet.
+        mRippleView.setupShader(RippleShader.RippleShape.CIRCLE);
         OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View view) {
@@ -230,11 +232,11 @@
         if (mRippleView != null) {
             int width = getMeasuredWidth();
             int height = getMeasuredHeight();
-            mRippleView.setColor(
-                    Utils.getColorAttr(mRippleView.getContext(),
-                            android.R.attr.colorAccent).getDefaultColor());
-            mRippleView.setOrigin(new PointF(width / 2, height / 2));
-            mRippleView.setRadius(Math.max(width, height) * 0.5f);
+            mRippleView.setCenter(width * 0.5f, height * 0.5f);
+            float maxSize = Math.max(width, height);
+            mRippleView.setMaxSize(maxSize, maxSize);
+            mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
+                    android.R.attr.colorAccent).getDefaultColor());
         }
 
         super.onLayout(changed, left, top, right, bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 3d40e67..c92cf54 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -99,6 +99,12 @@
 
     public static final BooleanFlag LOCKSCREEN_CUSTOM_CLOCKS = new BooleanFlag(207, false);
 
+    /**
+     * Flag to enable the usage of the new bouncer data source. This is a refactor of and
+     * eventual replacement of KeyguardBouncer.java.
+     */
+    public static final BooleanFlag MODERN_BOUNCER = new BooleanFlag(208, true);
+
     /***************************************/
     // 300 - power menu
     public static final BooleanFlag POWER_MENU_LITE =
@@ -145,9 +151,6 @@
 
     /***************************************/
     // 600- status bar
-    public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
-            new BooleanFlag(601, false);
-
     public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
             new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
 
@@ -223,7 +226,11 @@
             new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
 
     public static final BooleanFlag NEW_BACK_AFFORDANCE =
-            new BooleanFlag(1203, false /* default */, true /* teamfood */);
+            new BooleanFlag(1203, false /* default */, false /* teamfood */);
+
+    // 1300 - screenshots
+
+    public static final BooleanFlag SCREENSHOT_REQUEST_PROCESSOR = new BooleanFlag(1300, false);
 
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 495f697..0f1ae00 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -19,7 +19,6 @@
 import android.annotation.SuppressLint
 import android.app.StatusBarManager
 import android.content.Context
-import android.graphics.PointF
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.media.MediaRoute2Info
@@ -202,10 +201,10 @@
         val height = windowBounds.height()
         val width = windowBounds.width()
 
-        rippleView.radius = height / 5f
+        val maxDiameter = height / 2.5f
+        rippleView.setMaxSize(maxDiameter, maxDiameter)
         // Center the ripple on the bottom of the screen in the middle.
-        rippleView.origin = PointF(/* x= */ width / 2f, /* y= */ height.toFloat())
-
+        rippleView.setCenter(width * 0.5f, height.toFloat())
         val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
         val colorWithAlpha = ColorUtils.setAlphaComponent(color, 70)
         rippleView.setColor(colorWithAlpha)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index fed546bf..6a505f0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -23,10 +23,10 @@
 /**
  * An expanding ripple effect for the media tap-to-transfer receiver chip.
  */
-class ReceiverChipRippleView(
-        context: Context?, attrs: AttributeSet?
-) : RippleView(context, attrs) {
+class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
     init {
+        // TODO: use RippleShape#ELLIPSE when calling setupShader.
+        setupShader()
         setRippleFill(true)
         duration = 3000L
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 6bc50a6..da9fefa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -48,6 +48,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.Dumpable;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -89,6 +90,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+    private final KeyguardViewController mKeyguardViewController;
     private final UserTracker mUserTracker;
     private final SystemActions mSystemActions;
     private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -123,6 +125,7 @@
             OverviewProxyService overviewProxyService,
             Lazy<AssistManager> assistManagerLazy,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+            KeyguardViewController keyguardViewController,
             NavigationModeController navigationModeController,
             UserTracker userTracker,
             DumpManager dumpManager) {
@@ -131,6 +134,7 @@
         mAccessibilityManager = accessibilityManager;
         mAssistManagerLazy = assistManagerLazy;
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+        mKeyguardViewController = keyguardViewController;
         mUserTracker = userTracker;
         mSystemActions = systemActions;
         accessibilityManager.addAccessibilityServicesStateChangeListener(this);
@@ -317,8 +321,12 @@
      * {@link InputMethodService} and the keyguard states.
      */
     public boolean isImeShown(int vis) {
-        View shadeWindowView = mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
-        boolean isKeyguardShowing = mCentralSurfacesOptionalLazy.get().get().isKeyguardShowing();
+        View shadeWindowView = null;
+        if (mCentralSurfacesOptionalLazy.get().isPresent()) {
+            shadeWindowView =
+                    mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
+        }
+        boolean isKeyguardShowing = mKeyguardViewController.isShowing();
         boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
                 && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
         return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index b05e75e..b44b4de 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -510,7 +510,6 @@
     private fun playCommitBackAnimation() {
         // Check if we should vibrate again
         if (previousState != GestureState.FLUNG) {
-            backCallback.triggerBack()
             velocityTracker!!.computeCurrentVelocity(1000)
             val isSlow = abs(velocityTracker!!.xVelocity) < 500
             val hasNotVibratedRecently =
@@ -519,6 +518,10 @@
                 vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
             }
         }
+        // Dispatch the actual back trigger
+        if (DEBUG) Log.d(TAG, "playCommitBackAnimation() invoked triggerBack() on backCallback")
+        backCallback.triggerBack()
+
         playAnimation(setGoneEndListener)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 057ed24..fc6dcd3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -60,6 +60,7 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -82,6 +83,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.systemui.util.Assert;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
@@ -191,6 +193,7 @@
     private final int mDisplayId;
 
     private final Executor mMainExecutor;
+    private final Executor mBackgroundExecutor;
 
     private final Rect mPipExcludedBounds = new Rect();
     private final Rect mNavBarOverlayExcludedBounds = new Rect();
@@ -251,6 +254,7 @@
     private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
     private Map<String, Integer> mVocab;
     private boolean mUseMLModel;
+    private boolean mMLModelIsLoading;
     // minimum width below which we do not run the model
     private int mMLEnableWidth;
     private float mMLModelThreshold;
@@ -318,6 +322,7 @@
             SysUiState sysUiState,
             PluginManager pluginManager,
             @Main Executor executor,
+            @Background Executor backgroundExecutor,
             BroadcastDispatcher broadcastDispatcher,
             ProtoTracer protoTracer,
             NavigationModeController navigationModeController,
@@ -334,6 +339,7 @@
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = executor;
+        mBackgroundExecutor = backgroundExecutor;
         mOverviewProxyService = overviewProxyService;
         mSysUiState = sysUiState;
         mPluginManager = pluginManager;
@@ -631,28 +637,63 @@
             return;
         }
 
-        if (newState) {
-            mBackGestureTfClassifierProvider = mBackGestureTfClassifierProviderProvider.get();
-            mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
-                    SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
-            if (mBackGestureTfClassifierProvider.isActive()) {
-                Trace.beginSection("EdgeBackGestureHandler#loadVocab");
-                mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
-                Trace.endSection();
-                mUseMLModel = true;
+        mUseMLModel = newState;
+
+        if (mUseMLModel) {
+            Assert.isMainThread();
+            if (mMLModelIsLoading) {
+                Log.d(TAG, "Model tried to load while already loading.");
                 return;
             }
-        }
-
-        mUseMLModel = false;
-        if (mBackGestureTfClassifierProvider != null) {
+            mMLModelIsLoading = true;
+            mBackgroundExecutor.execute(() -> loadMLModel());
+        } else if (mBackGestureTfClassifierProvider != null) {
             mBackGestureTfClassifierProvider.release();
             mBackGestureTfClassifierProvider = null;
+            mVocab = null;
         }
     }
 
+    private void loadMLModel() {
+        BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProviderProvider.get();
+        float threshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
+        Map<String, Integer> vocab = null;
+        if (provider != null && !provider.isActive()) {
+            provider.release();
+            provider = null;
+            Log.w(TAG, "Cannot load model because it isn't active");
+        }
+        if (provider != null) {
+            Trace.beginSection("EdgeBackGestureHandler#loadVocab");
+            vocab = provider.loadVocab(mContext.getAssets());
+            Trace.endSection();
+        }
+        BackGestureTfClassifierProvider finalProvider = provider;
+        Map<String, Integer> finalVocab = vocab;
+        mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
+    }
+
+    private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
+            Map<String, Integer> vocab, float threshold) {
+        Assert.isMainThread();
+        mMLModelIsLoading = false;
+        if (!mUseMLModel) {
+            // This can happen if the user disables Gesture Nav while the model is loading.
+            if (provider != null) {
+                provider.release();
+            }
+            Log.d(TAG, "Model finished loading but isn't needed.");
+            return;
+        }
+        mBackGestureTfClassifierProvider = provider;
+        mVocab = vocab;
+        mMLModelThreshold = threshold;
+    }
+
     private int getBackGesturePredictionsCategory(int x, int y, int app) {
-        if (app == -1) {
+        BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProvider;
+        if (provider == null || app == -1) {
             return -1;
         }
         int distanceFromEdge;
@@ -673,7 +714,7 @@
             new long[]{(long) y},
         };
 
-        mMLResults = mBackGestureTfClassifierProvider.predict(featuresVector);
+        mMLResults = provider.predict(featuresVector);
         if (mMLResults == -1) {
             return -1;
         }
@@ -1031,6 +1072,7 @@
         private final SysUiState mSysUiState;
         private final PluginManager mPluginManager;
         private final Executor mExecutor;
+        private final Executor mBackgroundExecutor;
         private final BroadcastDispatcher mBroadcastDispatcher;
         private final ProtoTracer mProtoTracer;
         private final NavigationModeController mNavigationModeController;
@@ -1050,6 +1092,7 @@
                        SysUiState sysUiState,
                        PluginManager pluginManager,
                        @Main Executor executor,
+                       @Background Executor backgroundExecutor,
                        BroadcastDispatcher broadcastDispatcher,
                        ProtoTracer protoTracer,
                        NavigationModeController navigationModeController,
@@ -1067,6 +1110,7 @@
             mSysUiState = sysUiState;
             mPluginManager = pluginManager;
             mExecutor = executor;
+            mBackgroundExecutor = backgroundExecutor;
             mBroadcastDispatcher = broadcastDispatcher;
             mProtoTracer = protoTracer;
             mNavigationModeController = navigationModeController;
@@ -1089,6 +1133,7 @@
                     mSysUiState,
                     mPluginManager,
                     mExecutor,
+                    mBackgroundExecutor,
                     mBroadcastDispatcher,
                     mProtoTracer,
                     mNavigationModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index ec0d081..eeb1010 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -134,18 +134,9 @@
         mQSCarrierGroupController
                 .setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
 
-        List<String> rssiIgnoredSlots;
-
-        if (mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            rssiIgnoredSlots = List.of(
-                    getResources().getString(com.android.internal.R.string.status_bar_no_calling),
-                    getResources().getString(com.android.internal.R.string.status_bar_call_strength)
-            );
-        } else {
-            rssiIgnoredSlots = List.of(
-                    getResources().getString(com.android.internal.R.string.status_bar_mobile)
-            );
-        }
+        List<String> rssiIgnoredSlots = List.of(
+                getResources().getString(com.android.internal.R.string.status_bar_mobile)
+        );
 
         mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
                 mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
index 2dac639..e925b54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -27,7 +27,6 @@
     @JvmField val contentDescription: String? = null,
     @JvmField val typeContentDescription: String? = null,
     @JvmField val roaming: Boolean = false,
-    @JvmField val providerModelBehavior: Boolean = false
 ) {
     /**
      * Changes the visibility of this state by returning a copy with the visibility changed.
@@ -41,4 +40,4 @@
         if (this.visible == visible) return this
         else return copy(visible = visible)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 592da65..703b95a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -45,7 +45,7 @@
     private View mSpacer;
     @Nullable
     private CellSignalState mLastSignalState;
-    private boolean mProviderModelInitialized = false;
+    private boolean mMobileSignalInitialized = false;
     private boolean mIsSingleCarrier;
 
     public QSCarrier(Context context) {
@@ -96,35 +96,25 @@
             mMobileRoaming.setImageTintList(colorStateList);
             mMobileSignal.setImageTintList(colorStateList);
 
-            if (state.providerModelBehavior) {
-                if (!mProviderModelInitialized) {
-                    mProviderModelInitialized = true;
-                    mMobileSignal.setImageDrawable(
-                            mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
-                }
-                mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
-                mMobileSignal.setContentDescription(state.contentDescription);
-            } else {
-                if (!mProviderModelInitialized) {
-                    mProviderModelInitialized = true;
-                    mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
-                }
-                mMobileSignal.setImageLevel(state.mobileSignalIconId);
-                StringBuilder contentDescription = new StringBuilder();
-                if (state.contentDescription != null) {
-                    contentDescription.append(state.contentDescription).append(", ");
-                }
-                if (state.roaming) {
-                    contentDescription
-                            .append(mContext.getString(R.string.data_connection_roaming))
-                            .append(", ");
-                }
-                // TODO: show mobile data off/no internet text for 5 seconds before carrier text
-                if (hasValidTypeContentDescription(state.typeContentDescription)) {
-                    contentDescription.append(state.typeContentDescription);
-                }
-                mMobileSignal.setContentDescription(contentDescription);
+            if (!mMobileSignalInitialized) {
+                mMobileSignalInitialized = true;
+                mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
             }
+            mMobileSignal.setImageLevel(state.mobileSignalIconId);
+            StringBuilder contentDescription = new StringBuilder();
+            if (state.contentDescription != null) {
+                contentDescription.append(state.contentDescription).append(", ");
+            }
+            if (state.roaming) {
+                contentDescription
+                        .append(mContext.getString(R.string.data_connection_roaming))
+                        .append(", ");
+            }
+            // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+            if (hasValidTypeContentDescription(state.typeContentDescription)) {
+                contentDescription.append(state.typeContentDescription);
+            }
+            mMobileSignal.setContentDescription(contentDescription);
         }
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 209d09d..6a8bf75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,10 +42,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -78,7 +75,6 @@
     private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
     private int[] mLastSignalLevel = new int[SIM_SLOTS];
     private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
-    private final boolean mProviderModel;
     private final CarrierConfigTracker mCarrierConfigTracker;
 
     private boolean mIsSingleCarrier;
@@ -90,9 +86,6 @@
     private final SignalCallback mSignalCallback = new SignalCallback() {
                 @Override
                 public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
-                    if (mProviderModel) {
-                        return;
-                    }
                     int slotIndex = getSlotIndex(indicators.subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -107,91 +100,12 @@
                             indicators.statusIcon.icon,
                             indicators.statusIcon.contentDescription,
                             indicators.typeContentDescription.toString(),
-                            indicators.roaming,
-                            mProviderModel
+                            indicators.roaming
                     );
                     mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
                 }
 
                 @Override
-                public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
-                    if (!mProviderModel) {
-                        return;
-                    }
-                    int slotIndex = getSlotIndex(subId);
-                    if (slotIndex >= SIM_SLOTS) {
-                        Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
-                        return;
-                    }
-                    if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-                        Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
-                        return;
-                    }
-
-                    boolean displayCallStrengthIcon =
-                            mCarrierConfigTracker.getCallStrengthConfig(subId);
-
-                    if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
-                        if (statusIcon.visible) {
-                            mInfos[slotIndex] = new CellSignalState(
-                                    true,
-                                    statusIcon.icon,
-                                    statusIcon.contentDescription,
-                                    "",
-                                    false,
-                                    mProviderModel);
-                        } else {
-                            // Whenever the no Calling & SMS state is cleared, switched to the last
-                            // known call strength icon.
-                            if (displayCallStrengthIcon) {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        mLastSignalLevel[slotIndex],
-                                        mLastSignalLevelDescription[slotIndex],
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            } else {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        R.drawable.ic_qs_sim_card,
-                                        "",
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            }
-                        }
-                        mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
-                    } else {
-                        mLastSignalLevel[slotIndex] = statusIcon.icon;
-                        mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
-                        // Only Shows the call strength icon when the no Calling & SMS icon is not
-                        // shown.
-                        if (mInfos[slotIndex].mobileSignalIconId
-                                != R.drawable.ic_qs_no_calling_sms) {
-                            if (displayCallStrengthIcon) {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        statusIcon.icon,
-                                        statusIcon.contentDescription,
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            } else {
-                                mInfos[slotIndex] = new CellSignalState(
-                                        true,
-                                        R.drawable.ic_qs_sim_card,
-                                        "",
-                                        "",
-                                        false,
-                                        mProviderModel);
-                            }
-                            mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
-                        }
-                    }
-                }
-
-                @Override
                 public void setNoSims(boolean hasNoSims, boolean simDetected) {
                     if (hasNoSims) {
                         for (int i = 0; i < SIM_SLOTS; i++) {
@@ -219,14 +133,8 @@
             @Background Handler bgHandler, @Main Looper mainLooper,
             NetworkController networkController,
             CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
-            CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
-            SlotIndexResolver slotIndexResolver) {
+            CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
 
-        if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            mProviderModel = true;
-        } else {
-            mProviderModel = false;
-        }
         mActivityStarter = activityStarter;
         mBgHandler = bgHandler;
         mNetworkController = networkController;
@@ -262,8 +170,7 @@
                     R.drawable.ic_qs_no_calling_sms,
                     context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
                     "",
-                    false,
-                    mProviderModel);
+                    false);
             mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
             mLastSignalLevelDescription[i] =
                     context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
@@ -351,8 +258,7 @@
             for (int i = 0; i < SIM_SLOTS; i++) {
                 if (mInfos[i].visible
                         && mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
-                    mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false,
-                            mProviderModel);
+                    mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
                 }
             }
         }
@@ -470,15 +376,13 @@
         private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
         private final Context mContext;
         private final CarrierConfigTracker mCarrierConfigTracker;
-        private final FeatureFlags mFeatureFlags;
         private final SlotIndexResolver mSlotIndexResolver;
 
         @Inject
         public Builder(ActivityStarter activityStarter, @Background Handler handler,
                 @Main Looper looper, NetworkController networkController,
                 CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
-                CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
-                SlotIndexResolver slotIndexResolver) {
+                CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
             mActivityStarter = activityStarter;
             mHandler = handler;
             mLooper = looper;
@@ -486,7 +390,6 @@
             mCarrierTextControllerBuilder = carrierTextControllerBuilder;
             mContext = context;
             mCarrierConfigTracker = carrierConfigTracker;
-            mFeatureFlags = featureFlags;
             mSlotIndexResolver = slotIndexResolver;
         }
 
@@ -498,7 +401,7 @@
         public QSCarrierGroupController build() {
             return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
                     mNetworkController, mCarrierTextControllerBuilder, mContext,
-                    mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver);
+                    mCarrierConfigTracker, mSlotIndexResolver);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f629a036..d5efe36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -806,7 +806,8 @@
             return;
         }
 
-        mTelephonyManager.setDataEnabled(enabled);
+        mTelephonyManager.setDataEnabledForReason(
+                TelephonyManager.DATA_ENABLED_REASON_USER, enabled);
         if (disableOtherSubscriptions) {
             final List<SubscriptionInfo> subInfoList =
                     mSubscriptionManager.getActiveSubscriptionInfoList();
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
index 93a2efc..0a8e6e2 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
@@ -20,92 +20,107 @@
 import android.util.MathUtils
 
 /**
- * Shader class that renders an expanding charging ripple effect. A charging ripple contains
- * three elements:
- * 1. an expanding filled circle that appears in the beginning and quickly fades away
+ * Shader class that renders an expanding ripple effect. The ripple contains three elements:
+ *
+ * 1. an expanding filled [RippleShape] that appears in the beginning and quickly fades away
  * 2. an expanding ring that appears throughout the effect
  * 3. an expanding ring-shaped area that reveals noise over #2.
  *
+ * The ripple shader will be default to the circle shape if not specified.
+ *
  * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
  */
-class RippleShader internal constructor() : RuntimeShader(SHADER) {
+class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.CIRCLE) :
+        RuntimeShader(buildShader(rippleShape)) {
+
+    /** Shapes that the [RippleShader] supports. */
+    enum class RippleShape {
+        CIRCLE,
+        ROUNDED_BOX,
+        ELLIPSE
+    }
+
     companion object {
-        private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
+        private const val SHADER_UNIFORMS = """uniform vec2 in_center;
+                uniform vec2 in_size;
                 uniform float in_progress;
-                uniform float in_maxRadius;
+                uniform float in_cornerRadius;
+                uniform float in_thickness;
                 uniform float in_time;
                 uniform float in_distort_radial;
                 uniform float in_distort_xy;
-                uniform float in_radius;
                 uniform float in_fadeSparkle;
-                uniform float in_fadeCircle;
+                uniform float in_fadeFill;
                 uniform float in_fadeRing;
                 uniform float in_blur;
                 uniform float in_pixelDensity;
                 layout(color) uniform vec4 in_color;
                 uniform float in_sparkle_strength;"""
-        private const val SHADER_LIB = """float triangleNoise(vec2 n) {
-                    n  = fract(n * vec2(5.3987, 5.4421));
-                    n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
-                    float xy = n.x * n.y;
-                    return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+
+        private const val SHADER_CIRCLE_MAIN = """vec4 main(vec2 p) {
+                vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
+                float radius = in_size.x * 0.5;
+                float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
+                float inside = soften(sdCircle(p_distorted-in_center, radius * 1.2), in_blur);
+                float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+                    * (1.-sparkleRing) * in_fadeSparkle;
+
+                float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+                float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+                float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+                vec4 ripple = in_color * rippleAlpha;
+                return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+            }
+        """
+
+        private const val SHADER_ROUNDED_BOX_MAIN = """vec4 main(vec2 p) {
+                float sparkleRing = soften(roundedBoxRing(p-in_center, in_size, in_cornerRadius,
+                    in_thickness), in_blur);
+                float inside = soften(sdRoundedBox(p-in_center, in_size * 1.2, in_cornerRadius),
+                    in_blur);
+                float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+                    * (1.-sparkleRing) * in_fadeSparkle;
+
+                float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+                float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+                float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+                vec4 ripple = in_color * rippleAlpha;
+                return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+            }
+        """
+
+        private const val SHADER_ELLIPSE_MAIN = """vec4 main(vec2 p) {
+                vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
+
+                float sparkleRing = soften(ellipseRing(p_distorted-in_center, in_size), in_blur);
+                float inside = soften(sdEllipse(p_distorted-in_center, in_size * 1.2), in_blur);
+                float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+                    * (1.-sparkleRing) * in_fadeSparkle;
+
+                float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+                float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+                float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+                vec4 ripple = in_color * rippleAlpha;
+                return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+            }
+        """
+
+        private const val CIRCLE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
+                SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.CIRCLE_SDF +
+                SHADER_CIRCLE_MAIN
+        private const val ROUNDED_BOX_SHADER = SHADER_UNIFORMS +
+                RippleShaderUtilLibrary.SHADER_LIB + SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+                SdfShaderLibrary.ROUNDED_BOX_SDF + SHADER_ROUNDED_BOX_MAIN
+        private const val ELLIPSE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
+                SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.ELLIPSE_SDF +
+                SHADER_ELLIPSE_MAIN
+
+        private fun buildShader(rippleShape: RippleShape): String =
+                when (rippleShape) {
+                    RippleShape.CIRCLE -> CIRCLE_SHADER
+                    RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
+                    RippleShape.ELLIPSE -> ELLIPSE_SHADER
                 }
-                const float PI = 3.1415926535897932384626;
-
-                float threshold(float v, float l, float h) {
-                  return step(l, v) * (1.0 - step(h, v));
-                }
-
-                float sparkles(vec2 uv, float t) {
-                  float n = triangleNoise(uv);
-                  float s = 0.0;
-                  for (float i = 0; i < 4; i += 1) {
-                    float l = i * 0.01;
-                    float h = l + 0.1;
-                    float o = smoothstep(n - l, h, n);
-                    o *= abs(sin(PI * o * (t + 0.55 * i)));
-                    s += o;
-                  }
-                  return s;
-                }
-
-                float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
-                  float blurHalf = blur * 0.5;
-                  float d = distance(uv, xy);
-                  return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
-                }
-
-                float softRing(vec2 uv, vec2 xy, float radius, float blur) {
-                  float thickness_half = radius * 0.25;
-                  float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
-                  float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
-                  return circle_outer - circle_inner;
-                }
-
-                vec2 distort(vec2 p, vec2 origin, float time,
-                    float distort_amount_radial, float distort_amount_xy) {
-                    float2 distance = origin - p;
-                    float angle = atan(distance.y, distance.x);
-                    return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
-                                    cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
-                             + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
-                                    cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
-                }"""
-        private const val SHADER_MAIN = """vec4 main(vec2 p) {
-                    vec2 p_distorted = distort(p, in_origin, in_time, in_distort_radial,
-                        in_distort_xy);
-
-                    // Draw shapes
-                    float sparkleRing = softRing(p_distorted, in_origin, in_radius, in_blur);
-                    float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
-                        * sparkleRing * in_fadeSparkle;
-                    float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
-                    float rippleAlpha = max(circle * in_fadeCircle,
-                        softRing(p_distorted, in_origin, in_radius, in_blur) * in_fadeRing) * 0.45;
-                    vec4 ripple = in_color * rippleAlpha;
-                    return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
-                }"""
-        private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
 
         private fun subProgress(start: Float, end: Float, progress: Float): Float {
             val min = Math.min(start, end)
@@ -116,22 +131,18 @@
     }
 
     /**
-     * Maximum radius of the ripple.
+     * Sets the center position of the ripple.
      */
-    var radius: Float = 0.0f
-        set(value) {
-            field = value
-            setFloatUniform("in_maxRadius", value)
-        }
+    fun setCenter(x: Float, y: Float) {
+        setFloatUniform("in_center", x, y)
+    }
 
-    /**
-     * Origin coordinate of the ripple.
-     */
-    var origin: PointF = PointF()
-        set(value) {
-            field = value
-            setFloatUniform("in_origin", value.x, value.y)
-        }
+    /** Max width of the ripple. */
+    private var maxSize: PointF = PointF()
+    fun setMaxSize(width: Float, height: Float) {
+        maxSize.x = width
+        maxSize.y = height
+    }
 
     /**
      * Progress of the ripple. Float value between [0, 1].
@@ -140,20 +151,27 @@
         set(value) {
             field = value
             setFloatUniform("in_progress", value)
-            setFloatUniform("in_radius",
-                    (1 - (1 - value) * (1 - value) * (1 - value))* radius)
+            val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value)
+
+            setFloatUniform("in_size", /* width= */ maxSize.x * curvedProg,
+                    /* height= */ maxSize.y * curvedProg)
+            setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f)
+            // radius should not exceed width and height values.
+            setFloatUniform("in_cornerRadius",
+                    Math.min(maxSize.x, maxSize.y) * curvedProg)
+
             setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
 
             val fadeIn = subProgress(0f, 0.1f, value)
             val fadeOutNoise = subProgress(0.4f, 1f, value)
             var fadeOutRipple = 0f
-            var fadeCircle = 0f
+            var fadeFill = 0f
             if (!rippleFill) {
-                fadeCircle = subProgress(0f, 0.2f, value)
+                fadeFill = subProgress(0f, 0.6f, value)
                 fadeOutRipple = subProgress(0.3f, 1f, value)
             }
             setFloatUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise))
-            setFloatUniform("in_fadeCircle", 1 - fadeCircle)
+            setFloatUniform("in_fadeFill", 1 - fadeFill)
             setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
         }
 
@@ -169,7 +187,7 @@
     /**
      * A hex value representing the ripple color, in the format of ARGB
      */
-    var color: Int = 0xffffff.toInt()
+    var color: Int = 0xffffff
         set(value) {
             field = value
             setColorUniform("in_color", value)
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
new file mode 100644
index 0000000..0cacbc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ripple
+
+/** A common utility functions that are used for computing [RippleShader]. */
+class RippleShaderUtilLibrary {
+    companion object {
+        const val SHADER_LIB = """
+            float triangleNoise(vec2 n) {
+                    n  = fract(n * vec2(5.3987, 5.4421));
+                    n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
+                    float xy = n.x * n.y;
+                    return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+                }
+                const float PI = 3.1415926535897932384626;
+
+                float sparkles(vec2 uv, float t) {
+                    float n = triangleNoise(uv);
+                    float s = 0.0;
+                    for (float i = 0; i < 4; i += 1) {
+                        float l = i * 0.01;
+                        float h = l + 0.1;
+                        float o = smoothstep(n - l, h, n);
+                        o *= abs(sin(PI * o * (t + 0.55 * i)));
+                        s += o;
+                    }
+                    return s;
+                }
+
+                vec2 distort(vec2 p, float time, float distort_amount_radial,
+                    float distort_amount_xy) {
+                        float angle = atan(p.y, p.x);
+                          return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
+                                    cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
+                             + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
+                                    cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
+            }"""
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index fc52464..83d9f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -23,39 +23,42 @@
 import android.content.res.Configuration
 import android.graphics.Canvas
 import android.graphics.Paint
-import android.graphics.PointF
 import android.util.AttributeSet
 import android.view.View
+import com.android.systemui.ripple.RippleShader.RippleShape
 
 private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+private const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
 
 /**
- * A generic expanding ripple effect. To trigger the ripple expansion, set [radius] and [origin],
- * then call [startRipple].
+ * A generic expanding ripple effect.
+ *
+ * Set up the shader with a desired [RippleShape] using [setupShader], [setMaxSize] and [setCenter],
+ * then call [startRipple] to trigger the ripple expansion.
  */
 open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
-    private val rippleShader = RippleShader()
-    private val defaultColor: Int = 0xffffffff.toInt()
+
+    private lateinit var rippleShader: RippleShader
+    private lateinit var rippleShape: RippleShape
     private val ripplePaint = Paint()
 
     var rippleInProgress: Boolean = false
-    var radius: Float = 0.0f
-        set(value) {
-            rippleShader.radius = value
-            field = value
-        }
-    var origin: PointF = PointF()
-        set(value) {
-            rippleShader.origin = value
-            field = value
-        }
     var duration: Long = 1750
 
-    init {
-        rippleShader.color = defaultColor
-        rippleShader.progress = 0f
-        rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
-        ripplePaint.shader = rippleShader
+    private var maxWidth: Float = 0.0f
+    private var maxHeight: Float = 0.0f
+    fun setMaxSize(maxWidth: Float, maxHeight: Float) {
+        this.maxWidth = maxWidth
+        this.maxHeight = maxHeight
+        rippleShader.setMaxSize(maxWidth, maxHeight)
+    }
+
+    private var centerX: Float = 0.0f
+    private var centerY: Float = 0.0f
+    fun setCenter(x: Float, y: Float) {
+        this.centerX = x
+        this.centerY = y
+        rippleShader.setCenter(x, y)
     }
 
     override fun onConfigurationChanged(newConfig: Configuration?) {
@@ -68,6 +71,18 @@
         super.onAttachedToWindow()
     }
 
+    /** Initializes the shader. Must be called before [startRipple]. */
+    fun setupShader(rippleShape: RippleShape = RippleShape.CIRCLE) {
+        this.rippleShape = rippleShape
+        rippleShader = RippleShader(rippleShape)
+
+        rippleShader.color = RIPPLE_DEFAULT_COLOR
+        rippleShader.progress = 0f
+        rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
+
+        ripplePaint.shader = rippleShader
+    }
+
     @JvmOverloads
     fun startRipple(onAnimationEnd: Runnable? = null) {
         if (rippleInProgress) {
@@ -113,11 +128,24 @@
             // if it's unsupported.
             return
         }
-        // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
-        // the active effect area. Values here should be kept in sync with the
-        // animation implementation in the ripple shader.
-        val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
-                (1 - rippleShader.progress)) * radius * 2
-        canvas.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
+        // To reduce overdraw, we mask the effect to a circle or a rectangle that's bigger than the
+        // active effect area. Values here should be kept in sync with the animation implementation
+        // in the ripple shader.
+        if (rippleShape == RippleShape.CIRCLE) {
+            val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+                    (1 - rippleShader.progress)) * maxWidth
+            canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint)
+        } else {
+            val maskWidth = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+                    (1 - rippleShader.progress)) * maxWidth * 2
+            val maskHeight = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+                    (1 - rippleShader.progress)) * maxHeight * 2
+            canvas.drawRect(
+                    /* left= */ centerX - maskWidth,
+                    /* top= */ centerY - maskHeight,
+                    /* right= */ centerX + maskWidth,
+                    /* bottom= */ centerY + maskHeight,
+                    ripplePaint)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
new file mode 100644
index 0000000..7f26146
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ripple
+
+/** Library class that contains 2D signed distance functions. */
+class SdfShaderLibrary {
+    companion object {
+        const val CIRCLE_SDF = """
+            float sdCircle(vec2 p, float r) {
+                return (length(p)-r) / r;
+            }
+
+            float circleRing(vec2 p, float radius) {
+                float thicknessHalf = radius * 0.25;
+
+                float outerCircle = sdCircle(p, radius + thicknessHalf);
+                float innerCircle = sdCircle(p, radius);
+
+                return subtract(outerCircle, innerCircle);
+            }
+        """
+
+        const val ROUNDED_BOX_SDF = """
+            float sdRoundedBox(vec2 p, vec2 size, float cornerRadius) {
+                size *= 0.5;
+                cornerRadius *= 0.5;
+                vec2 d = abs(p)-size+cornerRadius;
+
+                float outside = length(max(d, 0.0));
+                float inside = min(max(d.x, d.y), 0.0);
+
+                return (outside+inside-cornerRadius)/size.y;
+            }
+
+            float roundedBoxRing(vec2 p, vec2 size, float cornerRadius,
+                float borderThickness) {
+                float outerRoundBox = sdRoundedBox(p, size, cornerRadius);
+                float innerRoundBox = sdRoundedBox(p, size - vec2(borderThickness),
+                    cornerRadius - borderThickness);
+                return subtract(outerRoundBox, innerRoundBox);
+            }
+        """
+
+        // Used non-trigonometry parametrization and Halley's method (iterative) for root finding.
+        // This is more expensive than the regular circle SDF, recommend to use the circle SDF if
+        // possible.
+        const val ELLIPSE_SDF = """float sdEllipse(vec2 p, vec2 wh) {
+            wh *= 0.5;
+
+            // symmetry
+            (wh.x > wh.y) ? wh = wh.yx, p = abs(p.yx) : p = abs(p);
+
+            vec2 u = wh*p, v = wh*wh;
+
+            float U1 = u.y/2.0;  float U5 = 4.0*U1;
+            float U2 = v.y-v.x;  float U6 = 6.0*U1;
+            float U3 = u.x-U2;   float U7 = 3.0*U3;
+            float U4 = u.x+U2;
+
+            float t = 0.5;
+            for (int i = 0; i < 3; i ++) {
+                float F1 = t*(t*t*(U1*t+U3)+U4)-U1;
+                float F2 = t*t*(U5*t+U7)+U4;
+                float F3 = t*(U6*t+U7);
+
+                t += (F1*F2)/(F1*F3-F2*F2);
+            }
+
+            t = clamp(t, 0.0, 1.0);
+
+            float d = distance(p, wh*vec2(1.0-t*t,2.0*t)/(t*t+1.0));
+            d /= wh.y;
+
+            return (dot(p/wh,p/wh)>1.0) ? d : -d;
+        }
+
+        float ellipseRing(vec2 p, vec2 wh) {
+            vec2 thicknessHalf = wh * 0.25;
+
+            float outerEllipse = sdEllipse(p, wh + thicknessHalf);
+            float innerEllipse = sdEllipse(p, wh);
+
+            return subtract(outerEllipse, innerEllipse);
+        }
+        """
+
+        const val SHADER_SDF_OPERATION_LIB = """
+            float soften(float d, float blur) {
+                float blurHalf = blur * 0.5;
+                return smoothstep(-blurHalf, blurHalf, d);
+            }
+
+            float subtract(float outer, float inner) {
+                return max(outer, -inner);
+            }
+        """
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
new file mode 100644
index 0000000..39f35a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Rect
+
+interface ImageCapture {
+
+    fun captureDisplay(displayId: Int, crop: Rect? = null): Bitmap?
+
+    fun captureTask(taskId: Int): Bitmap?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
new file mode 100644
index 0000000..258c436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.IBinder
+import android.util.Log
+import android.view.DisplayAddress
+import android.view.SurfaceControl
+import android.view.SurfaceControl.DisplayCaptureArgs
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import androidx.annotation.VisibleForTesting
+import javax.inject.Inject
+
+private const val TAG = "ImageCaptureImpl"
+
+open class ImageCaptureImpl @Inject constructor(
+    private val displayManager: DisplayManager,
+    private val atmService: IActivityTaskManager
+) : ImageCapture {
+
+    override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? {
+        val width = crop?.width() ?: 0
+        val height = crop?.height() ?: 0
+        val sourceCrop = crop ?: Rect()
+        val displayToken = physicalDisplayToken(displayId) ?: return null
+        val buffer = captureDisplay(displayToken, width, height, sourceCrop)
+
+        return buffer?.asBitmap()
+    }
+
+    override fun captureTask(taskId: Int): Bitmap? {
+        val snapshot = atmService.takeTaskSnapshot(taskId)
+        return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
+    }
+
+    @VisibleForTesting
+    open fun physicalDisplayToken(displayId: Int): IBinder? {
+        val display = displayManager.getDisplay(displayId)
+        if (display == null) {
+            Log.e(TAG, "No display with id: $displayId")
+            return null
+        }
+        val address = display.address
+        if (address !is DisplayAddress.Physical) {
+            Log.e(TAG, "Display does not have a physical address: $display")
+            return null
+        }
+        return SurfaceControl.getPhysicalDisplayToken(address.physicalDisplayId)
+    }
+
+    @VisibleForTesting
+    open fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): ScreenshotHardwareBuffer? {
+        val captureArgs = DisplayCaptureArgs.Builder(displayToken)
+            .setSize(width, height)
+            .setSourceCrop(crop)
+            .build()
+        return SurfaceControl.captureDisplay(captureArgs)
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
new file mode 100644
index 0000000..beb54c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.net.Uri
+import android.util.Log
+import android.view.WindowManager.ScreenshotType
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Processes a screenshot request sent from {@link ScreenshotHelper}.
+ */
+@SysUISingleton
+internal class RequestProcessor @Inject constructor(
+    private val controller: ScreenshotController,
+) {
+    fun processRequest(
+        @ScreenshotType type: Int,
+        onSavedListener: Consumer<Uri>,
+        request: ScreenshotRequest,
+        callback: RequestCallback
+    ) {
+
+        if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
+
+            controller.handleImageAsScreenshot(
+                image, request.boundsInScreen, request.insets,
+                request.taskId, request.userId, request.topComponent, onSavedListener, callback
+            )
+            return
+        }
+
+        when (type) {
+            TAKE_SCREENSHOT_FULLSCREEN ->
+                controller.takeScreenshotFullscreen(null, onSavedListener, callback)
+            TAKE_SCREENSHOT_SELECTED_REGION ->
+                controller.takeScreenshotPartial(null, onSavedListener, callback)
+            else -> Log.w(TAG, "Invalid screenshot option: $type")
+        }
+    }
+
+    companion object {
+        const val TAG: String = "RequestProcessor"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 82de389..69ee8e8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -57,14 +57,12 @@
 import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
 import android.view.Display;
-import android.view.DisplayAddress;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
 import android.view.KeyEvent;
@@ -72,7 +70,6 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.ScrollCaptureResponse;
-import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
@@ -249,6 +246,7 @@
     private final ScreenshotSmartActions mScreenshotSmartActions;
     private final UiEventLogger mUiEventLogger;
     private final ImageExporter mImageExporter;
+    private final ImageCapture mImageCapture;
     private final Executor mMainExecutor;
     private final ExecutorService mBgExecutor;
     private final BroadcastSender mBroadcastSender;
@@ -295,6 +293,7 @@
             ScrollCaptureClient scrollCaptureClient,
             UiEventLogger uiEventLogger,
             ImageExporter imageExporter,
+            ImageCapture imageCapture,
             @Main Executor mainExecutor,
             ScrollCaptureController scrollCaptureController,
             LongScreenshotData longScreenshotHolder,
@@ -308,6 +307,7 @@
         mScrollCaptureClient = scrollCaptureClient;
         mUiEventLogger = uiEventLogger;
         mImageExporter = imageExporter;
+        mImageCapture = imageCapture;
         mMainExecutor = mainExecutor;
         mScrollCaptureController = scrollCaptureController;
         mLongScreenshotHolder = longScreenshotHolder;
@@ -531,7 +531,7 @@
 
         // copy the input Rect, since SurfaceControl.screenshot can mutate it
         Rect screenRect = new Rect(crop);
-        Bitmap screenshot = captureScreenshot(crop);
+        Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop);
 
         if (screenshot == null) {
             Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
@@ -549,32 +549,6 @@
                 ClipboardOverlayController.SELF_PERMISSION);
     }
 
-    private Bitmap captureScreenshot(Rect crop) {
-        int width = crop.width();
-        int height = crop.height();
-        Bitmap screenshot = null;
-        final Display display = getDefaultDisplay();
-        final DisplayAddress address = display.getAddress();
-        if (!(address instanceof DisplayAddress.Physical)) {
-            Log.e(TAG, "Skipping Screenshot - Default display does not have a physical address: "
-                    + display);
-        } else {
-            final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
-
-            final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(
-                    physicalAddress.getPhysicalDisplayId());
-            final SurfaceControl.DisplayCaptureArgs captureArgs =
-                    new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
-                            .setSourceCrop(crop)
-                            .setSize(width, height)
-                            .build();
-            final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
-                    SurfaceControl.captureDisplay(captureArgs);
-            screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
-        }
-        return screenshot;
-    }
-
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, ComponentName topComponent, boolean showFlash) {
         withWindowAttached(() ->
@@ -720,7 +694,7 @@
             mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
                 DisplayMetrics displayMetrics = new DisplayMetrics();
                 getDefaultDisplay().getRealMetrics(displayMetrics);
-                Bitmap newScreenshot = captureScreenshot(
+                Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY,
                         new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
 
                 mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 32d8203..f1f0223 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -21,6 +21,7 @@
 
 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+import static com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
@@ -57,6 +58,8 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FlagListenable.FlagEvent;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -75,6 +78,8 @@
     private final Handler mHandler;
     private final Context mContext;
     private final @Background Executor mBgExecutor;
+    private final RequestProcessor mProcessor;
+    private final FeatureFlags mFeatureFlags;
 
     private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
         @Override
@@ -104,7 +109,8 @@
     public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
             DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
             ScreenshotNotificationsController notificationsController, Context context,
-            @Background Executor bgExecutor) {
+            @Background Executor bgExecutor, FeatureFlags featureFlags,
+            RequestProcessor processor) {
         if (DEBUG_SERVICE) {
             Log.d(TAG, "new " + this);
         }
@@ -116,6 +122,9 @@
         mNotificationsController = notificationsController;
         mContext = context;
         mBgExecutor = bgExecutor;
+        mFeatureFlags = featureFlags;
+        mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
+        mProcessor = processor;
     }
 
     @Override
@@ -218,6 +227,12 @@
         mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
                 topComponent == null ? "" : topComponent.getPackageName());
 
+        if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
+            Log.d(TAG, "handleMessage: Using request processor");
+            mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback);
+            return true;
+        }
+
         switch (msg.what) {
             case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
                 if (DEBUG_SERVICE) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 91ef3c3..3e442587 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -18,6 +18,8 @@
 
 import android.app.Service;
 
+import com.android.systemui.screenshot.ImageCapture;
+import com.android.systemui.screenshot.ImageCaptureImpl;
 import com.android.systemui.screenshot.TakeScreenshotService;
 
 import dagger.Binds;
@@ -37,4 +39,6 @@
     @ClassKey(TakeScreenshotService.class)
     public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
 
+    @Binds
+    public abstract ImageCapture bindImageCapture(ImageCaptureImpl capture);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 5793105..0f9ac36 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -264,14 +264,8 @@
             Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
         )
 
-        carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            listOf(
-                header.context.getString(com.android.internal.R.string.status_bar_no_calling),
-                header.context.getString(com.android.internal.R.string.status_bar_call_strength)
-            )
-        } else {
+        carrierIconSlots =
             listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
-        }
         qsCarrierGroupController = qsCarrierGroupControllerBuilder
             .setQSCarrierGroup(qsCarrierGroup)
             .build()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 58e9bb8..7f0e76b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -44,7 +44,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
-import android.app.ActivityManager;
 import android.app.Fragment;
 import android.app.StatusBarManager;
 import android.content.ContentResolver;
@@ -288,7 +287,6 @@
     private final NotificationPanelView mView;
     private final VibratorHelper mVibratorHelper;
     private final MetricsLogger mMetricsLogger;
-    private final ActivityManager mActivityManager;
     private final ConfigurationController mConfigurationController;
     private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@@ -341,14 +339,14 @@
     private final RecordingController mRecordingController;
     private final PanelEventsEmitter mPanelEventsEmitter;
     private boolean mSplitShadeEnabled;
-    // The bottom padding reserved for elements of the keyguard measuring notifications
+    /** The bottom padding reserved for elements of the keyguard measuring notifications. */
     private float mKeyguardNotificationBottomPadding;
     /**
      * The top padding from where notification should start in lockscreen.
      * Should be static also during animations and should match the Y of the first notification.
      */
     private float mKeyguardNotificationTopPadding;
-    // Current max allowed keyguard notifications determined by measuring the panel
+    /** Current max allowed keyguard notifications determined by measuring the panel. */
     private int mMaxAllowedKeyguardNotifications;
 
     private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
@@ -728,7 +726,6 @@
             AccessibilityManager accessibilityManager, @DisplayId int displayId,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             MetricsLogger metricsLogger,
-            ActivityManager activityManager,
             ConfigurationController configurationController,
             Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
@@ -798,7 +795,6 @@
                 panelExpansionStateManager,
                 ambientState,
                 interactionJankMonitor,
-                keyguardUnlockAnimationController,
                 systemClock);
         mView = view;
         mVibratorHelper = vibratorHelper;
@@ -808,7 +804,6 @@
         mQRCodeScannerController = qrCodeScannerController;
         mControlsComponent = controlsComponent;
         mMetricsLogger = metricsLogger;
-        mActivityManager = activityManager;
         mConfigurationController = configurationController;
         mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
         mMediaHierarchyManager = mediaHierarchyManager;
@@ -1504,10 +1499,7 @@
     }
 
     private void updateKeyguardStatusViewAlignment(boolean animate) {
-        boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
-                .getVisibleNotificationCount() != 0
-                || mMediaDataManager.hasActiveMediaOrRecommendation();
-        boolean shouldBeCentered = !mSplitShadeEnabled || !hasVisibleNotifications || mDozing;
+        boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
         if (mStatusViewCentered != shouldBeCentered) {
             mStatusViewCentered = shouldBeCentered;
             ConstraintSet constraintSet = new ConstraintSet();
@@ -1531,6 +1523,15 @@
         mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(mStatusViewCentered));
     }
 
+    private boolean shouldKeyguardStatusViewBeCentered() {
+        boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+                .getVisibleNotificationCount() != 0
+                || mMediaDataManager.hasActiveMediaOrRecommendation();
+        boolean isOnAod = mDozing && mDozeParameters.getAlwaysOn();
+        return !mSplitShadeEnabled || !hasVisibleNotifications || isOnAod
+                || hasPulsingNotifications();
+    }
+
     /**
      * @return the padding of the stackscroller when unlocked
      */
@@ -3969,7 +3970,7 @@
      * notification data being displayed. In the new notification pipeline, this is handled in
      * {@link ShadeViewManager}.
      */
-    public void updateNotificationViews(String reason) {
+    public void updateNotificationViews() {
         mNotificationStackScrollLayoutController.updateFooter();
 
         mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList());
@@ -4426,7 +4427,7 @@
             NotificationStackScrollLayout.OnEmptySpaceClickListener {
         @Override
         public void onEmptySpaceClicked(float x, float y) {
-            onEmptySpaceClick(x);
+            onEmptySpaceClick();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 15e1129..1d92105 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -89,7 +89,6 @@
         Dumpable, ConfigurationListener {
 
     private static final String TAG = "NotificationShadeWindowController";
-    private static final boolean DEBUG = false;
 
     private final Context mContext;
     private final WindowManager mWindowManager;
@@ -190,7 +189,7 @@
                 return;
             }
         }
-        mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
+        mCallbacks.add(new WeakReference<>(callback));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 121d69d..e52170e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -18,6 +18,8 @@
 
 import static android.view.WindowInsets.Type.systemBars;
 
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
+
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
 import android.annotation.LayoutRes;
@@ -52,14 +54,12 @@
 import com.android.internal.view.FloatingActionMode;
 import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
 
 /**
  * Combined keyguard and notification panel view. Also holding backdrop and scrims.
  */
 public class NotificationShadeWindowView extends FrameLayout {
     public static final String TAG = "NotificationShadeWindowView";
-    public static final boolean DEBUG = CentralSurfaces.DEBUG;
 
     private int mRightInset = 0;
     private int mLeftInset = 0;
@@ -221,7 +221,7 @@
         }
     }
 
-    class LayoutParams extends FrameLayout.LayoutParams {
+    private static class LayoutParams extends FrameLayout.LayoutParams {
 
         public boolean ignoreRightInset;
 
@@ -243,7 +243,7 @@
     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback,
             int type) {
         if (type == ActionMode.TYPE_FLOATING) {
-            return startActionMode(originalView, callback, type);
+            return startActionMode(originalView, callback);
         }
         return super.startActionModeForChild(originalView, callback, type);
     }
@@ -258,14 +258,10 @@
         final FloatingActionMode mode =
                 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
         mFloatingActionModeOriginatingView = originatingView;
-        mFloatingToolbarPreDrawListener =
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        mode.updateViewLocationInWindow();
-                        return true;
-                    }
-                };
+        mFloatingToolbarPreDrawListener = () -> {
+            mode.updateViewLocationInWindow();
+            return true;
+        };
         return mode;
     }
 
@@ -292,10 +288,10 @@
     }
 
     private ActionMode startActionMode(
-            View originatingView, ActionMode.Callback callback, int type) {
+            View originatingView, ActionMode.Callback callback) {
         ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
         ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback);
-        if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
+        if (wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
             setHandledFloatingActionMode(mode);
         } else {
             mode = null;
@@ -382,7 +378,7 @@
     /**
      * Minimal window to satisfy FloatingToolbar.
      */
-    private Window mFakeWindow = new Window(mContext) {
+    private final Window mFakeWindow = new Window(mContext) {
         @Override
         public void takeSurface(SurfaceHolder.Callback2 callback) {
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 587e0e6d..02316b7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -48,13 +48,12 @@
     private View mStackScroller;
     private View mKeyguardStatusBar;
 
-    private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
-    private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
+    private final ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
+    private final ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
     private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
     private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
     private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
     private QS mQs;
-    private View mQSScrollView;
     private View mQSContainer;
 
     @Nullable
@@ -76,7 +75,6 @@
     public void onFragmentViewCreated(String tag, Fragment fragment) {
         mQs = (QS) fragment;
         mQSFragmentAttachedListener.accept(mQs);
-        mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
         mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java b/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
index 1082967..efff0db 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
@@ -49,10 +49,6 @@
         super(context, attrs, defStyleAttr);
     }
 
-    public PanelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
     public void setOnTouchListener(PanelViewController.TouchHandler touchHandler) {
         super.setOnTouchListener(touchHandler);
         mTouchHandler = touchHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index 229acf4..4aad245 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -19,11 +19,11 @@
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
 import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.classifier.Classifier.UNLOCK;
+import static com.android.systemui.shade.PanelView.DEBUG;
 
 import static java.lang.Float.isNaN;
 
@@ -53,7 +53,6 @@
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.doze.DozeLog;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -77,7 +76,6 @@
 import java.util.List;
 
 public abstract class PanelViewController {
-    public static final boolean DEBUG = PanelView.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
     public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
     public static final float FLING_SPEED_UP_FACTOR = 0.6f;
@@ -97,7 +95,7 @@
     protected boolean mTouchSlopExceededBeforeDown;
     private float mMinExpandHeight;
     private boolean mPanelUpdateWhenAnimatorEnds;
-    private boolean mVibrateOnOpening;
+    private final boolean mVibrateOnOpening;
     protected boolean mIsLaunchAnimationRunning;
     private int mFixedDuration = NO_FIXED_DURATION;
     protected float mOverExpansion;
@@ -144,7 +142,6 @@
     private int mTouchSlop;
     private float mSlopMultiplier;
     protected boolean mHintAnimationRunning;
-    private boolean mOverExpandedBeforeFling;
     private boolean mTouchAboveFalsingThreshold;
     private int mUnlockFalsingThreshold;
     private boolean mTouchStartedInEmptyArea;
@@ -155,9 +152,9 @@
 
     private ValueAnimator mHeightAnimator;
     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
-    private FlingAnimationUtils mFlingAnimationUtils;
-    private FlingAnimationUtils mFlingAnimationUtilsClosing;
-    private FlingAnimationUtils mFlingAnimationUtilsDismissing;
+    private final FlingAnimationUtils mFlingAnimationUtils;
+    private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+    private final FlingAnimationUtils mFlingAnimationUtilsDismissing;
     private final LatencyTracker mLatencyTracker;
     private final FalsingManager mFalsingManager;
     private final DozeLog mDozeLog;
@@ -174,13 +171,14 @@
     private float mInitialTouchY;
     private float mInitialTouchX;
     private boolean mTouchDisabled;
+    private boolean mInitialTouchFromKeyguard;
 
     /**
      * Whether or not the PanelView can be expanded or collapsed with a drag.
      */
-    private boolean mNotificationsDragEnabled;
+    private final boolean mNotificationsDragEnabled;
 
-    private Interpolator mBounceInterpolator;
+    private final Interpolator mBounceInterpolator;
     protected KeyguardBottomAreaView mKeyguardBottomArea;
 
     /**
@@ -201,7 +199,6 @@
     protected final AmbientState mAmbientState;
     protected final LockscreenGestureLogger mLockscreenGestureLogger;
     private final PanelExpansionStateManager mPanelExpansionStateManager;
-    private final TouchHandler mTouchHandler;
     private final InteractionJankMonitor mInteractionJankMonitor;
     protected final SystemClock mSystemClock;
 
@@ -229,8 +226,6 @@
         return mAmbientState;
     }
 
-    private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
-
     public PanelViewController(
             PanelView view,
             FalsingManager falsingManager,
@@ -247,9 +242,7 @@
             PanelExpansionStateManager panelExpansionStateManager,
             AmbientState ambientState,
             InteractionJankMonitor interactionJankMonitor,
-            KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             SystemClock systemClock) {
-        mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
             public void onKeyguardFadingAwayChanged() {
@@ -261,7 +254,7 @@
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mLockscreenGestureLogger = lockscreenGestureLogger;
         mPanelExpansionStateManager = panelExpansionStateManager;
-        mTouchHandler = createTouchHandler();
+        TouchHandler touchHandler = createTouchHandler();
         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -274,7 +267,7 @@
         });
 
         mView.addOnLayoutChangeListener(createLayoutChangeListener());
-        mView.setOnTouchListener(mTouchHandler);
+        mView.setOnTouchListener(touchHandler);
         mView.setOnConfigurationChangedListener(createOnConfigurationChangedListener());
 
         mResources = mView.getResources();
@@ -398,11 +391,12 @@
     public void startExpandMotion(float newX, float newY, boolean startTracking,
             float expandedHeight) {
         if (!mHandlingPointerUp && !mStatusBarStateController.isDozing()) {
-            beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+            beginJankMonitoring();
         }
         mInitialOffsetOnTouch = expandedHeight;
         mInitialTouchY = newY;
         mInitialTouchX = newX;
+        mInitialTouchFromKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
         if (startTracking) {
             mTouchSlopExceeded = true;
             setExpandedHeight(mInitialOffsetOnTouch);
@@ -425,20 +419,14 @@
                     mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
 
             final boolean expand;
-            if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
-                // If the keyguard is fading away, don't expand it again. This can happen if you're
-                // swiping to unlock, the app below the keyguard is in landscape, and the screen
-                // rotates while your finger is still down after the swipe to unlock.
-                if (mKeyguardStateController.isKeyguardFadingAway()) {
-                    expand = false;
-                } else if (onKeyguard) {
+            if (mKeyguardStateController.isKeyguardFadingAway()
+                    || (mInitialTouchFromKeyguard && !onKeyguard)) {
+                // Don't expand for any touches that started from the keyguard and ended after the
+                // keyguard is gone.
+                expand = false;
+            } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+                if (onKeyguard) {
                     expand = true;
-                } else if (mKeyguardStateController.isKeyguardFadingAway()) {
-                    // If we're in the middle of dismissing the keyguard, don't expand due to the
-                    // cancelled gesture. Gesture cancellation during an unlock is expected in some
-                    // situations, such keeping your finger down while swiping to unlock to an app
-                    // that is locked in landscape (the rotation will cancel the touch event).
-                    expand = false;
                 } else if (mCentralSurfaces.isBouncerShowingOverDream()) {
                     expand = false;
                 } else {
@@ -475,7 +463,7 @@
         } else if (!mCentralSurfaces.isBouncerShowing()
                 && !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
                 && !mKeyguardStateController.isKeyguardGoingAway()) {
-            boolean expands = onEmptySpaceClick(mInitialTouchX);
+            boolean expands = onEmptySpaceClick();
             onTrackingStopped(expands);
         }
         mVelocityTracker.clear();
@@ -670,7 +658,7 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 if (!mStatusBarStateController.isDozing()) {
-                    beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+                    beginJankMonitoring();
                 }
             }
 
@@ -702,10 +690,8 @@
         mIsSpringBackAnimation = true;
         ValueAnimator animator = ValueAnimator.ofFloat(mOverExpansion, 0);
         animator.addUpdateListener(
-                animation -> {
-                    setOverExpansionInternal((float) animation.getAnimatedValue(),
-                            false /* isFromGesture */);
-                });
+                animation -> setOverExpansionInternal((float) animation.getAnimatedValue(),
+                        false /* isFromGesture */));
         animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         animator.addListener(new AnimatorListenerAdapter() {
@@ -731,10 +717,10 @@
         setAnimator(null);
         mKeyguardStateController.notifyPanelFlingEnd();
         if (!cancelled) {
-            endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+            endJankMonitoring();
             notifyExpandingFinished();
         } else {
-            cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+            cancelJankMonitoring();
         }
         updatePanelExpansionAndVisibility();
     }
@@ -773,13 +759,6 @@
         setExpandedHeight(currentMaxPanelHeight);
     }
 
-    private float getStackHeightFraction(float height) {
-        final float gestureFraction = height / getMaxPanelHeight();
-        final float stackHeightFraction = Interpolators.ACCELERATE_DECELERATE
-                .getInterpolation(gestureFraction);
-        return stackHeightFraction;
-    }
-
     public void setExpandedHeightInternal(float h) {
         if (isNaN(h)) {
             Log.wtf(TAG, "ExpandedHeight set to NaN");
@@ -911,13 +890,8 @@
         return !isFullyCollapsed() && !mTracking && !mClosing;
     }
 
-    private final Runnable mFlingCollapseRunnable = new Runnable() {
-        @Override
-        public void run() {
-            fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
-                    false /* expandBecauseOfFalsing */);
-        }
-    };
+    private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
+            mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
 
     public void expand(final boolean animate) {
         if (!isFullyCollapsed() && !isCollapsing()) {
@@ -950,7 +924,7 @@
                             mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                             if (mAnimateAfterExpanding) {
                                 notifyExpandingStarted();
-                                beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+                                beginJankMonitoring();
                                 fling(0, true /* expand */);
                             } else {
                                 setExpandedFraction(1f);
@@ -1150,7 +1124,7 @@
      *
      * @return whether the panel will be expanded after the action performed by this method
      */
-    protected boolean onEmptySpaceClick(float x) {
+    protected boolean onEmptySpaceClick() {
         if (mHintAnimationRunning) {
             return true;
         }
@@ -1432,9 +1406,9 @@
                     // mHeightAnimator is null, there is no remaining frame, ends instrumenting.
                     if (mHeightAnimator == null) {
                         if (event.getActionMasked() == MotionEvent.ACTION_UP) {
-                            endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+                            endJankMonitoring();
                         } else {
-                            cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+                            cancelJankMonitoring();
                         }
                     }
                     break;
@@ -1465,28 +1439,32 @@
         }
     }
 
-    private void beginJankMonitoring(int cuj) {
+    private void beginJankMonitoring() {
         if (mInteractionJankMonitor == null) {
             return;
         }
         InteractionJankMonitor.Configuration.Builder builder =
-                InteractionJankMonitor.Configuration.Builder.withView(cuj, mView)
+                InteractionJankMonitor.Configuration.Builder.withView(
+                                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+                                mView)
                         .setTag(isFullyCollapsed() ? "Expand" : "Collapse");
         mInteractionJankMonitor.begin(builder);
     }
 
-    private void endJankMonitoring(int cuj) {
+    private void endJankMonitoring() {
         if (mInteractionJankMonitor == null) {
             return;
         }
-        InteractionJankMonitor.getInstance().end(cuj);
+        InteractionJankMonitor.getInstance().end(
+                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
     }
 
-    private void cancelJankMonitoring(int cuj) {
+    private void cancelJankMonitoring() {
         if (mInteractionJankMonitor == null) {
             return;
         }
-        InteractionJankMonitor.getInstance().cancel(cuj);
+        InteractionJankMonitor.getInstance().cancel(
+                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
     }
 
     protected float getExpansionFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 3013ad0..a57d849b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -61,21 +61,19 @@
     private int mVisibleState = -1;
     private DualToneHandler mDualToneHandler;
     private boolean mForceHidden;
-    private boolean mProviderModel;
 
     /**
      * Designated constructor
      */
     public static StatusBarMobileView fromContext(
             Context context,
-            String slot,
-            boolean providerModel
+            String slot
     ) {
         LayoutInflater inflater = LayoutInflater.from(context);
         StatusBarMobileView v = (StatusBarMobileView)
                 inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
         v.setSlot(slot);
-        v.init(providerModel);
+        v.init();
         v.setVisibleState(STATE_ICON);
         return v;
     }
@@ -108,17 +106,12 @@
         outRect.bottom += translationY;
     }
 
-    private void init(boolean providerModel) {
-        mProviderModel = providerModel;
+    private void init() {
         mDualToneHandler = new DualToneHandler(getContext());
         mMobileGroup = findViewById(R.id.mobile_group);
         mMobile = findViewById(R.id.mobile_signal);
         mMobileType = findViewById(R.id.mobile_type);
-        if (mProviderModel) {
-            mMobileRoaming = findViewById(R.id.mobile_roaming_large);
-        } else {
-            mMobileRoaming = findViewById(R.id.mobile_roaming);
-        }
+        mMobileRoaming = findViewById(R.id.mobile_roaming);
         mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
         mIn = findViewById(R.id.mobile_in);
         mOut = findViewById(R.id.mobile_out);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 6914ae6..1638780 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -21,6 +21,7 @@
 import android.telephony.SubscriptionInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
 
@@ -36,6 +37,7 @@
  * Implements network listeners and forwards the calls along onto other listeners but on
  * the current or specified Looper.
  */
+@SysUISingleton
 public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
     private static final String TAG = "CallbackHandler";
     private static final int MSG_EMERGENCE_CHANGED           = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index f2014e9..ec221b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -26,25 +26,17 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings.Global;
-import android.telephony.AccessNetworkConstants;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
-import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsRegistrationAttributes;
-import android.telephony.ims.RegistrationManager.RegistrationCallback;
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.AccessibilityContentDescriptions;
 import com.android.settingslib.SignalIcon.MobileIconGroup;
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.MobileMappings.Config;
@@ -54,8 +46,6 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.util.CarrierConfigTracker;
 
 import java.io.PrintWriter;
@@ -70,33 +60,22 @@
 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
     private static final int STATUS_HISTORY_SIZE = 64;
-    private static final int IMS_TYPE_WWAN = 1;
-    private static final int IMS_TYPE_WLAN = 2;
-    private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
     private final TelephonyManager mPhone;
     private final CarrierConfigTracker mCarrierConfigTracker;
-    private final ImsMmTelManager mImsMmTelManager;
     private final SubscriptionDefaults mDefaults;
     private final String mNetworkNameDefault;
     private final String mNetworkNameSeparator;
     private final ContentObserver mObserver;
-    private final boolean mProviderModelBehavior;
-    private final Handler mReceiverHandler;
-    private int mImsType = IMS_TYPE_WWAN;
     // Save entire info for logging, we only use the id.
     final SubscriptionInfo mSubscriptionInfo;
     private Map<String, MobileIconGroup> mNetworkToIconLookup;
 
-    private int mLastLevel;
     private MobileIconGroup mDefaultIcons;
     private Config mConfig;
     @VisibleForTesting
     boolean mInflateSignalStrengths = false;
-    private int mLastWwanLevel;
-    private int mLastWlanLevel;
-    private int mLastWlanCrossSimLevel;
     @VisibleForTesting
-    MobileStatusTracker mMobileStatusTracker;
+    final MobileStatusTracker mMobileStatusTracker;
 
     // Save the previous STATUS_HISTORY_SIZE states for logging.
     private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
@@ -133,52 +112,6 @@
                 }
             };
 
-    private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
-        @Override
-        public void onRegistered(ImsRegistrationAttributes attributes) {
-            Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
-            int imsTransportType = attributes.getTransportType();
-            int registrationAttributes = attributes.getAttributeFlags();
-            if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                mImsType = IMS_TYPE_WWAN;
-                IconState statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-            } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                if (registrationAttributes == 0) {
-                    mImsType = IMS_TYPE_WLAN;
-                    IconState statusIcon = new IconState(
-                            true,
-                            getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
-                            getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
-                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-                } else if (registrationAttributes
-                        == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
-                    mImsType = IMS_TYPE_WLAN_CROSS_SIM;
-                    IconState statusIcon = new IconState(
-                            true,
-                            getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
-                            getCallStrengthDescription(
-                                    mLastWlanCrossSimLevel, /* isWifi= */false));
-                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-                }
-            }
-        }
-
-        @Override
-        public void onUnregistered(ImsReasonInfo info) {
-            Log.d(mTag, "onDeregistered: " + "info=" + info);
-            mImsType = IMS_TYPE_WWAN;
-            IconState statusIcon = new IconState(
-                    true,
-                    getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                    getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-        }
-    };
-
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
     public MobileSignalController(
@@ -192,7 +125,7 @@
             SubscriptionDefaults defaults,
             Looper receiverLooper,
             CarrierConfigTracker carrierConfigTracker,
-            FeatureFlags featureFlags
+            MobileStatusTrackerFactory mobileStatusTrackerFactory
     ) {
         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
@@ -206,7 +139,6 @@
                 R.string.status_bar_network_name_separator).toString();
         mNetworkNameDefault = getTextIfExists(
                 com.android.internal.R.string.lockscreen_carrier_default).toString();
-        mReceiverHandler = new Handler(receiverLooper);
 
         mNetworkToIconLookup = mapIconSets(mConfig);
         mDefaultIcons = getDefaultIcons(mConfig);
@@ -223,10 +155,7 @@
                 updateTelephony();
             }
         };
-        mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
-        mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
-                info, mDefaults, mMobileCallback);
-        mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
+        mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
     }
 
     void setConfiguration(Config config) {
@@ -271,41 +200,14 @@
         mContext.getContentResolver().registerContentObserver(Global.getUriFor(
                 Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
                 true, mObserver);
-        if (mProviderModelBehavior) {
-            mReceiverHandler.post(mTryRegisterIms);
-        }
     }
 
-    // There is no listener to monitor whether the IMS service is ready, so we have to retry the
-    // IMS registration.
-    private final Runnable mTryRegisterIms = new Runnable() {
-        private static final int MAX_RETRY = 12;
-        private int mRetryCount;
-
-        @Override
-        public void run() {
-            try {
-                mRetryCount++;
-                mImsMmTelManager.registerImsRegistrationCallback(
-                        mReceiverHandler::post, mRegistrationCallback);
-                Log.d(mTag, "registerImsRegistrationCallback succeeded");
-            } catch (RuntimeException | ImsException e) {
-                if (mRetryCount < MAX_RETRY) {
-                    Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
-                    // Wait for 5 seconds to retry
-                    mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
-                }
-            }
-        }
-    };
-
     /**
      * Stop listening for phone state changes.
      */
     public void unregisterListener() {
         mMobileStatusTracker.setListening(false);
         mContext.getContentResolver().unregisterContentObserver(mObserver);
-        mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
     }
 
     private void updateInflateSignalStrength() {
@@ -394,7 +296,7 @@
         CharSequence qsDescription = null;
 
         if (mCurrentState.dataSim) {
-            // If using provider model behavior, only show QS icons if the state is also default
+            // only show QS icons if the state is also default
             if (!mCurrentState.isDefault) {
                 return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
             }
@@ -416,32 +318,15 @@
 
     private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
         final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
-        boolean showTriangle = false;
-        int typeIcon = 0;
-        IconState statusIcon = null;
+        IconState statusIcon = new IconState(
+                mCurrentState.enabled && !mCurrentState.airplaneMode,
+                getCurrentIconId(), contentDescription);
 
-        if (mProviderModelBehavior) {
-            boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
-                    && (mCurrentState.dataSim && mCurrentState.isDefault);
-            typeIcon =
-                    (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
-            showDataIconStatusBar |= mCurrentState.roaming;
-            statusIcon = new IconState(
-                    showDataIconStatusBar && !mCurrentState.airplaneMode,
-                    getCurrentIconId(), contentDescription);
-
-            showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
-        } else {
-            statusIcon = new IconState(
-                    mCurrentState.enabled && !mCurrentState.airplaneMode,
-                    getCurrentIconId(), contentDescription);
-
-            boolean showDataIconInStatusBar =
-                    (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
-            typeIcon =
-                    (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
-            showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
-        }
+        boolean showDataIconInStatusBar =
+                (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+        int typeIcon =
+                (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+        boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
 
         return new SbInfo(showTriangle, typeIcon, statusIcon);
     }
@@ -560,144 +445,7 @@
     }
 
     private void updateMobileStatus(MobileStatus mobileStatus) {
-        int lastVoiceState = mCurrentState.getVoiceServiceState();
         mCurrentState.setFromMobileStatus(mobileStatus);
-
-        notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
-        if (mProviderModelBehavior) {
-            maybeNotifyCallStateChanged(lastVoiceState);
-        }
-    }
-
-    /** Call state changed is only applicable when provider model behavior is true */
-    private void maybeNotifyCallStateChanged(int lastVoiceState) {
-        int currentVoiceState = mCurrentState.getVoiceServiceState();
-        if (lastVoiceState == currentVoiceState) {
-            return;
-        }
-        // Only update the no calling Status in the below scenarios
-        // 1. The first valid voice state has been received
-        // 2. The voice state has been changed and either the last or current state is
-        //    ServiceState.STATE_IN_SERVICE
-        if (lastVoiceState == -1
-                || (lastVoiceState == ServiceState.STATE_IN_SERVICE
-                        || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
-            boolean isNoCalling = mCurrentState.isNoCalling();
-            isNoCalling &= !hideNoCalling();
-            IconState statusIcon = new IconState(isNoCalling,
-                    R.drawable.ic_qs_no_calling_sms,
-                    getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
-            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-        }
-    }
-
-    void updateNoCallingState() {
-        int currentVoiceState = mCurrentState.getVoiceServiceState();
-        boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
-        isNoCalling &= !hideNoCalling();
-        IconState statusIcon = new IconState(isNoCalling,
-                R.drawable.ic_qs_no_calling_sms,
-                getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
-        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    private boolean hideNoCalling() {
-        return mNetworkController.hasDefaultNetwork()
-                && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
-    }
-
-    private int getCallStrengthIcon(int level, boolean isWifi) {
-        return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
-                : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
-    }
-
-    private String getCallStrengthDescription(int level, boolean isWifi) {
-        return isWifi
-                ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
-                        .toString()
-                : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
-                        .toString();
-    }
-
-    void refreshCallIndicator(SignalCallback callback) {
-        boolean isNoCalling = mCurrentState.isNoCalling();
-        isNoCalling &= !hideNoCalling();
-        IconState statusIcon = new IconState(isNoCalling,
-                R.drawable.ic_qs_no_calling_sms,
-                getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
-        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
-
-        switch (mImsType) {
-            case IMS_TYPE_WWAN:
-                statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-                break;
-            case IMS_TYPE_WLAN:
-                statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
-                        getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
-                break;
-            case IMS_TYPE_WLAN_CROSS_SIM:
-                statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
-                        getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
-        }
-        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    void notifyWifiLevelChange(int level) {
-        if (!mProviderModelBehavior) {
-            return;
-        }
-        mLastWlanLevel = level;
-        if (mImsType != IMS_TYPE_WLAN) {
-            return;
-        }
-        IconState statusIcon = new IconState(
-                true,
-                getCallStrengthIcon(level, /* isWifi= */true),
-                getCallStrengthDescription(level, /* isWifi= */true));
-        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    void notifyDefaultMobileLevelChange(int level) {
-        if (!mProviderModelBehavior) {
-            return;
-        }
-        mLastWlanCrossSimLevel = level;
-        if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
-            return;
-        }
-        IconState statusIcon = new IconState(
-                true,
-                getCallStrengthIcon(level, /* isWifi= */false),
-                getCallStrengthDescription(level, /* isWifi= */false));
-        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-    }
-
-    void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
-        if (!mProviderModelBehavior) {
-            return;
-        }
-        int newLevel = getSignalLevel(signalStrength);
-        if (newLevel != mLastLevel) {
-            mLastLevel = newLevel;
-            mLastWwanLevel = newLevel;
-            if (mImsType == IMS_TYPE_WWAN) {
-                IconState statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(newLevel, /* isWifi= */false),
-                        getCallStrengthDescription(newLevel, /* isWifi= */false));
-                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-            }
-            if (mCurrentState.dataSim) {
-                mNetworkController.notifyDefaultMobileLevelChange(newLevel);
-            }
-        }
     }
 
     int getSignalLevel(SignalStrength signalStrength) {
@@ -801,19 +549,14 @@
         mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
     }
 
-    @VisibleForTesting
-    void setImsType(int imsType) {
-        mImsType = imsType;
-    }
-
     @Override
     public void dump(PrintWriter pw) {
         super.dump(pw);
         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
-        pw.println("  mProviderModelBehavior=" + mProviderModelBehavior + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
         pw.println("  mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
+        pw.println("  mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening());
         pw.println("  MobileStatusHistory");
         int size = 0;
         for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -843,6 +586,11 @@
             icon = iconState;
             description = desc;
         }
+
+        @Override
+        public String toString() {
+            return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon;
+        }
     }
 
     /** Box for status bar icon info */
@@ -856,5 +604,11 @@
             ratTypeIcon = typeIcon;
             icon = iconState;
         }
+
+        @Override
+        public String toString() {
+            return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon
+                    + " icon=" + icon;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
new file mode 100644
index 0000000..7938179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.content.Context
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.MobileStatusTracker
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.CarrierConfigTracker
+import javax.inject.Inject
+
+/**
+ * Factory to make MobileSignalController injectable
+ */
+@SysUISingleton
+internal class MobileSignalControllerFactory @Inject constructor(
+    val context: Context,
+    val callbackHandler: CallbackHandler,
+    val carrierConfigTracker: CarrierConfigTracker,
+) {
+    fun createMobileSignalController(
+        config: MobileMappings.Config,
+        hasMobileData: Boolean,
+        phone: TelephonyManager,
+        networkController: NetworkControllerImpl,
+        subscriptionInfo: SubscriptionInfo,
+        subscriptionDefaults: MobileStatusTracker.SubscriptionDefaults,
+        receiverLooper: Looper,
+    ): MobileSignalController {
+        val mobileTrackerFactory = MobileStatusTrackerFactory(
+            phone,
+            receiverLooper,
+            subscriptionInfo,
+            subscriptionDefaults)
+
+        return MobileSignalController(
+            context,
+            config,
+            hasMobileData,
+            phone,
+            callbackHandler,
+            networkController,
+            subscriptionInfo,
+            subscriptionDefaults,
+            receiverLooper,
+            carrierConfigTracker,
+            mobileTrackerFactory,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
new file mode 100644
index 0000000..a4c1a198
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileStatusTracker
+
+/**
+ * Factory for [MobileStatusTracker], which lives in SettingsLib
+ */
+class MobileStatusTrackerFactory (
+    val phone: TelephonyManager,
+    val receiverLooper: Looper,
+    val info: SubscriptionInfo,
+    val defaults: MobileStatusTracker.SubscriptionDefaults,
+) {
+    fun createTracker(
+        callback: MobileStatusTracker.Callback
+    ): MobileStatusTracker {
+        return MobileStatusTracker(
+            phone,
+            receiverLooper,
+            info,
+            defaults,
+            callback)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 268531d..ea7ec4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -71,8 +71,6 @@
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogLevel;
 import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
@@ -133,12 +131,11 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final DemoModeController mDemoModeController;
     private final Object mLock = new Object();
-    private final boolean mProviderModelBehavior;
     private Config mConfig;
     private final CarrierConfigTracker mCarrierConfigTracker;
-    private final FeatureFlags mFeatureFlags;
     private final DumpManager mDumpManager;
     private final LogBuffer mLogBuffer;
+    private final MobileSignalControllerFactory mMobileFactory;
 
     private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -234,9 +231,9 @@
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
             WifiStatusTrackerFactory trackerFactory,
+            MobileSignalControllerFactory mobileFactory,
             @Main Handler handler,
             InternetDialogFactory internetDialogFactory,
-            FeatureFlags featureFlags,
             DumpManager dumpManager,
             @StatusBarNetworkControllerLog LogBuffer logBuffer) {
         this(context, connectivityManager,
@@ -256,8 +253,8 @@
                 demoModeController,
                 carrierConfigTracker,
                 trackerFactory,
+                mobileFactory,
                 handler,
-                featureFlags,
                 dumpManager,
                 logBuffer);
         mReceiverHandler.post(mRegisterListeners);
@@ -282,8 +279,8 @@
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
             WifiStatusTrackerFactory trackerFactory,
+            MobileSignalControllerFactory mobileFactory,
             @Main Handler handler,
-            FeatureFlags featureFlags,
             DumpManager dumpManager,
             LogBuffer logBuffer
     ) {
@@ -297,6 +294,7 @@
         mCallbackHandler = callbackHandler;
         mDataSaverController = new DataSaverControllerImpl(context);
         mBroadcastDispatcher = broadcastDispatcher;
+        mMobileFactory = mobileFactory;
 
         mSubscriptionManager = subManager;
         mSubDefaults = defaultsHandler;
@@ -304,7 +302,6 @@
         mHasMobileDataFeature = telephonyManager.isDataCapable();
         mDemoModeController = demoModeController;
         mCarrierConfigTracker = carrierConfigTracker;
-        mFeatureFlags = featureFlags;
         mDumpManager = dumpManager;
         mLogBuffer = logBuffer;
 
@@ -456,7 +453,6 @@
         };
 
         mDemoModeController.addCallback(this);
-        mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
 
         mDumpManager.registerDumpable(TAG, this);
     }
@@ -497,16 +493,16 @@
 
         // broadcasts
         IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
-        filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
-        filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
-        filter.addAction(Intent.ACTION_SERVICE_STATE);
-        filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        filter.addAction(Intent.ACTION_SERVICE_STATE);
+        filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
         filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
         mListening = true;
 
@@ -659,20 +655,6 @@
         return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
     }
 
-    void notifyWifiLevelChange(int level) {
-        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
-            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
-            mobileSignalController.notifyWifiLevelChange(level);
-        }
-    }
-
-    void notifyDefaultMobileLevelChange(int level) {
-        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
-            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
-            mobileSignalController.notifyDefaultMobileLevelChange(level);
-        }
-    }
-
     private void notifyControllersMobileDataChanged() {
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -745,9 +727,6 @@
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.notifyListeners(cb);
-            if (mProviderModelBehavior) {
-                mobileSignalController.refreshCallIndicator(cb);
-            }
         }
         mCallbackHandler.setListening(cb, true);
     }
@@ -862,9 +841,6 @@
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController controller = mMobileSignalControllers.valueAt(i);
             controller.setConfiguration(mConfig);
-            if (mProviderModelBehavior) {
-                controller.refreshCallIndicator(mCallbackHandler);
-            }
         }
         refreshLocale();
     }
@@ -981,11 +957,15 @@
                 mMobileSignalControllers.put(subId, cachedControllers.get(subId));
                 cachedControllers.remove(subId);
             } else {
-                MobileSignalController controller = new MobileSignalController(mContext, mConfig,
-                        mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
-                        mCallbackHandler, this, subscriptions.get(i),
-                        mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
-                        mFeatureFlags);
+                MobileSignalController controller = mMobileFactory.createMobileSignalController(
+                        mConfig,
+                        mHasMobileDataFeature,
+                        mPhone.createForSubscriptionId(subId),
+                        this,
+                        subscriptions.get(i),
+                        mSubDefaults,
+                        mReceiverHandler.getLooper()
+                );
                 controller.setUserSetupComplete(mUserSetup);
                 mMobileSignalControllers.put(subId, controller);
                 if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1139,24 +1119,11 @@
                 || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
 
         pushConnectivityToSignals();
-        if (mProviderModelBehavior) {
-            mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
-            mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
-                    mNoNetworksAvailable);
-            for (int i = 0; i < mMobileSignalControllers.size(); i++) {
-                MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
-                mobileSignalController.updateNoCallingState();
-            }
-            notifyAllListeners();
-        } else {
-            mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
-                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
-            mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
-                    mNoNetworksAvailable);
-        }
+        mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+        mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+                mNoNetworksAvailable);
     }
 
     /**
@@ -1346,7 +1313,7 @@
                 mMobileSignalControllers.clear();
                 int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
                 for (int i = start /* get out of normal index range */; i < start + num; i++) {
-                    subs.add(addSignalController(i, i));
+                    subs.add(addDemoModeSignalController(i, i));
                 }
                 mCallbackHandler.setSubs(subs);
                 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -1372,7 +1339,7 @@
             List<SubscriptionInfo> subs = new ArrayList<>();
             while (mMobileSignalControllers.size() <= slot) {
                 int nextSlot = mMobileSignalControllers.size();
-                subs.add(addSignalController(nextSlot, nextSlot));
+                subs.add(addDemoModeSignalController(nextSlot, nextSlot));
             }
             if (!subs.isEmpty()) {
                 mCallbackHandler.setSubs(subs);
@@ -1462,14 +1429,20 @@
         mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
     }
 
-    private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
+    private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) {
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
                 null, null, null, "", false, null, null);
-        MobileSignalController controller = new MobileSignalController(mContext,
-                mConfig, mHasMobileDataFeature,
-                mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
-                info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
-                mFeatureFlags);
+
+        MobileSignalController controller = mMobileFactory.createMobileSignalController(
+                mConfig,
+                mHasMobileDataFeature,
+                mPhone.createForSubscriptionId(info.getSubscriptionId()),
+                this,
+                info,
+                mSubDefaults,
+                mReceiverHandler.getLooper()
+        );
+
         mMobileSignalControllers.put(id, controller);
         controller.getState().userSetup = true;
         return info;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 87cdb17..12f2c22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -222,7 +222,6 @@
         mCurrentState.connected = mWifiTracker.connected;
         mCurrentState.ssid = mWifiTracker.ssid;
         mCurrentState.rssi = mWifiTracker.rssi;
-        boolean levelChanged = mCurrentState.level != mWifiTracker.level;
         mCurrentState.level = mWifiTracker.level;
         mCurrentState.statusLabel = mWifiTracker.statusLabel;
         mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -230,10 +229,6 @@
         mCurrentState.iconGroup =
                 mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
                         : mUnmergedWifiIconGroup;
-
-        if (levelChanged) {
-            mNetworkController.notifyWifiLevelChange(mCurrentState.level);
-        }
     }
 
     boolean isCarrierMergedWifi(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 126a986..dbf4810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -18,8 +18,10 @@
 
 import android.animation.ObjectAnimator
 import android.util.FloatProperty
+import com.android.systemui.Dumpable
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,17 +34,20 @@
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.math.min
 
 @SysUISingleton
 class NotificationWakeUpCoordinator @Inject constructor(
+    dumpManager: DumpManager,
     private val mHeadsUpManager: HeadsUpManager,
     private val statusBarStateController: StatusBarStateController,
     private val bypassController: KeyguardBypassController,
     private val dozeParameters: DozeParameters,
     private val screenOffAnimationController: ScreenOffAnimationController
-) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener,
+    Dumpable {
 
     private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
         "notificationVisibility") {
@@ -60,6 +65,7 @@
 
     private var mLinearDozeAmount: Float = 0.0f
     private var mDozeAmount: Float = 0.0f
+    private var mDozeAmountSource: String = "init"
     private var mNotificationVisibleAmount = 0.0f
     private var mNotificationsVisible = false
     private var mNotificationsVisibleForExpansion = false
@@ -142,6 +148,7 @@
         }
 
     init {
+        dumpManager.registerDumpable(this)
         mHeadsUpManager.addListener(this)
         statusBarStateController.addCallback(this)
         addListener(object : WakeUpListener {
@@ -248,13 +255,14 @@
             // Let's notify the scroller that an animation started
             notifyAnimationStart(mLinearDozeAmount == 1.0f)
         }
-        setDozeAmount(linear, eased)
+        setDozeAmount(linear, eased, source = "StatusBar")
     }
 
-    fun setDozeAmount(linear: Float, eased: Float) {
+    fun setDozeAmount(linear: Float, eased: Float, source: String) {
         val changed = linear != mLinearDozeAmount
         mLinearDozeAmount = linear
         mDozeAmount = eased
+        mDozeAmountSource = source
         mStackScrollerController.setDozeAmount(mDozeAmount)
         updateHideAmount()
         if (changed && linear == 0.0f) {
@@ -271,7 +279,7 @@
             // undefined state, so it's an indication that we should do state cleanup. We override
             // the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
             // See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
-            setDozeAmount(0f, 0f)
+            setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)")
         }
 
         if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) {
@@ -311,12 +319,11 @@
      */
     private fun overrideDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
-            var amount = 1.0f
-            if (statusBarStateController.state == StatusBarState.SHADE ||
-                statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
-                amount = 0.0f
+            if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+                setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
+            } else {
+                setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
             }
-            setDozeAmount(amount, amount)
             return true
         }
         return false
@@ -332,7 +339,7 @@
      */
     private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
         if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
-            setDozeAmount(1f, 1f)
+            setDozeAmount(1f, 1f, source = "Override: animating screen off")
             return true
         }
 
@@ -426,4 +433,24 @@
          */
         @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
     }
-}
\ No newline at end of file
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("mLinearDozeAmount: $mLinearDozeAmount")
+        pw.println("mDozeAmount: $mDozeAmount")
+        pw.println("mDozeAmountSource: $mDozeAmountSource")
+        pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
+        pw.println("mNotificationsVisible: $mNotificationsVisible")
+        pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
+        pw.println("mVisibilityAmount: $mVisibilityAmount")
+        pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+        pw.println("pulseExpanding: $pulseExpanding")
+        pw.println("state: ${StatusBarState.toString(state)}")
+        pw.println("fullyAwake: $fullyAwake")
+        pw.println("wakingUp: $wakingUp")
+        pw.println("willWakeUp: $willWakeUp")
+        pw.println("collapsedEnoughToHide: $collapsedEnoughToHide")
+        pw.println("pulsing: $pulsing")
+        pw.println("notificationsFullyHidden: $notificationsFullyHidden")
+        pw.println("canShowPulsingHuns: $canShowPulsingHuns")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 6dbbf0d..fc8e7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -30,7 +30,6 @@
 import com.android.systemui.R;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -255,9 +254,7 @@
 
     public void addMobileView(MobileIconState state) {
         Log.d(TAG, "addMobileView: ");
-        StatusBarMobileView view = StatusBarMobileView.fromContext(
-                mContext, state.slot,
-                mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+        StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot);
 
         view.applyMobileState(state);
         view.setStaticDrawableColor(mColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index a2140c6ab..7b8c5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -423,6 +423,21 @@
                 + getActualPaddingEnd();
     }
 
+    @VisibleForTesting
+    boolean shouldForceOverflow(int i, int speedBumpIndex, float iconAppearAmount,
+            int maxVisibleIcons) {
+        return speedBumpIndex != -1 && i >= speedBumpIndex
+                && iconAppearAmount > 0.0f || i >= maxVisibleIcons;
+    }
+
+    @VisibleForTesting
+    boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd,
+            float iconSize) {
+        // Layout end, as used here, does not include padding end.
+        final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize;
+        return translationX >= overflowX;
+    }
+
     /**
      * Calculate the horizontal translations for each notification based on how much the icons
      * are inserted into the notification container.
@@ -448,26 +463,26 @@
             if (mFirstVisibleIconState == null) {
                 mFirstVisibleIconState = iconState;
             }
-            boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
-                    && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
-            boolean isLastChild = i == childCount - 1;
-            float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
-                    ? ((StatusBarIconView) view).getIconScaleIncreased()
-                    : 1f;
             iconState.visibleState = iconState.hidden
                     ? StatusBarIconView.STATE_HIDDEN
                     : StatusBarIconView.STATE_ICON;
 
-            final float overflowDotX = layoutEnd - mIconSize;
-            boolean isOverflowing = translationX > overflowDotX;
+            final boolean forceOverflow = shouldForceOverflow(i, mSpeedBumpIndex,
+                    iconState.iconAppearAmount, maxVisibleIcons);
+            final boolean isOverflowing = forceOverflow || isOverflowing(
+                    /* isLastChild= */ i == childCount - 1, translationX, layoutEnd, mIconSize);
 
-            if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
-                firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
+            // First icon to overflow.
+            if (firstOverflowIndex == -1 && isOverflowing) {
+                firstOverflowIndex = i;
                 mVisualOverflowStart = layoutEnd - mIconSize;
                 if (forceOverflow || mIsStaticLayout) {
                     mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
                 }
             }
+            final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
+                    ? ((StatusBarIconView) view).getIconScaleIncreased()
+                    : 1f;
             translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
         }
         mNumDots = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 31d9266..30b640b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -38,7 +38,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.demomode.DemoModeCommandReceiver;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -361,9 +360,7 @@
         }
 
         private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
-            StatusBarMobileView view = StatusBarMobileView.fromContext(
-                            mContext, slot,
-                    mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+            StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
             return view;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4d9c811..dac532b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
 import static android.view.WindowInsets.Type.navigationBars;
 
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -387,6 +388,7 @@
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
         } else if (mShowing && !hideBouncerOverDream) {
             if (!isWakeAndUnlocking()
+                    && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
                     && !mCentralSurfaces.isInLaunchTransition()
                     && !isUnlockCollapsing()) {
                 mBouncer.setExpansion(fraction);
@@ -398,9 +400,8 @@
             }
         } else if (!mShowing && mBouncer.inTransit()) {
             // Keyguard is not visible anymore, but expansion animation was still running.
-            // We need to keep propagating the expansion state to the bouncer, otherwise it will be
-            // stuck in transit.
-            mBouncer.setExpansion(fraction);
+            // We need to hide the bouncer, otherwise it will be stuck in transit.
+            mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
         } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
             // Panel expanded while pulsing but didn't translate the bouncer (because we are
             // unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -739,8 +740,10 @@
         }
         mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
 
-        // setDozing(false) will call reset once we stop dozing.
-        if (!mDozing) {
+        // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
+        // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
+        // unexpected visible behavior if the keyguard is still visible as we're animating unlocked.
+        if (!mDozing && !mKeyguardStateController.isKeyguardGoingAway()) {
             // If Keyguard is reshown, don't hide the bouncer as it might just have been requested
             // by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
             reset(isOccluding /* hideBouncerWhenShowing*/);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index ee242a4..492734e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,8 +26,6 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -66,7 +64,6 @@
     private final Handler mHandler = Handler.getMain();
     private final CarrierConfigTracker mCarrierConfigTracker;
     private final TunerService mTunerService;
-    private final FeatureFlags mFeatureFlags;
 
     private boolean mHideAirplane;
     private boolean mHideMobile;
@@ -90,8 +87,7 @@
             CarrierConfigTracker carrierConfigTracker,
             NetworkController networkController,
             SecurityController securityController,
-            TunerService tunerService,
-            FeatureFlags featureFlags
+            TunerService tunerService
     ) {
         mContext = context;
 
@@ -100,7 +96,6 @@
         mNetworkController = networkController;
         mSecurityController = securityController;
         mTunerService = tunerService;
-        mFeatureFlags = featureFlags;
 
         mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
         mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -378,40 +373,6 @@
     }
 
     @Override
-    public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
-            boolean noNetworksAvailable) {
-        if (!mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "setConnectivityStatus: "
-                    + "noDefaultNetwork = " + noDefaultNetwork + ","
-                    + "noValidatedNetwork = " + noValidatedNetwork + ","
-                    + "noNetworksAvailable = " + noNetworksAvailable);
-        }
-        WifiIconState newState = mWifiIconState.copy();
-        newState.noDefaultNetwork = noDefaultNetwork;
-        newState.noValidatedNetwork = noValidatedNetwork;
-        newState.noNetworksAvailable = noNetworksAvailable;
-        newState.slot = mSlotWifi;
-        newState.airplaneSpacerVisible = mIsAirplaneMode;
-        if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
-            newState.visible = true;
-            newState.resId = R.drawable.ic_qs_no_internet_unavailable;
-        } else if (noDefaultNetwork && !noNetworksAvailable
-                && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
-            newState.visible = true;
-            newState.resId = R.drawable.ic_qs_no_internet_available;
-        } else {
-            newState.visible = false;
-            newState.resId = 0;
-        }
-        updateWifiIconWithState(newState);
-        mWifiIconState = newState;
-    }
-
-
-    @Override
     public void setEthernetIndicators(IconState state) {
         boolean visible = state.visible && !mHideEthernet;
         int resId = state.icon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index f406825..f61b488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -473,6 +473,10 @@
         int state = mAnimationScheduler.getAnimationState();
         if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
             animateShow(mEndSideContent, animate);
+        } else {
+            // We are in the middle of a system status event animation, which will animate the
+            // alpha (but not the visibility). Allow the view to become visible again
+            mEndSideContent.setVisibility(View.VISIBLE);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
new file mode 100644
index 0000000..6c02b0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface exposing a flow for raw connectivity information. Clients should collect on
+ * [rawConnectivityInfoFlow] to get updates on connectivity information.
+ *
+ * Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
+ * and all clients get references to the same flow.
+ *
+ * This will be used for the new status bar pipeline to compile information we need to display some
+ * of the icons in the RHS of the status bar.
+ */
+interface ConnectivityInfoCollector {
+    val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
+}
+
+/**
+ * An object containing all of the raw connectivity information.
+ *
+ * Importantly, all the information in this object should not be processed at all (i.e., the data
+ * that we receive from callbacks should be piped straight into this object and not be filtered,
+ * manipulated, or processed in any way). Instead, any listeners on
+ * [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
+ *
+ * This allows us to keep all the processing in one place which is beneficial for logging and
+ * debugging purposes.
+ */
+data class RawConnectivityInfo(
+        val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
new file mode 100644
index 0000000..8d69422
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilitiesRepo
+import kotlinx.coroutines.CoroutineScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * The real implementation of [ConnectivityInfoCollector] that will collect information from all the
+ * relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
+ */
+@SysUISingleton
+class ConnectivityInfoCollectorImpl @Inject constructor(
+        networkCapabilitiesRepo: NetworkCapabilitiesRepo,
+        @Application scope: CoroutineScope,
+) : ConnectivityInfoCollector {
+    override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
+            // TODO(b/238425913): Collect all the separate flows for individual raw information into
+            //   this final flow.
+            networkCapabilitiesRepo.dataStream
+                    .map {
+                        RawConnectivityInfo(networkCapabilityInfo = it)
+                    }
+                    .stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
index a841914..1aae250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
@@ -19,7 +19,15 @@
 import android.content.Context
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
 
 /**
  * A processor that transforms raw connectivity information that we get from callbacks and turns it
@@ -27,20 +35,43 @@
  *
  * This will be used for the new status bar pipeline to calculate the list of icons that should be
  * displayed in the RHS of the status bar.
+ *
+ * Anyone can listen to [processedInfoFlow] to get updates to the processed data.
  */
 @SysUISingleton
 class ConnectivityInfoProcessor @Inject constructor(
+        connectivityInfoCollector: ConnectivityInfoCollector,
         context: Context,
-        private val statusBarPipelineFlags: StatusBarPipelineFlags,
+        @Application private val scope: CoroutineScope,
+        statusBarPipelineFlags: StatusBarPipelineFlags,
 ) : CoreStartable(context) {
+    // Note: This flow will not start running until a client calls `collect` on it, which means that
+    // [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
+    // happens.
+    val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
+            if (!statusBarPipelineFlags.isNewPipelineEnabled())
+                emptyFlow()
+            else connectivityInfoCollector.rawConnectivityInfoFlow
+                    .map { it.process() }
+                    .stateIn(
+                            scope,
+                            started = Lazily,
+                            initialValue = ProcessedConnectivityInfo()
+                    )
+
     override fun start() {
-        if (statusBarPipelineFlags.isNewPipelineEnabled()) {
-            init()
-        }
     }
 
-    /** Initializes this processor and everything it depends on. */
-    private fun init() {
-        // TODO(b/238425913): Register all the connectivity callbacks here.
+    private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
+        // TODO(b/238425913): Actually process the raw info into meaningful data.
+        return ProcessedConnectivityInfo(this.networkCapabilityInfo)
     }
 }
+
+/**
+ * An object containing connectivity info that has been processed into data that can be directly
+ * used by the status bar (and potentially other SysUI areas) to display icons.
+ */
+data class ProcessedConnectivityInfo(
+        val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 771bb0c..c4e2b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.pipeline.dagger
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
 import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
 import dagger.Binds
 import dagger.Module
@@ -30,4 +32,9 @@
     @IntoMap
     @ClassKey(ConnectivityInfoProcessor::class)
     abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
+
+    @Binds
+    abstract fun provideConnectivityInfoCollector(
+            impl: ConnectivityInfoCollectorImpl
+    ): ConnectivityInfoCollector
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 85ecfd3..035404c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,6 +16,7 @@
 
 package com.android.keyguard;
 
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
@@ -31,7 +32,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -41,6 +41,7 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.IStrongAuthTracker;
 import android.app.trust.TrustManager;
@@ -52,6 +53,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
@@ -68,6 +70,7 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IRemoteCallback;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telephony.ServiceState;
@@ -182,6 +185,18 @@
     private ActiveUnlockConfig mActiveUnlockConfig;
     @Mock
     private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
+    @Mock
+    private IActivityManager mActivityService;
+
+    private final int mCurrentUserId = 100;
+    private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
+
+    @Captor
+    private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
+            mBiometricEnabledCallbackArgCaptor;
+    @Captor
+    private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
+
     // Direct executor
     private Executor mBackgroundExecutor = Runnable::run;
     private Executor mMainExecutor = Runnable::run;
@@ -190,18 +205,16 @@
     private TestableContext mSpiedContext;
     private MockitoSession mMockitoSession;
     private StatusBarStateController.StateListener mStatusBarStateListener;
+    private IBiometricEnabledOnKeyguardCallback mBiometricEnabledOnKeyguardCallback;
 
     @Before
-    public void setup() {
+    public void setup() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         mSpiedContext = spy(mContext);
         when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
         when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
-        doAnswer(invocation -> {
-            IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
-            callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser());
-            return null;
-        }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
+        when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo);
+        when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId);
         when(mFaceManager.isHardwareDetected()).thenReturn(true);
         when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
         when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
@@ -262,13 +275,20 @@
                 .startMocking();
         ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                 .when(SubscriptionManager::getDefaultSubscriptionId);
+        KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
         ExtendedMockito.doReturn(KeyguardUpdateMonitor.getCurrentUser())
                 .when(ActivityManager::getCurrentUser);
+        ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);
 
         mTestableLooper = TestableLooper.get(this);
         allowTestableLooperAsMainThread();
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
 
+        verify(mBiometricManager)
+                .registerEnabledOnKeyguardCallback(mBiometricEnabledCallbackArgCaptor.capture());
+        mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
+        biometricsEnabledForCurrentUser();
+
         verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
         mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
         mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -718,7 +738,6 @@
         verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
     }
 
-
     @Test
     public void testFaceAndFingerprintLockout() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -924,6 +943,7 @@
 
     @Test
     public void testSecondaryLockscreenRequirement() {
+        KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
         int user = KeyguardUpdateMonitor.getCurrentUser();
         String packageName = "fake.test.package";
         String cls = "FakeService";
@@ -1097,8 +1117,7 @@
     @Test
     public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
         // GIVEN state for face auth should run aside from StatusBarState
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
-                KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+        biometricsNotDisabledThroughDevicePolicyManager();
         mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
         setKeyguardBouncerVisibility(false /* isVisible */);
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -1153,6 +1172,306 @@
         verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
     }
 
+    @Test
+    public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
+        mFaceManager = null;
+        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        strongAuthNotRequired();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        // Fingerprint is locked out.
+        fingerprintErrorLockedOut();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        bouncerFullyVisibleAndNotGoingToSleep();
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        strongAuthNotRequired();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        triggerSuccessfulFaceAuth();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+        // This disables face auth
+        when(mUserManager.isPrimaryUser()).thenReturn(false);
+        mKeyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        strongAuthNotRequired();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        biometricsEnabledForCurrentUser();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        userNotCurrentlySwitching();
+
+        // This disables face auth
+        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+        mTestableLooper.processAllMessages();
+
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        // This disables face auth
+        biometricsDisabledForCurrentUser();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        userCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        secureCameraLaunched();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        secureCameraLaunched();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        occludingAppRequestsFaceAuth();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        bouncerFullyVisibleAndNotGoingToSleep();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
+            throws RemoteException {
+        // Face auth should run when the following is true.
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        triggerAuthInterrupt();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    private void triggerAuthInterrupt() {
+        mKeyguardUpdateMonitor.onAuthInterruptDetected(true);
+    }
+
+    private void occludingAppRequestsFaceAuth() {
+        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+    }
+
+    private void secureCameraLaunched() {
+        mKeyguardUpdateMonitor.onCameraLaunched();
+    }
+
+    private void userCurrentlySwitching() {
+        mKeyguardUpdateMonitor.setSwitchingUser(true);
+    }
+
+    private void fingerprintErrorLockedOut() {
+        mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+                .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
+    }
+
+    private void triggerSuccessfulFaceAuth() {
+        mKeyguardUpdateMonitor.requestFaceAuth(true);
+        verify(mFaceManager).authenticate(any(),
+                any(),
+                mAuthenticationCallbackCaptor.capture(),
+                any(),
+                anyInt(),
+                anyBoolean());
+        mAuthenticationCallbackCaptor.getValue()
+                .onAuthenticationSucceeded(
+                        new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
+    }
+
+    private void currentUserIsPrimary() {
+        when(mUserManager.isPrimaryUser()).thenReturn(true);
+    }
+
+    private void biometricsNotDisabledThroughDevicePolicyManager() {
+        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+                KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+    }
+
+    private void biometricsEnabledForCurrentUser() throws RemoteException {
+        mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
+    }
+
+    private void biometricsDisabledForCurrentUser() throws RemoteException {
+        mBiometricEnabledOnKeyguardCallback.onChanged(
+                false,
+                KeyguardUpdateMonitor.getCurrentUser()
+        );
+    }
+
+    private void strongAuthNotRequired() {
+        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(0);
+    }
+
+    private void currentUserDoesNotHaveTrust() {
+        mKeyguardUpdateMonitor.onTrustChanged(
+                false,
+                KeyguardUpdateMonitor.getCurrentUser(),
+                -1,
+                new ArrayList<>()
+        );
+    }
+
+    private void userNotCurrentlySwitching() {
+        mKeyguardUpdateMonitor.setSwitchingUser(false);
+    }
+
+    private void keyguardNotGoingAway() {
+        mKeyguardUpdateMonitor.setKeyguardGoingAway(false);
+    }
+
+    private void bouncerFullyVisibleAndNotGoingToSleep() {
+        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true, true);
+        mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1);
+    }
+
     private void setKeyguardBouncerVisibility(boolean isVisible) {
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 6978490..3ac28c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -35,12 +35,12 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
 import org.mockito.Mockito.eq
-import org.mockito.Mockito.reset
 import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -63,6 +63,7 @@
         controller = WiredChargingRippleController(
                 commandRegistry, batteryController, configurationController,
                 featureFlags, context, windowManager, systemClock, uiEventLogger)
+        rippleView.setupShader()
         controller.rippleView = rippleView // Replace the real ripple view with a mock instance
         controller.registerCallbacks()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index edcf479..8073103 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -39,6 +39,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -112,7 +113,7 @@
         mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
                 mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
                 mSystemActions, mOverviewProxyService, mAssistManagerLazy,
-                () -> Optional.of(mock(CentralSurfaces.class)),
+                () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardViewController.class),
                 mNavigationModeController, mUserTracker, mDumpManager);
 
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index fe5f433..51f0953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,6 +72,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -193,6 +194,8 @@
     @Mock
     private CentralSurfaces mCentralSurfaces;
     @Mock
+    private KeyguardViewController mKeyguardViewController;
+    @Mock
     private UserContextProvider mUserContextProvider;
     @Mock
     private Resources mResources;
@@ -237,8 +240,8 @@
                     mock(AccessibilityButtonTargetsObserver.class),
                     mSystemActions, mOverviewProxyService,
                     () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
-                    mock(NavigationModeController.class), mock(UserTracker.class),
-                    mock(DumpManager.class)));
+                    mKeyguardViewController, mock(NavigationModeController.class),
+                    mock(UserTracker.class), mock(DumpManager.class)));
             mNavigationBar = createNavBar(mContext);
             mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
         });
@@ -377,7 +380,7 @@
 
         // Verify navbar didn't alter and showing back icon when the keyguard is showing without
         // requesting IME insets visible.
-        doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
+        doReturn(true).when(mKeyguardViewController).isShowing();
         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 07c8af9..be14cc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.qs.carrier.QSCarrierGroup
 import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -46,10 +45,10 @@
 import org.mockito.Answers
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -162,7 +161,6 @@
 
     @Test
     fun testRSSISlot_notCombined() {
-        `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false)
         controller.init()
 
         val captor = argumentCaptor<List<String>>()
@@ -174,20 +172,6 @@
     }
 
     @Test
-    fun testRSSISlot_combined() {
-        `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(true)
-        controller.init()
-
-        val captor = argumentCaptor<List<String>>()
-        verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
-
-        assertThat(captor.value).containsExactly(
-            mContext.getString(com.android.internal.R.string.status_bar_no_calling),
-            mContext.getString(com.android.internal.R.string.status_bar_call_strength)
-        )
-    }
-
-    @Test
     fun testSingleCarrierCallback() {
         controller.init()
         reset(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 09ce37b..1e7722a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -44,7 +44,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.CarrierTextManager;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -88,7 +87,6 @@
     @Mock
     private QSCarrier mQSCarrier3;
     private TestableLooper mTestableLooper;
-    @Mock private FeatureFlags mFeatureFlags;
     @Mock
     private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
 
@@ -130,7 +128,7 @@
         mQSCarrierGroupController = new QSCarrierGroupController.Builder(
                 mActivityStarter, handler, TestableLooper.get(this).getLooper(),
                 mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
-                mFeatureFlags, mSlotIndexResolver)
+                mSlotIndexResolver)
                 .setQSCarrierGroup(mQSCarrierGroup)
                 .build();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 5212255..99a17a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -22,13 +22,11 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.util.FeatureFlagUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -59,14 +57,14 @@
 
     @Test
     public void testUpdateState_first() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, false));
     }
 
     @Test
     public void testUpdateState_same() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, false));
         assertFalse(mQSCarrier.updateState(c, false));
@@ -74,7 +72,7 @@
 
     @Test
     public void testUpdateState_changed() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, false));
 
@@ -85,14 +83,14 @@
 
     @Test
     public void testUpdateState_singleCarrier_first() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c, true));
     }
 
     @Test
     public void testUpdateState_singleCarrier_noShowIcon() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         mQSCarrier.updateState(c, true);
 
@@ -101,7 +99,7 @@
 
     @Test
     public void testUpdateState_multiCarrier_showIcon() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         mQSCarrier.updateState(c, false);
 
@@ -110,7 +108,7 @@
 
     @Test
     public void testUpdateState_changeSingleMultiSingle() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         mQSCarrier.updateState(c, true);
         assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
new file mode 100644
index 0000000..2d2f4cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ripple
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RippleViewTest : SysuiTestCase() {
+    @Mock
+    private lateinit var rippleView: RippleView
+
+    @Before
+    fun setup() {
+        rippleView = RippleView(context, null)
+    }
+
+    @Test
+    fun testSetupShader_compilesCircle() {
+        rippleView.setupShader(RippleShader.RippleShape.CIRCLE)
+    }
+
+    @Test
+    fun testSetupShader_compilesRoundedBox() {
+        rippleView.setupShader(RippleShader.RippleShape.ROUNDED_BOX)
+    }
+
+    @Test
+    fun testSetupShader_compilesEllipse() {
+        rippleView.setupShader(RippleShader.RippleShape.ELLIPSE)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
new file mode 100644
index 0000000..ce3f20d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.Binder
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test the logic within ImageCaptureImpl
+ */
+@RunWith(AndroidTestingRunner::class)
+class ImageCaptureImplTest : SysuiTestCase() {
+    private val displayManager = mock<DisplayManager>()
+    private val atmService = mock<IActivityTaskManager>()
+    private val capture = TestableImageCaptureImpl(displayManager, atmService)
+
+    @Test
+    fun captureDisplayWithCrop() {
+        capture.captureDisplay(Display.DEFAULT_DISPLAY, Rect(1, 2, 3, 4))
+        assertThat(capture.token).isNotNull()
+        assertThat(capture.width!!).isEqualTo(2)
+        assertThat(capture.height!!).isEqualTo(2)
+        assertThat(capture.crop!!).isEqualTo(Rect(1, 2, 3, 4))
+    }
+
+    @Test
+    fun captureDisplayWithNullCrop() {
+        capture.captureDisplay(Display.DEFAULT_DISPLAY, null)
+        assertThat(capture.token).isNotNull()
+        assertThat(capture.width!!).isEqualTo(0)
+        assertThat(capture.height!!).isEqualTo(0)
+        assertThat(capture.crop!!).isEqualTo(Rect())
+    }
+
+    class TestableImageCaptureImpl(
+        displayManager: DisplayManager,
+        atmService: IActivityTaskManager
+    ) :
+        ImageCaptureImpl(displayManager, atmService) {
+
+        var token: IBinder? = null
+        var width: Int? = null
+        var height: Int? = null
+        var crop: Rect? = null
+
+        override fun physicalDisplayToken(displayId: Int): IBinder {
+            return Binder()
+        }
+
+        override fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect):
+                ScreenshotHardwareBuffer {
+            this.token = displayToken
+            this.width = width
+            this.height = height
+            this.crop = crop
+            return ScreenshotHardwareBuffer(null, null, false, false)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
new file mode 100644
index 0000000..002f23a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.ColorSpace
+import android.graphics.Insets
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.net.Uri
+import android.view.WindowManager
+import android.view.WindowManager.ScreenshotSource
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
+import org.junit.Test
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.isNull
+
+class RequestProcessorTest {
+    private val controller = mock<ScreenshotController>()
+    private val bitmapCaptor = argumentCaptor<Bitmap>()
+
+    @Test
+    fun testFullScreenshot() {
+        val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+        val onSavedListener = mock<Consumer<Uri>>()
+        val callback = mock<RequestCallback>()
+        val processor = RequestProcessor(controller)
+
+        processor.processRequest(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, onSavedListener,
+            request, callback)
+
+        verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
+            eq(onSavedListener), eq(callback))
+    }
+
+    @Test
+    fun testSelectedRegionScreenshot() {
+        val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+        val onSavedListener = mock<Consumer<Uri>>()
+        val callback = mock<RequestCallback>()
+        val processor = RequestProcessor(controller)
+
+        processor.processRequest(WindowManager.TAKE_SCREENSHOT_SELECTED_REGION, onSavedListener,
+            request, callback)
+
+        verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
+            eq(onSavedListener), eq(callback))
+    }
+
+    @Test
+    fun testProvidedImageScreenshot() {
+        val taskId = 1111
+        val userId = 2222
+        val bounds = Rect(50, 50, 150, 150)
+        val topComponent = ComponentName("test", "test")
+        val processor = RequestProcessor(controller)
+
+        val buffer = HardwareBuffer.create(100, 100, HardwareBuffer.RGBA_8888, 1,
+            HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+        val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+        val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+        val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_OTHER, bitmapBundle,
+            bounds, Insets.NONE, taskId, userId, topComponent)
+
+        val onSavedListener = mock<Consumer<Uri>>()
+        val callback = mock<RequestCallback>()
+
+        processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener,
+            request, callback)
+
+        verify(controller).handleImageAsScreenshot(
+            bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
+            eq(topComponent), eq(onSavedListener), eq(callback)
+        )
+
+        assertThat(bitmapCaptor.value.equalsHardwareBitmap(bitmap)).isTrue()
+    }
+
+    private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
+        return bitmap.hardwareBuffer == this.hardwareBuffer &&
+                bitmap.colorSpace == this.colorSpace
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index c9405c8..fc28349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -47,6 +47,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.PointF;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -461,6 +462,7 @@
 
         NotificationWakeUpCoordinator coordinator =
                 new NotificationWakeUpCoordinator(
+                        mDumpManager,
                         mock(HeadsUpManagerPhone.class),
                         new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
                                 mInteractionJankMonitor),
@@ -527,7 +529,7 @@
                 mNotificationShadeWindowController,
                 mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
                 mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
-                mMetricsLogger, mActivityManager, mConfigurationController,
+                mMetricsLogger, mConfigurationController,
                 () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
                 mConversationNotificationManager, mMediaHiearchyManager,
                 mStatusBarKeyguardViewManager,
@@ -902,6 +904,76 @@
     }
 
     @Test
+    public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isTrue();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_pulsing_isCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
+    public void keyguardStatusView_singleShade_isCentered() {
+        enableSplitShade(/* enabled= */ false);
+        // The conditions below would make the clock NOT be centered on split shade.
+        // On single shade it should always be centered though.
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+        mStatusBarStateController.setState(KEYGUARD);
+        setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
+
+        assertThat(isKeyguardStatusViewCentered()).isFalse();
+    }
+
+    @Test
     public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
         givenViewAttached();
         when(mResources.getBoolean(
@@ -1473,4 +1545,19 @@
                 .thenReturn(splitShadeFullTransitionDistance);
         mNotificationPanelViewController.updateResources();
     }
+
+    private void setDozing(boolean dozing, boolean dozingAlwaysOn) {
+        when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
+        mNotificationPanelViewController.setDozing(
+                /* dozing= */ dozing,
+                /* animate= */ false,
+                /* wakeUpTouchLocation= */ new PointF()
+        );
+    }
+
+    private boolean isKeyguardStatusViewCentered() {
+        mNotificationPanelViewController.updateResources();
+        return getConstraintSetLayout(R.id.keyguard_status_view).endToEnd
+                == ConstraintSet.PARENT_ID;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 0d1879c..f8a0d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -70,8 +70,6 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -127,8 +125,8 @@
     protected CarrierConfigTracker mCarrierConfigTracker;
     protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
     protected Handler mMainHandler;
-    protected FeatureFlags mFeatureFlags;
     protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
+    protected MobileSignalControllerFactory mMobileFactory;
 
     protected int mSubId;
 
@@ -158,9 +156,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mFeatureFlags = mock(FeatureFlags.class);
-        when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false);
-
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
         TestableResources res = mContext.getOrCreateTestableResources();
@@ -224,6 +219,11 @@
 
         mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
                 mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+        mMobileFactory = new MobileSignalControllerFactory(
+                mContext,
+                mCallbackHandler,
+                mCarrierConfigTracker
+        );
 
         mNetworkController = new NetworkControllerImpl(mContext,
                 mMockCm,
@@ -243,8 +243,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class)
         );
@@ -438,10 +438,6 @@
         updateSignalStrength();
     }
 
-    public void setImsType(int imsType) {
-        mMobileSignalController.setImsType(imsType);
-    }
-
     public void setIsGsm(boolean gsm) {
         when(mSignalStrength.isGsm()).thenReturn(gsm);
         updateSignalStrength();
@@ -637,5 +633,4 @@
     protected void assertDataNetworkNameEquals(String expected) {
         assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
     }
-
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index e3dd6f4..ed8a3e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -145,8 +145,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 new Handler(TestableLooper.get(this).getLooper()),
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 698899a..a76676e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -85,8 +85,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class)
         );
@@ -121,8 +121,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         TestableLooper.get(this).processAllMessages();
@@ -155,8 +155,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
@@ -192,8 +192,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         mNetworkController.registerListeners();
@@ -277,8 +277,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
+                mMobileFactory,
                 mMainHandler,
-                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 3f71491..68170ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -30,7 +30,6 @@
 import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.telephony.CellSignalStrength;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -285,44 +284,6 @@
         verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
     }
 
-    @Test
-    public void testCallStrengh() {
-        if (true) return;
-        String testSsid = "Test SSID";
-        setWifiEnabled(true);
-        setWifiState(true, testSsid);
-        // Set the ImsType to be IMS_TYPE_WLAN
-        setImsType(2);
-        setWifiLevel(1);
-        for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
-            setWifiLevel(testLevel);
-            verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
-        }
-        // Set the ImsType to be IMS_TYPE_WWAN
-        setImsType(1);
-        setupDefaultSignal();
-        for (int testStrength = 0;
-                testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
-            setLevel(testStrength);
-            verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
-        }
-    }
-
-    @Test
-    public void testNonPrimaryWiFi() {
-        if (true) return;
-        String testSsid = "Test SSID";
-        setWifiEnabled(true);
-        setWifiState(true, testSsid);
-        // Set the ImsType to be IMS_TYPE_WLAN
-        setImsType(2);
-        setWifiLevel(1);
-        verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
-        when(mWifiInfo.isPrimary()).thenReturn(false);
-        setWifiLevel(3);
-        verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
-    }
-
     protected void setWifiActivity(int activity) {
         // TODO: Not this, because this variable probably isn't sticking around.
         mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 2ff6dd4..086e5df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -153,6 +153,106 @@
         assertTrue(iconContainer.hasOverflow())
     }
 
+    @Test
+    fun shouldForceOverflow_appearingAboveSpeedBump_true() {
+        val forceOverflow = iconContainer.shouldForceOverflow(
+                /* i= */ 1,
+                /* speedBumpIndex= */ 0,
+                /* iconAppearAmount= */ 1f,
+                /* maxVisibleIcons= */ 5
+        )
+        assertTrue(forceOverflow);
+    }
+
+    @Test
+    fun shouldForceOverflow_moreThanMaxVisible_true() {
+        val forceOverflow = iconContainer.shouldForceOverflow(
+                /* i= */ 10,
+                /* speedBumpIndex= */ 11,
+                /* iconAppearAmount= */ 0f,
+                /* maxVisibleIcons= */ 5
+        )
+        assertTrue(forceOverflow);
+    }
+
+    @Test
+    fun shouldForceOverflow_belowSpeedBumpAndLessThanMaxVisible_false() {
+        val forceOverflow = iconContainer.shouldForceOverflow(
+                /* i= */ 0,
+                /* speedBumpIndex= */ 11,
+                /* iconAppearAmount= */ 0f,
+                /* maxVisibleIcons= */ 5
+        )
+        assertFalse(forceOverflow);
+    }
+
+    @Test
+    fun isOverflowing_lastChildXLessThanLayoutEnd_false() {
+        val isOverflowing = iconContainer.isOverflowing(
+                /* isLastChild= */ true,
+                /* translationX= */ 0f,
+                /* layoutEnd= */ 10f,
+                /* iconSize= */ 2f,
+        )
+        assertFalse(isOverflowing)
+    }
+
+
+    @Test
+    fun isOverflowing_lastChildXEqualToLayoutEnd_true() {
+        val isOverflowing = iconContainer.isOverflowing(
+                /* isLastChild= */ true,
+                /* translationX= */ 10f,
+                /* layoutEnd= */ 10f,
+                /* iconSize= */ 2f,
+        )
+        assertTrue(isOverflowing)
+    }
+
+    @Test
+    fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() {
+        val isOverflowing = iconContainer.isOverflowing(
+                /* isLastChild= */ true,
+                /* translationX= */ 20f,
+                /* layoutEnd= */ 10f,
+                /* iconSize= */ 2f,
+        )
+        assertTrue(isOverflowing)
+    }
+
+    @Test
+    fun isOverflowing_notLastChildXLessThanDotX_false() {
+        val isOverflowing = iconContainer.isOverflowing(
+                /* isLastChild= */ false,
+                /* translationX= */ 0f,
+                /* layoutEnd= */ 10f,
+                /* iconSize= */ 2f,
+        )
+        assertFalse(isOverflowing)
+    }
+
+    @Test
+    fun isOverflowing_notLastChildXGreaterThanDotX_true() {
+        val isOverflowing = iconContainer.isOverflowing(
+                /* isLastChild= */ false,
+                /* translationX= */ 20f,
+                /* layoutEnd= */ 10f,
+                /* iconSize= */ 2f,
+        )
+        assertTrue(isOverflowing)
+    }
+
+    @Test
+    fun isOverflowing_notLastChildXEqualToDotX_true() {
+        val isOverflowing = iconContainer.isOverflowing(
+                /* isLastChild= */ false,
+                /* translationX= */ 8f,
+                /* layoutEnd= */ 10f,
+                /* iconSize= */ 2f,
+        )
+        assertTrue(isOverflowing)
+    }
+
     private fun mockStatusBarIcon() : StatusBarIconView {
         val iconView = mock(StatusBarIconView::class.java)
         whenever(iconView.width).thenReturn(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1046dbc..2dcdcfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -202,12 +202,12 @@
     }
 
     @Test
-    public void onPanelExpansionChanged_propagatesToBouncer_evenAfterHidden() {
+    public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
         mStatusBarKeyguardViewManager.hide(0, 0);
         when(mBouncer.inTransit()).thenReturn(true);
 
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mBouncer).setExpansion(eq(EXPANSION_EVENT.getFraction()));
+        verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
     }
 
     @Test
@@ -249,6 +249,23 @@
     }
 
     @Test
+    public void onPanelExpansionChanged_neverTranslatesBouncerWhenDismissBouncer() {
+        // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
+        // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
+        // which would mistakenly cause the bouncer to show briefly before its visibility
+        // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
+        // bouncer if the bouncer is dismissing as a result of a biometric unlock.
+        when(mBiometricUnlockController.getMode())
+                .thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
+        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+                expansionEvent(
+                        /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+                        /* expanded= */ true,
+                        /* tracking= */ false));
+        verify(mBouncer, never()).setExpansion(anyFloat());
+    }
+
+    @Test
     public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
         when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 1a8ffc1..4c8599d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,6 +16,11 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.RUNNING_CHIP_ANIM;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -25,6 +30,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.animation.Animator;
 import android.app.Fragment;
 import android.app.StatusBarManager;
 import android.content.Context;
@@ -130,7 +136,8 @@
     }
 
     @Test
-    public void testDisableSystemInfo() {
+    public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
+        when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -143,6 +150,98 @@
     }
 
     @Test
+    public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
+        // GIVEN the status bar hides the system info via disable flags, while there is no event
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+
+        // WHEN the disable flags are cleared during a system event animation
+        when(mAnimationScheduler.getAnimationState()).thenReturn(RUNNING_CHIP_ANIM);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        // THEN the view is made visible again, but still low alpha
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+        assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+
+        // WHEN the system event animation finishes
+        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+        Animator anim = fragment.onSystemEventAnimationFinish(false);
+        anim.start();
+        processAllMessages();
+        anim.end();
+
+        // THEN the system info is full alpha
+        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+    }
+
+    @Test
+    public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
+        // GIVEN the status bar hides the system info via disable flags, while there is no event
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+
+        // WHEN the system event animation finishes
+        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+        Animator anim = fragment.onSystemEventAnimationFinish(false);
+        anim.start();
+        processAllMessages();
+        anim.end();
+
+        // THEN the system info is at full alpha, but still INVISIBLE (since the disable flag is
+        // still set)
+        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+    }
+
+
+    @Test
+    public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
+        // GIVEN the status bar is not disabled
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+        // WHEN the system event animation begins
+        Animator anim = fragment.onSystemEventAnimationBegin();
+        anim.start();
+        processAllMessages();
+        anim.end();
+
+        // THEN the system info is visible but alpha 0
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+        assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+    }
+
+    @Test
+    public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
+        // GIVEN the status bar is not disabled
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+        // WHEN the system event animation begins
+        Animator anim = fragment.onSystemEventAnimationBegin();
+        anim.start();
+        processAllMessages();
+        anim.end();
+
+        // THEN the system info is visible but alpha 0
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+        assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+
+        // WHEN the system event animation finishes
+        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+        anim = fragment.onSystemEventAnimationFinish(false);
+        anim.start();
+        processAllMessages();
+        anim.end();
+
+        // THEN the syste info is full alpha and VISIBLE
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+    }
+
+    @Test
     public void testDisableNotifications() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
new file mode 100644
index 0000000..515a7c9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import android.net.NetworkCapabilities
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(InternalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConnectivityInfoProcessorTest : SysuiTestCase() {
+
+    private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()
+
+    @Before
+    fun setUp() {
+        whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
+    }
+
+    @Test
+    fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
+        // GIVEN a processor hooked up to a collector
+        val scope = CoroutineScope(Dispatchers.Unconfined)
+        val collector = FakeConnectivityInfoCollector()
+        val processor = ConnectivityInfoProcessor(
+                collector,
+                context,
+                scope,
+                statusBarPipelineFlags,
+        )
+
+        var mostRecentValue: ProcessedConnectivityInfo? = null
+        val job = launch(start = CoroutineStart.UNDISPATCHED) {
+            processor.processedInfoFlow.collect {
+                mostRecentValue = it
+            }
+        }
+
+        // WHEN the collector emits a value
+        val networkCapabilityInfo = mapOf(
+                10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
+        )
+        collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
+        // Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
+        // this test. So, our test needs to yield to let the job run.
+        // Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
+        yield()
+
+        // THEN the processor receives it
+        assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)
+
+        job.cancel()
+        scope.cancel()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
new file mode 100644
index 0000000..710e5f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A test-friendly implementation of [ConnectivityInfoCollector] that just emits whatever value it
+ * receives in [emitValue].
+ */
+class FakeConnectivityInfoCollector : ConnectivityInfoCollector {
+    private val _rawConnectivityInfoFlow = MutableStateFlow(RawConnectivityInfo())
+    override val rawConnectivityInfoFlow = _rawConnectivityInfoFlow.asStateFlow()
+
+    suspend fun emitValue(value: RawConnectivityInfo) {
+        _rawConnectivityInfoFlow.emit(value)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 35e9060..b255188 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -56,6 +56,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -208,6 +209,10 @@
     }
 
     @VisibleForTesting
+    VirtualDeviceManagerInternal getLocalServiceInstance() {
+        return mLocalService;
+    }
+
     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
             VirtualDeviceImpl.PendingTrampolineCallback {
 
@@ -308,10 +313,11 @@
             final long tokenTwo = Binder.clearCallingIdentity();
             try {
                 virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
-                return displayId;
             } finally {
                 Binder.restoreCallingIdentity(tokenTwo);
             }
+            mLocalService.onVirtualDisplayCreated(displayId);
+            return displayId;
         }
 
         @Nullable
@@ -378,6 +384,10 @@
     }
 
     private final class LocalService extends VirtualDeviceManagerInternal {
+        @GuardedBy("mVirtualDeviceManagerLock")
+        private final ArrayList<VirtualDeviceManagerInternal.VirtualDisplayListener>
+                mVirtualDisplayListeners = new ArrayList<>();
+
         @Override
         public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
             synchronized (mVirtualDeviceManagerLock) {
@@ -386,10 +396,30 @@
         }
 
         @Override
+        public void onVirtualDisplayCreated(int displayId) {
+            final VirtualDisplayListener[] listeners;
+            synchronized (mVirtualDeviceManagerLock) {
+                listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
+            }
+            mHandler.post(() -> {
+                for (VirtualDisplayListener listener : listeners) {
+                    listener.onVirtualDisplayCreated(displayId);
+                }
+            });
+        }
+
+        @Override
         public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
+            final VirtualDisplayListener[] listeners;
             synchronized (mVirtualDeviceManagerLock) {
                 ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
+                listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
             }
+            mHandler.post(() -> {
+                for (VirtualDisplayListener listener : listeners) {
+                    listener.onVirtualDisplayRemoved(displayId);
+                }
+            });
         }
 
         @Override
@@ -435,6 +465,22 @@
             }
             return false;
         }
+
+        @Override
+        public void registerVirtualDisplayListener(
+                @NonNull VirtualDisplayListener listener) {
+            synchronized (mVirtualDeviceManagerLock) {
+                mVirtualDisplayListeners.add(listener);
+            }
+        }
+
+        @Override
+        public void unregisterVirtualDisplayListener(
+                @NonNull VirtualDisplayListener listener) {
+            synchronized (mVirtualDeviceManagerLock) {
+                mVirtualDisplayListeners.remove(listener);
+            }
+        }
     }
 
     private static final class PendingTrampolineMap {
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 98ad81d..98129ed 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -23,6 +23,8 @@
 
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.IActivityTaskManager;
@@ -41,6 +43,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Helper class to asynchronously fetch the assist data and screenshot from the current running
@@ -141,50 +144,35 @@
     }
 
     /**
-     * Request that autofill data be loaded asynchronously. The resulting data will be provided
-     * through the {@link AssistDataRequesterCallbacks}.
-     *
-     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
-     * String, boolean)}.
-     */
-    public void requestAutofillData(List<IBinder> activityTokens, int callingUid,
-            String callingPackage) {
-        requestData(activityTokens, true /* requestAutofillData */,
-                true /* fetchData */, false /* fetchScreenshot */,
-                true /* fetchStructure */, true /* allowFetchData */,
-                false /* allowFetchScreenshot */, false /* ignoreTopActivityCheck */, callingUid,
-                callingPackage);
-    }
-
-    /**
      * Request that assist data be loaded asynchronously. The resulting data will be provided
      * through the {@link AssistDataRequesterCallbacks}.
      *
-     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
-     * String, boolean)}.
+     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+     * int, String, String)}.
      */
-    public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+    public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
             final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
-            int callingUid, String callingPackage) {
+            int callingUid, @NonNull String callingPackage,
+            @Nullable String callingAttributionTag) {
         requestAssistData(activityTokens, fetchData, fetchScreenshot, true /* fetchStructure */,
                 allowFetchData, allowFetchScreenshot, false /* ignoreTopActivityCheck */,
-                callingUid, callingPackage);
+                callingUid, callingPackage, callingAttributionTag);
     }
 
     /**
      * Request that assist data be loaded asynchronously. The resulting data will be provided
      * through the {@link AssistDataRequesterCallbacks}.
      *
-     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
-     * String, boolean)}.
+     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+     * int, String, String)}.
      */
-    public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+    public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
             final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
             boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
-            String callingPackage) {
+            @NonNull String callingPackage, @Nullable String callingAttributionTag) {
         requestData(activityTokens, false /* requestAutofillData */, fetchData, fetchScreenshot,
                 fetchStructure, allowFetchData, allowFetchScreenshot, ignoreTopActivityCheck,
-                callingUid, callingPackage);
+                callingUid, callingPackage, callingAttributionTag);
     }
 
     /**
@@ -208,14 +196,22 @@
      *     requester is allowed to fetch the assist screenshot
      * @param ignoreTopActivityCheck overrides the check for whether the activity is in focus when
      *     making the request. Used when passing an activity from Recents.
+     * @param callingUid the uid of the real caller
+     * @param callingPackage the package name of the real caller
+     * @param callingAttributionTag The {@link Context#createAttributionContext attribution tag}
+     *     of the calling context or {@code null} for default attribution
      */
-    private void requestData(List<IBinder> activityTokens, final boolean requestAutofillData,
-            final boolean fetchData, final boolean fetchScreenshot, final boolean fetchStructure,
-            boolean allowFetchData, boolean allowFetchScreenshot, boolean ignoreTopActivityCheck,
-            int callingUid, String callingPackage) {
+    private void requestData(@NonNull List<IBinder> activityTokens,
+            final boolean requestAutofillData, final boolean fetchData,
+            final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
+            boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
+            @NonNull String callingPackage, @Nullable String callingAttributionTag) {
         // TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity,
         //                   then no assist data is requested for any of the other activities
 
+        Objects.requireNonNull(activityTokens);
+        Objects.requireNonNull(callingPackage);
+
         // Early exit if there are no activity to fetch for
         if (activityTokens.isEmpty()) {
             // No activities, just dispatch request-complete
@@ -241,8 +237,9 @@
         mAssistScreenshot.clear();
 
         if (fetchData) {
-            if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
-                    == MODE_ALLOWED && allowFetchData) {
+            if (mAppOpsManager.noteOpNoThrow(mRequestStructureAppOps, callingUid,
+                    callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+                    && allowFetchData) {
                 final int numActivities = activityTokens.size();
                 for (int i = 0; i < numActivities; i++) {
                     IBinder topActivity = activityTokens.get(i);
@@ -291,8 +288,9 @@
         }
 
         if (fetchScreenshot) {
-            if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
-                    == MODE_ALLOWED && allowFetchScreenshot) {
+            if (mAppOpsManager.noteOpNoThrow(mRequestScreenshotAppOps, callingUid,
+                    callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+                    && allowFetchScreenshot) {
                 try {
                     MetricsLogger.count(mContext, "assist_with_screen", 1);
                     mPendingScreenshotCount++;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e4224ed..0c0ae7d 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1872,15 +1872,21 @@
     /** Called on handler thread */
     @VisibleForTesting
     void dispatchUserSwitchComplete(@UserIdInt int userId) {
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("dispatchUserSwitchComplete-" + userId);
         mInjector.getWindowManager().setSwitchingUser(false);
         final int observerCount = mUserSwitchObservers.beginBroadcast();
         for (int i = 0; i < observerCount; i++) {
             try {
+                t.traceBegin("onUserSwitchComplete-" + userId + " #" + i + " "
+                        + mUserSwitchObservers.getBroadcastCookie(i));
                 mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+                t.traceEnd();
             } catch (RemoteException e) {
             }
         }
         mUserSwitchObservers.finishBroadcast();
+        t.traceEnd();
     }
 
     private void dispatchLockedBootComplete(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/app/GameTaskInfoProvider.java b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
index f078d98..25a4f37 100644
--- a/services/core/java/com/android/server/app/GameTaskInfoProvider.java
+++ b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.app;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -93,7 +95,8 @@
             runningTaskInfos = mActivityTaskManager.getTasks(
                     /* maxNum= */ Integer.MAX_VALUE,
                     /* filterOnlyVisibleRecents= */ false,
-                    /* keepIntentExtra= */ false);
+                    /* keepIntentExtra= */ false,
+                    INVALID_DISPLAY);
         } catch (RemoteException ex) {
             Slog.w(TAG, "Failed to fetch running tasks");
             return null;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f0a7df4..5a20db3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -369,6 +369,7 @@
     private static final int MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR = 47;
     private static final int MSG_ROTATION_UPDATE = 48;
     private static final int MSG_FOLD_UPDATE = 49;
+    private static final int MSG_RESET_SPATIALIZER = 50;
 
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -376,6 +377,7 @@
     private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
     private static final int MSG_INIT_STREAMS_VOLUMES = 101;
     private static final int MSG_INIT_SPATIALIZER = 102;
+
     // end of messages handled under wakelock
 
     // retry delay in case of failure to indicate system ready to AudioFlinger
@@ -3942,14 +3944,12 @@
             case AudioSystem.MODE_IN_COMMUNICATION:
             case AudioSystem.MODE_IN_CALL:
             case AudioSystem.MODE_NORMAL:
+            case AudioSystem.MODE_CALL_SCREENING:
+            case AudioSystem.MODE_CALL_REDIRECT:
+            case AudioSystem.MODE_COMMUNICATION_REDIRECT:
                 break;
-            case AudioSystem.MODE_RINGTONE:
-                // not changing anything for ringtone
-                return;
-            case AudioSystem.MODE_CURRENT:
-            case AudioSystem.MODE_INVALID:
             default:
-                // don't know what to do in this case, better bail
+                // no-op is enough for all other values
                 return;
         }
 
@@ -3977,14 +3977,12 @@
             case AudioSystem.MODE_IN_COMMUNICATION:
             case AudioSystem.MODE_IN_CALL:
             case AudioSystem.MODE_NORMAL:
+            case AudioSystem.MODE_CALL_SCREENING:
+            case AudioSystem.MODE_CALL_REDIRECT:
+            case AudioSystem.MODE_COMMUNICATION_REDIRECT:
                 break;
-            case AudioSystem.MODE_RINGTONE:
-                // not changing anything for ringtone
-                return;
-            case AudioSystem.MODE_CURRENT:
-            case AudioSystem.MODE_INVALID:
             default:
-                // don't know what to do in this case, better bail
+                // no-op is enough for all other values
                 return;
         }
 
@@ -8309,6 +8307,10 @@
                     onPersistSpatialAudioDeviceSettings();
                     break;
 
+                case MSG_RESET_SPATIALIZER:
+                    mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
+                    break;
+
                 case MSG_CHECK_MUSIC_ACTIVE:
                     onCheckMusicActive((String) msg.obj);
                     break;
@@ -9287,6 +9289,16 @@
                 /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
     }
 
+    /**
+     * post a message to schedule a reset of the spatializer state
+     */
+    void postResetSpatializer() {
+        sendMsg(mAudioHandler,
+                MSG_RESET_SPATIALIZER,
+                SENDMSG_REPLACE,
+                /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
+    }
+
     void onInitSpatializer() {
         final String settings = mSettings.getSecureStringForUser(mContentResolver,
                 Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index d9037fe..66a4527 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -53,6 +53,8 @@
 
     private static final String TAG = "AS.BtHelper";
 
+    private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
     private final @NonNull AudioDeviceBroker mDeviceBroker;
 
     BtHelper(@NonNull AudioDeviceBroker broker) {
@@ -911,6 +913,8 @@
                 return "ENCODING_APTX_HD";
             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
                 return "ENCODING_LDAC";
+            case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+                return "ENCODING_OPUS";
             default:
                 return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
         }
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1def72b..e27fb11 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -389,10 +389,10 @@
             try {
                 mSpat.setLevel(level);
             } catch (RemoteException e) {
-                Log.e(TAG, "Can't set spatializer level", e);
-                mState = STATE_NOT_SUPPORTED;
-                mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
-                enabled = false;
+                Log.e(TAG, "onRoutingUpdated() Can't set spatializer level", e);
+                // try to recover by resetting the native spatializer state
+                postReset();
+                return;
             }
         }
 
@@ -404,6 +404,10 @@
         }
     }
 
+    private void postReset() {
+        mAudioService.postResetSpatializer();
+    }
+
     //------------------------------------------------------
     // spatializer callback from native
     private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
@@ -751,33 +755,29 @@
                 if (enabled) {
                     throw (new IllegalStateException("Can't enable when uninitialized"));
                 }
-                return;
+                break;
             case STATE_NOT_SUPPORTED:
                 if (enabled) {
                     Log.e(TAG, "Can't enable when unsupported");
                 }
-                return;
+                break;
             case STATE_DISABLED_UNAVAILABLE:
             case STATE_DISABLED_AVAILABLE:
                 if (enabled) {
                     createSpat();
                     onRoutingUpdated();
-                    break;
-                } else {
-                    // already in disabled state
-                    return;
-                }
+                    // onRoutingUpdated() can update the "enabled" state based on context
+                    // and will call setDispatchFeatureEnabledState().
+                } // else { nothing to do as already disabled }
+                break;
             case STATE_ENABLED_UNAVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (!enabled) {
                     releaseSpat();
-                    break;
-                } else {
-                    // already in enabled state
-                    return;
-                }
+                    setDispatchFeatureEnabledState(false, "setSpatializerEnabledInt");
+                } // else { nothing to do as already enabled }
+                break;
         }
-        setDispatchFeatureEnabledState(enabled, "setSpatializerEnabledInt");
     }
 
     synchronized int getCapableImmersiveAudioLevel() {
@@ -1161,8 +1161,11 @@
             case STATE_DISABLED_AVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (mSpat == null) {
-                    throw (new IllegalStateException(
-                            "null Spatializer when calling " + funcName));
+                    // try to recover by resetting the native spatializer state
+                    Log.e(TAG, "checkSpatForHeadTracking(): "
+                            + "native spatializer should not be null in state: " + mState);
+                    postReset();
+                    return false;
                 }
                 break;
         }
@@ -1252,8 +1255,8 @@
             case STATE_DISABLED_AVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (mSpat == null) {
-                    throw (new IllegalStateException(
-                            "null Spatializer for setParameter for key:" + key));
+                    Log.e(TAG, "setParameter(" + key + "): null spatializer in state: " + mState);
+                    return;
                 }
                 break;
         }
@@ -1276,8 +1279,8 @@
             case STATE_DISABLED_AVAILABLE:
             case STATE_ENABLED_AVAILABLE:
                 if (mSpat == null) {
-                    throw (new IllegalStateException(
-                            "null Spatializer for getParameter for key:" + key));
+                    Log.e(TAG, "getParameter(" + key + "): null spatializer in state: " + mState);
+                    return;
                 }
                 break;
         }
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 23f0ffb..351a1e9 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -39,7 +39,6 @@
 
 public class BroadcastRadioService extends SystemService {
     private static final String TAG = "BcRadioSrv";
-    private static final boolean DEBUG = false;
 
     private final ServiceImpl mServiceImpl = new ServiceImpl();
 
@@ -74,6 +73,7 @@
 
         @Override
         public List<RadioManager.ModuleProperties> listModules() {
+            Slog.v(TAG, "Listing HIDL modules");
             enforcePolicyAccess();
             List<RadioManager.ModuleProperties> modules = new ArrayList<>();
             modules.addAll(mV1Modules);
@@ -84,7 +84,7 @@
         @Override
         public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
                 boolean withAudio, ITunerCallback callback) throws RemoteException {
-            if (DEBUG) Slog.i(TAG, "Opening module " + moduleId);
+            Slog.v(TAG, "Opening module " + moduleId);
             enforcePolicyAccess();
             if (callback == null) {
                 throw new IllegalArgumentException("Callback must not be empty");
@@ -101,16 +101,14 @@
         @Override
         public ICloseHandle addAnnouncementListener(int[] enabledTypes,
                 IAnnouncementListener listener) {
-            if (DEBUG) {
-                Slog.i(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
-            }
+            Slog.v(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
             Objects.requireNonNull(enabledTypes);
             Objects.requireNonNull(listener);
             enforcePolicyAccess();
 
             synchronized (mLock) {
                 if (!mHal2.hasAnyModules()) {
-                    Slog.i(TAG, "There are no HAL 2.x modules registered");
+                    Slog.i(TAG, "There are no HAL 2.0 modules registered");
                     return new AnnouncementAggregator(listener, mLock);
                 }
 
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 5c07f76..534e828 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -132,6 +132,7 @@
     }
 
     public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
+        Slog.v(TAG, "List HIDL 2.0 modules");
         synchronized (mLock) {
             return mModules.values().stream().map(module -> module.mProperties)
                     .collect(Collectors.toList());
@@ -152,10 +153,11 @@
 
     public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
         boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
+        Slog.v(TAG, "Open HIDL 2.0 session");
         Objects.requireNonNull(callback);
 
         if (!withAudio) {
-            throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.x");
+            throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.0");
         }
 
         RadioModule module = null;
@@ -175,6 +177,7 @@
 
     public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
             @NonNull IAnnouncementListener listener) {
+        Slog.v(TAG, "Add announcementListener");
         AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock);
         boolean anySupported = false;
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index ef7f4c9..aeaa678 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -142,8 +142,12 @@
     public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName,
             Object lock) {
         try {
+            Slog.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
             IBroadcastRadio service = IBroadcastRadio.getService(fqName);
-            if (service == null) return null;
+            if (service == null) {
+                Slog.w(TAG, "No service found for fqName " + fqName);
+                return null;
+            }
 
             Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>();
             service.getAmFmRegionConfig(false, (result, config) -> {
@@ -160,7 +164,7 @@
 
             return new RadioModule(service, prop, lock);
         } catch (RemoteException ex) {
-            Slog.e(TAG, "failed to load module " + fqName, ex);
+            Slog.e(TAG, "Failed to load module " + fqName, ex);
             return null;
         }
     }
@@ -171,6 +175,7 @@
 
     public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
             throws RemoteException {
+        Slog.i(TAG, "Open TunerSession");
         synchronized (mLock) {
             if (mHalTunerSession == null) {
                 Mutable<ITunerSession> hwSession = new Mutable<>();
@@ -201,6 +206,7 @@
         // Copy the contents of mAidlTunerSessions into a local array because TunerSession.close()
         // must be called without mAidlTunerSessions locked because it can call
         // onTunerSessionClosed().
+        Slog.i(TAG, "Close TunerSessions");
         TunerSession[] tunerSessions;
         synchronized (mLock) {
             tunerSessions = new TunerSession[mAidlTunerSessions.size()];
@@ -313,7 +319,7 @@
         }
         onTunerSessionProgramListFilterChanged(null);
         if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
-            Slog.v(TAG, "closing HAL tuner session");
+            Slog.i(TAG, "Closing HAL tuner session");
             try {
                 mHalTunerSession.close();
             } catch (RemoteException ex) {
@@ -365,6 +371,7 @@
 
     public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
             @NonNull android.hardware.radio.IAnnouncementListener listener) throws RemoteException {
+        Slog.i(TAG, "Add AnnouncementListener");
         ArrayList<Byte> enabledList = new ArrayList<>();
         for (int type : enabledTypes) {
             enabledList.add((byte)type);
@@ -401,6 +408,7 @@
     }
 
     Bitmap getImage(int id) {
+        Slog.i(TAG, "Get image for id " + id);
         if (id == 0) throw new IllegalArgumentException("Image ID is missing");
 
         byte[] rawImage;
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index d476fd6..c13216b 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -27,6 +27,7 @@
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.os.RemoteException;
+import android.util.Log;
 import android.util.MutableBoolean;
 import android.util.MutableInt;
 import android.util.Slog;
@@ -61,8 +62,13 @@
         mLock = Objects.requireNonNull(lock);
     }
 
+    private boolean isDebugEnabled() {
+        return Log.isLoggable(TAG, Log.DEBUG);
+    }
+
     @Override
     public void close() {
+        if (isDebugEnabled()) Slog.d(TAG, "Close");
         close(null);
     }
 
@@ -74,6 +80,7 @@
      * @param error Optional error to send to client before session is closed.
      */
     public void close(@Nullable Integer error) {
+        if (isDebugEnabled()) Slog.d(TAG, "Close on error " + error);
         synchronized (mLock) {
             if (mIsClosed) return;
             if (error != null) {
@@ -104,7 +111,7 @@
         synchronized (mLock) {
             checkNotClosedLocked();
             mDummyConfig = Objects.requireNonNull(config);
-            Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x");
+            Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
             mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
         }
     }
@@ -137,6 +144,10 @@
 
     @Override
     public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+        if (isDebugEnabled()) {
+            Slog.d(TAG, "Step with directionDown " + directionDown
+                    + " skipSubChannel " + skipSubChannel);
+        }
         synchronized (mLock) {
             checkNotClosedLocked();
             int halResult = mHwSession.step(!directionDown);
@@ -146,6 +157,10 @@
 
     @Override
     public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+        if (isDebugEnabled()) {
+            Slog.d(TAG, "Scan with directionDown " + directionDown
+                    + " skipSubChannel " + skipSubChannel);
+        }
         synchronized (mLock) {
             checkNotClosedLocked();
             int halResult = mHwSession.scan(!directionDown, skipSubChannel);
@@ -155,6 +170,7 @@
 
     @Override
     public void tune(ProgramSelector selector) throws RemoteException {
+        if (isDebugEnabled()) Slog.d(TAG, "Tune with selector " + selector);
         synchronized (mLock) {
             checkNotClosedLocked();
             int halResult = mHwSession.tune(Convert.programSelectorToHal(selector));
@@ -164,6 +180,7 @@
 
     @Override
     public void cancel() {
+        Slog.i(TAG, "Cancel");
         synchronized (mLock) {
             checkNotClosedLocked();
             Utils.maybeRethrow(mHwSession::cancel);
@@ -172,23 +189,25 @@
 
     @Override
     public void cancelAnnouncement() {
-        Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in 2.x");
+        Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in HAL 2.0");
     }
 
     @Override
     public Bitmap getImage(int id) {
+        if (isDebugEnabled()) Slog.d(TAG, "Get image for " + id);
         return mModule.getImage(id);
     }
 
     @Override
     public boolean startBackgroundScan() {
-        Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x");
+        Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.0");
         mModule.fanoutAidlCallback(cb -> cb.onBackgroundScanComplete());
         return true;
     }
 
     @Override
     public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
+        if (isDebugEnabled()) Slog.d(TAG, "start programList updates " + filter);
         // If the AIDL client provides a null filter, it wants all updates, so use the most broad
         // filter.
         if (filter == null) {
@@ -247,6 +266,7 @@
 
     @Override
     public void stopProgramListUpdates() throws RemoteException {
+        if (isDebugEnabled()) Slog.d(TAG, "Stop programList updates");
         synchronized (mLock) {
             checkNotClosedLocked();
             mProgramInfoCache = null;
@@ -270,7 +290,7 @@
 
     @Override
     public boolean isConfigFlagSet(int flag) {
-        Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag));
+        if (isDebugEnabled()) Slog.d(TAG, "Is ConfigFlagSet for " + ConfigFlag.toString(flag));
         synchronized (mLock) {
             checkNotClosedLocked();
 
@@ -292,7 +312,9 @@
 
     @Override
     public void setConfigFlag(int flag, boolean value) throws RemoteException {
-        Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+        if (isDebugEnabled()) {
+            Slog.d(TAG, "Set ConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+        }
         synchronized (mLock) {
             checkNotClosedLocked();
             int halResult = mHwSession.setConfigFlag(flag, value);
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index f2b4d42..230bfc5 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -16,6 +16,7 @@
 
 package com.android.server.companion.virtual;
 
+import android.annotation.NonNull;
 import android.companion.virtual.IVirtualDevice;
 
 /**
@@ -24,16 +25,41 @@
  */
 public abstract class VirtualDeviceManagerInternal {
 
+    /** Interface to listen to the creation and destruction of virtual displays. */
+    public interface VirtualDisplayListener {
+        /** Notifies that a virtual display was created. */
+        void onVirtualDisplayCreated(int displayId);
+
+        /** Notifies that a virtual display was removed. */
+        void onVirtualDisplayRemoved(int displayId);
+    }
+
+    /** Register a listener for the creation and destruction of virtual displays. */
+    public abstract void registerVirtualDisplayListener(
+            @NonNull VirtualDisplayListener listener);
+
+    /** Unregister a listener for the creation and  destruction of virtual displays. */
+    public abstract void unregisterVirtualDisplayListener(
+            @NonNull VirtualDisplayListener listener);
+
+
     /**
      * Validate the virtual device.
      */
     public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
 
     /**
-     * Notify a virtual display is removed.
+     * Notifies that a virtual display is created.
+     *
+     * @param displayId The display id of the created virtual display.
+     */
+    public abstract void onVirtualDisplayCreated(int displayId);
+
+    /**
+     * Notifies that a virtual display is removed.
      *
      * @param virtualDevice The virtual device where the virtual display located.
-     * @param displayId The display id of the removed virtual display.
+     * @param displayId     The display id of the removed virtual display.
      */
     public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
 
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 114690a..a817cea 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -345,7 +345,9 @@
             brightnessEvent.setFlags(brightnessEvent.getFlags()
                     | (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
                     | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
-                        ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
+                        ? BrightnessEvent.FLAG_DOZE_SCALE : 0)
+                    | (mCurrentBrightnessMapper.isForIdleMode()
+                        ? BrightnessEvent.FLAG_IDLE_CURVE : 0));
         }
 
         if (!mAmbientLuxValid) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b343480..2dd3864 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2583,8 +2583,8 @@
         final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
                 display, mSyncRoot);
         final DisplayPowerController displayPowerController = new DisplayPowerController(
-                mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
-                mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+                mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+                mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
                 () -> handleBrightnessChange(display));
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c01a228..6150b49 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -48,6 +48,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.MutableFloat;
@@ -58,6 +59,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.logging.MetricsLogger;
@@ -97,7 +99,7 @@
  * blocker as long as the display is not ready.  So most of the work done here
  * does not need to worry about holding a suspend blocker unless it happens
  * independently of the display ready signal.
-   *
+ *
  * For debugging, you can make the color fade and brightness animations run
  * slower by changing the "animator duration scale" option in Development Settings.
  */
@@ -256,6 +258,9 @@
     // to reach the final state.
     private final boolean mBrightnessBucketsInDozeConfig;
 
+    private final Clock mClock;
+    private final Injector mInjector;
+
     //  Maximum time a ramp animation can take.
     private long mBrightnessRampIncreaseMaxTimeMillis;
     private long mBrightnessRampDecreaseMaxTimeMillis;
@@ -495,20 +500,22 @@
     /**
      * Creates the display power controller.
      */
-    public DisplayPowerController(Context context,
+    DisplayPowerController(Context context, Injector injector,
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
             Runnable onBrightnessChangeRunnable) {
+
+        mInjector = injector != null ? injector : new Injector();
+        mClock = mInjector.getClock();
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
-        final String displayIdStr = "[" + mDisplayId + "]";
-        TAG = "DisplayPowerController" + displayIdStr;
-        mSuspendBlockerIdUnfinishedBusiness = displayIdStr + "unfinished business";
-        mSuspendBlockerIdOnStateChanged = displayIdStr + "on state changed";
-        mSuspendBlockerIdProxPositive = displayIdStr + "prox positive";
-        mSuspendBlockerIdProxNegative = displayIdStr + "prox negative";
-        mSuspendBlockerIdProxDebounce = displayIdStr + "prox debounce";
+        TAG = "DisplayPowerController[" + mDisplayId + "]";
+        mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
+        mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
+        mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
+        mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
+        mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
 
         mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
@@ -681,7 +688,8 @@
 
     /**
      * Get the {@link BrightnessChangeEvent}s for the specified user.
-     * @param userId userId to fetch data for
+     *
+     * @param userId         userId to fetch data for
      * @param includePackage if false will null out the package name in events
      */
     @Nullable
@@ -723,10 +731,11 @@
      * The controller makes a copy of the provided object and then
      * begins adjusting the power state to match what was requested.
      *
-     * @param request The requested power state.
+     * @param request                  The requested power state.
      * @param waitForNegativeProximity If true, issues a request to wait for
-     * negative proximity before turning the screen back on, assuming the screen
-     * was turned off by the proximity sensor.
+     *                                 negative proximity before turning the screen back on,
+     *                                 assuming the screen
+     *                                 was turned off by the proximity sensor.
      * @return True if display is ready, false if there are important changes that must
      * be made asynchronously (such as turning the screen on), in which case the caller
      * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
@@ -834,7 +843,7 @@
         synchronized (mLock) {
             mStopped = true;
             Message msg = mHandler.obtainMessage(MSG_STOP);
-            mHandler.sendMessage(msg);
+            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
 
             if (mDisplayWhiteBalanceController != null) {
                 mDisplayWhiteBalanceController.setEnabled(false);
@@ -888,12 +897,12 @@
         if (!mStopped && !mPendingUpdatePowerStateLocked) {
             mPendingUpdatePowerStateLocked = true;
             Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
-            mHandler.sendMessage(msg);
+            mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
         }
     }
 
     private void initialize(int displayState) {
-        mPowerState = new DisplayPowerState(mBlanker,
+        mPowerState = mInjector.getDisplayPowerState(mBlanker,
                 mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
 
         if (mColorFadeEnabled) {
@@ -908,7 +917,7 @@
             mColorFadeOffAnimator.addListener(mAnimatorListener);
         }
 
-        mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState,
+        mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
                 DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
                 DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
         mScreenBrightnessRampAnimator.setAnimationTimeLimits(
@@ -1077,13 +1086,16 @@
         @Override
         public void onAnimationStart(Animator animation) {
         }
+
         @Override
         public void onAnimationEnd(Animator animation) {
             sendUpdatePowerState();
         }
+
         @Override
         public void onAnimationRepeat(Animator animation) {
         }
+
         @Override
         public void onAnimationCancel(Animator animation) {
         }
@@ -1124,8 +1136,8 @@
         mOnProximityNegativeMessages = 0;
 
         final float brightness = mPowerState != null
-            ? mPowerState.getScreenBrightness()
-            : PowerManager.BRIGHTNESS_MIN;
+                ? mPowerState.getScreenBrightness()
+                : PowerManager.BRIGHTNESS_MIN;
         reportStats(brightness);
 
         if (mPowerState != null) {
@@ -1203,7 +1215,7 @@
                 state = Display.STATE_ON;
                 break;
         }
-        assert(state != Display.STATE_UNKNOWN);
+        assert (state != Display.STATE_UNKNOWN);
 
         // Apply the proximity sensor.
         if (mProximitySensor != null) {
@@ -1290,16 +1302,16 @@
         final boolean autoBrightnessEnabledInDoze =
                 mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
         final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
-                    && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
-                    && Float.isNaN(brightnessState)
-                    && mAutomaticBrightnessController != null;
+                && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+                && Float.isNaN(brightnessState)
+                && mAutomaticBrightnessController != null;
         final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
-                    && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+                && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
         final int autoBrightnessState = autoBrightnessEnabled
                 ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
                 : autoBrightnessDisabledDueToDisplayOff
-                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
-                : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+                        ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+                        : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
         final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
 
@@ -1589,9 +1601,9 @@
             // AND if we are not in idle screen brightness mode.
             if (!brightnessIsTemporary
                     && (mAutomaticBrightnessController != null
-                            && !mAutomaticBrightnessController.isInIdleMode())) {
+                    && !mAutomaticBrightnessController.isInIdleMode())) {
                 if (userInitiatedChange && (mAutomaticBrightnessController == null
-                            || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+                        || !mAutomaticBrightnessController.hasValidAmbientLux())) {
                     // If we don't have a valid lux reading we can't report a valid
                     // slider event so notify as if the system changed the brightness.
                     userInitiatedChange = false;
@@ -1782,26 +1794,26 @@
             boolean changed = false;
 
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
-                        brightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+                            brightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
-                        adjustedBrightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+                            adjustedBrightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
-                        minBrightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+                            minBrightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
-                        maxBrightness);
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+                            maxBrightness);
             changed |=
-                mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
-                        mHbmController.getHighBrightnessMode());
+                    mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+                            mHbmController.getHighBrightnessMode());
             changed |=
-                mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
-                        mHbmController.getTransitionPoint());
+                    mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+                            mHbmController.getTransitionPoint());
             changed |=
-                mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
-                        mBrightnessThrottler.getBrightnessMaxReason());
+                    mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+                            mBrightnessThrottler.getBrightnessMaxReason());
 
             return changed;
         }
@@ -1943,7 +1955,7 @@
         }
         if (!isOff
                 && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
-                        || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
+                || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
             setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
             if (mPowerState.getColorFadeLevel() == 0.0f) {
                 blockScreenOn();
@@ -2071,7 +2083,7 @@
                 } else if (mPowerState.prepareColorFade(mContext,
                         mColorFadeFadesConfig ?
                                 ColorFade.MODE_FADE :
-                                        ColorFade.MODE_WARM_UP)) {
+                                ColorFade.MODE_WARM_UP)) {
                     mColorFadeOnAnimator.start();
                 } else {
                     mColorFadeOnAnimator.end();
@@ -2173,8 +2185,8 @@
                 mPowerState.dismissColorFadeResources();
             } else if (performScreenOffTransition
                     && mPowerState.prepareColorFade(mContext,
-                            mColorFadeFadesConfig ?
-                                    ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
+                    mColorFadeFadesConfig
+                            ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
                     && mPowerState.getScreenState() != Display.STATE_OFF) {
                 // Perform the screen off animation.
                 mColorFadeOffAnimator.start();
@@ -2245,7 +2257,7 @@
         if (mProximitySensorEnabled
                 && mPendingProximity != PROXIMITY_UNKNOWN
                 && mPendingProximityDebounceTime >= 0) {
-            final long now = SystemClock.uptimeMillis();
+            final long now = mClock.uptimeMillis();
             if (mPendingProximityDebounceTime <= now) {
                 if (mProximity != mPendingProximity) {
                     // if the status of the sensor changed, stop ignoring.
@@ -2523,7 +2535,7 @@
             pw.println("  mCachedBrightnessInfo.hbmTransitionPoint=" +
                     mCachedBrightnessInfo.hbmTransitionPoint.value);
             pw.println("  mCachedBrightnessInfo.brightnessMaxReason =" +
-                    mCachedBrightnessInfo.brightnessMaxReason .value);
+                    mCachedBrightnessInfo.brightnessMaxReason.value);
         }
         pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
         pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2706,7 +2718,7 @@
         }
 
         float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
-        synchronized(mCachedBrightnessInfo) {
+        synchronized (mCachedBrightnessInfo) {
             if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
                 return;
             }
@@ -2740,8 +2752,6 @@
         }
     }
 
-
-
     private final class DisplayControllerHandler extends Handler {
         public DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -2806,7 +2816,7 @@
                     break;
 
                 case MSG_BRIGHTNESS_RAMP_DONE:
-                    if (mPowerState != null)  {
+                    if (mPowerState != null) {
                         final float brightness = mPowerState.getScreenBrightness();
                         reportStats(brightness);
                     }
@@ -2823,7 +2833,7 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             if (mProximitySensorEnabled) {
-                final long time = SystemClock.uptimeMillis();
+                final long time = mClock.uptimeMillis();
                 final float distance = event.values[0];
                 boolean positive = distance >= 0.0f && distance < mProximityThreshold;
                 handleProximitySensorEvent(time, positive);
@@ -2892,19 +2902,68 @@
         }
     }
 
+    @VisibleForTesting
+    String getSuspendBlockerUnfinishedBusinessId(int displayId) {
+        return "[" + displayId + "]unfinished business";
+    }
+
+    String getSuspendBlockerOnStateChangedId(int displayId) {
+        return "[" + displayId + "]on state changed";
+    }
+
+    String getSuspendBlockerProxPositiveId(int displayId) {
+        return "[" + displayId + "]prox positive";
+    }
+
+    String getSuspendBlockerProxNegativeId(int displayId) {
+        return "[" + displayId + "]prox negative";
+    }
+
+    @VisibleForTesting
+    String getSuspendBlockerProxDebounceId(int displayId) {
+        return "[" + displayId + "]prox debounce";
+    }
+
+    /** Functional interface for providing time. */
+    @VisibleForTesting
+    interface Clock {
+        /**
+         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+         */
+        long uptimeMillis();
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        Clock getClock() {
+            return SystemClock::uptimeMillis;
+        }
+
+        DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+                int displayId, int displayState) {
+            return new DisplayPowerState(blanker, colorFade, displayId, displayState);
+        }
+
+        DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+                FloatProperty<DisplayPowerState> firstProperty,
+                FloatProperty<DisplayPowerState> secondProperty) {
+            return new DualRampAnimator(dps, firstProperty, secondProperty);
+        }
+    }
+
     static class CachedBrightnessInfo {
         public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableFloat adjustedBrightness =
-            new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableFloat brightnessMin =
-            new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableFloat brightnessMax =
-            new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+                new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
         public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
         public MutableFloat hbmTransitionPoint =
-            new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+                new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
         public MutableInt brightnessMaxReason =
-            new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+                new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
 
         public boolean checkAndSetFloat(MutableFloat mf, float f) {
             if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 6698612..d831dbd 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -27,8 +27,9 @@
 public final class BrightnessEvent {
     public static final int FLAG_RBC = 0x1;
     public static final int FLAG_INVALID_LUX = 0x2;
-    public static final int FLAG_DOZE_SCALE = 0x3;
-    public static final int FLAG_USER_SET = 0x4;
+    public static final int FLAG_DOZE_SCALE = 0x4;
+    public static final int FLAG_USER_SET = 0x8;
+    public static final int FLAG_IDLE_CURVE = 0x16;
 
     private BrightnessReason mReason = new BrightnessReason();
     private int mDisplayId;
@@ -257,6 +258,7 @@
         return ((mFlags & FLAG_USER_SET) != 0 ? "user_set " : "")
                 + ((mFlags & FLAG_RBC) != 0 ? "rbc " : "")
                 + ((mFlags & FLAG_INVALID_LUX) != 0 ? "invalid_lux " : "")
-                + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "");
+                + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "")
+                + ((mFlags & FLAG_IDLE_CURVE) != 0 ? "idle_curve " : "");
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 6c75dbf..eb73234 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -223,6 +223,17 @@
     }
 
     @AnyThread
+    boolean updateEditorToolType(int toolType) {
+        try {
+            mTarget.updateEditorToolType(toolType);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+            return false;
+        }
+        return true;
+    }
+
+    @AnyThread
     void changeInputMethodSubtype(InputMethodSubtype subtype) {
         try {
             mTarget.changeInputMethodSubtype(subtype);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6f37b51..d48acb1 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -132,6 +132,7 @@
 import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.InputDevice;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManager.DisplayImePolicy;
@@ -3320,7 +3321,8 @@
 
     @Override
     public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
-            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            int lastClickTooType, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
         int uid = Binder.getCallingUid();
         ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3332,7 +3334,8 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
-                return showCurrentInputLocked(windowToken, flags, resultReceiver, reason);
+                return showCurrentInputLocked(
+                        windowToken, flags, lastClickTooType, resultReceiver, reason);
             } finally {
                 Binder.restoreCallingIdentity(ident);
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3404,8 +3407,15 @@
     }
 
     @GuardedBy("ImfLock.class")
-    boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
+    boolean showCurrentInputLocked(IBinder windowToken, int flags,
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+        return showCurrentInputLocked(
+                windowToken, flags, MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
+    }
+
+    @GuardedBy("ImfLock.class")
+    private boolean showCurrentInputLocked(IBinder windowToken, int flags, int lastClickToolType,
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
         mShowRequested = true;
         if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) {
             return false;
@@ -3434,6 +3444,10 @@
                         + ", " + showFlags + ", " + resultReceiver + ") for reason: "
                         + InputMethodDebug.softInputDisplayReasonToString(reason));
             }
+
+            if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
+                curMethod.updateEditorToolType(lastClickToolType);
+            }
             // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
             if (curMethod.showSoftInput(showInputToken, showFlags, resultReceiver)) {
                 onShowHideSoftInputRequested(true /* show */, windowToken, reason);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d9cf353..5b75b6a 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -144,7 +144,7 @@
     private static final int PASSWORD_SCRYPT_LOG_R = 3;
     private static final int PASSWORD_SCRYPT_LOG_P = 1;
     private static final int PASSWORD_SALT_LENGTH = 16;
-    private static final int PASSWORD_TOKEN_LENGTH = 32;
+    private static final int STRETCHED_LSKF_LENGTH = 32;
     private static final String TAG = "SyntheticPasswordManager";
 
     private static final byte[] PERSONALIZATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
@@ -772,7 +772,7 @@
             LockscreenCredential credential, SyntheticPassword sp, int userId) {
         long protectorId = generateProtectorId();
         PasswordData pwd = PasswordData.create(credential.getType());
-        byte[] pwdToken = computePasswordToken(credential, pwd);
+        byte[] stretchedLskf = stretchLskf(credential, pwd);
         final long sid;
         final byte[] protectorSecret;
 
@@ -780,7 +780,7 @@
             // Protector uses Weaver to verify the LSKF
             int weaverSlot = getNextAvailableWeaverSlot();
             Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
-            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
+            byte[] weaverSecret = weaverEnroll(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf),
                     null);
             if (weaverSecret == null) {
                 throw new IllegalStateException(
@@ -793,7 +793,7 @@
 
             pwd.passwordHandle = null;
             sid = GateKeeper.INVALID_SECURE_USER_ID;
-            protectorSecret = transformUnderWeaverSecret(pwdToken, weaverSecret);
+            protectorSecret = transformUnderWeaverSecret(stretchedLskf, weaverSecret);
         } else {
             // Protector uses Gatekeeper to verify the LSKF
 
@@ -807,7 +807,7 @@
             GateKeeperResponse response;
             try {
                 response = gatekeeper.enroll(fakeUid(userId), null, null,
-                        passwordTokenToGkInput(pwdToken));
+                        stretchedLskfToGkPassword(stretchedLskf));
             } catch (RemoteException e) {
                 throw new IllegalStateException("Failed to enroll LSKF for new SP protector for "
                         + "user " + userId, e);
@@ -818,7 +818,7 @@
             }
             pwd.passwordHandle = response.getPayload();
             sid = sidFromPasswordHandle(pwd.passwordHandle);
-            protectorSecret = transformUnderSecdiscardable(pwdToken,
+            protectorSecret = transformUnderSecdiscardable(stretchedLskf,
                     createSecdiscardable(protectorId, userId));
             // No need to pass in quality since the credential type already encodes sufficient info
             synchronizeFrpPassword(pwd, 0, userId);
@@ -836,12 +836,13 @@
         PersistentData persistentData = mStorage.readPersistentDataBlock();
         if (persistentData.type == PersistentData.TYPE_SP) {
             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
-            byte[] pwdToken = computePasswordToken(userCredential, pwd);
+            byte[] stretchedLskf = stretchLskf(userCredential, pwd);
 
             GateKeeperResponse response;
             try {
                 response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
-                        0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
+                        0 /* challenge */, pwd.passwordHandle,
+                        stretchedLskfToGkPassword(stretchedLskf));
             } catch (RemoteException e) {
                 Slog.e(TAG, "FRP verifyChallenge failed", e);
                 return VerifyCredentialResponse.ERROR;
@@ -853,10 +854,10 @@
                 return VerifyCredentialResponse.ERROR;
             }
             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
-            byte[] pwdToken = computePasswordToken(userCredential, pwd);
+            byte[] stretchedLskf = stretchLskf(userCredential, pwd);
             int weaverSlot = persistentData.userId;
 
-            return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
+            return weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf)).stripPayload();
         } else {
             Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
                     + persistentData.type);
@@ -1034,7 +1035,7 @@
             return result;
         }
 
-        byte[] pwdToken = computePasswordToken(credential, pwd);
+        byte[] stretchedLskf = stretchLskf(credential, pwd);
 
         final byte[] protectorSecret;
         final long sid;
@@ -1046,20 +1047,20 @@
                 result.gkResponse = VerifyCredentialResponse.ERROR;
                 return result;
             }
-            result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
+            result.gkResponse = weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf));
             if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
                 return result;
             }
             sid = GateKeeper.INVALID_SECURE_USER_ID;
-            protectorSecret = transformUnderWeaverSecret(pwdToken,
+            protectorSecret = transformUnderWeaverSecret(stretchedLskf,
                     result.gkResponse.getGatekeeperHAT());
         } else {
             // Protector uses Gatekeeper to verify the LSKF
-            byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
+            byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
             GateKeeperResponse response;
             try {
                 response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
-                        pwd.passwordHandle, gkPwdToken);
+                        pwd.passwordHandle, gkPassword);
             } catch (RemoteException e) {
                 Slog.e(TAG, "gatekeeper verify failed", e);
                 result.gkResponse = VerifyCredentialResponse.ERROR;
@@ -1072,7 +1073,7 @@
                     GateKeeperResponse reenrollResponse;
                     try {
                         reenrollResponse = gatekeeper.enroll(fakeUid(userId),
-                                pwd.passwordHandle, gkPwdToken, gkPwdToken);
+                                pwd.passwordHandle, gkPassword, gkPassword);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
                         reenrollResponse = GateKeeperResponse.ERROR;
@@ -1098,7 +1099,7 @@
                 return result;
             }
             sid = sidFromPasswordHandle(pwd.passwordHandle);
-            protectorSecret = transformUnderSecdiscardable(pwdToken,
+            protectorSecret = transformUnderSecdiscardable(stretchedLskf,
                     loadSecdiscardable(protectorId, userId));
         }
         // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
@@ -1468,18 +1469,20 @@
         return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
     }
 
-    private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
+    private byte[] stretchLskf(LockscreenCredential credential, PasswordData data) {
         final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential();
         return scrypt(password, data.salt, 1 << data.scryptLogN, 1 << data.scryptLogR,
-                1 << data.scryptLogP, PASSWORD_TOKEN_LENGTH);
+                1 << data.scryptLogP, STRETCHED_LSKF_LENGTH);
     }
 
-    private byte[] passwordTokenToGkInput(byte[] token) {
-        return SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_USER_GK_AUTH, token);
+    private byte[] stretchedLskfToGkPassword(byte[] stretchedLskf) {
+        return SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_USER_GK_AUTH,
+                stretchedLskf);
     }
 
-    private byte[] passwordTokenToWeaverKey(byte[] token) {
-        byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY, token);
+    private byte[] stretchedLskfToWeaverKey(byte[] stretchedLskf) {
+        byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY,
+                stretchedLskf);
         if (key.length < mWeaverConfig.keySize) {
             throw new IllegalArgumentException("weaver key length too small");
         }
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index a5c762a..c12dc8e 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -119,7 +119,7 @@
         ComponentName componentName = getComponentName(pendingIntent, componentType);
         if (componentName != null) {
             if (!TextUtils.equals(componentName.getPackageName(), sessionPackageName)) {
-                EventLog.writeEvent(0x534e4554, "238177121", -1, "");
+                EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
                 throw new IllegalArgumentException("ComponentName does not belong to "
                         + "sessionPackageName. sessionPackageName = " + sessionPackageName
                         + ", ComponentName pkg = " + componentName.getPackageName());
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1ee9a87..4f8771a 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -914,16 +914,7 @@
         }
 
         @Override
-        public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
-                throws RemoteException {
-            //mPackageName has been verified in MediaSessionService.enforcePackageName().
-            if (!TextUtils.equals(sessionPackageName, mPackageName)) {
-                EventLog.writeEvent(0x534e4554, "238177121", -1, "");
-                throw new IllegalArgumentException("sessionPackageName name does not match "
-                        + "package name provided to MediaSessionRecord. sessionPackageName = "
-                        + sessionPackageName + ", pkg = "
-                        + mPackageName);
-            }
+        public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
             final long token = Binder.clearCallingIdentity();
             try {
                 if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -931,7 +922,7 @@
                     return;
                 }
                 mMediaButtonReceiverHolder =
-                        MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
+                        MediaButtonReceiverHolder.create(mContext, mUserId, pi, mPackageName);
                 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -945,7 +936,7 @@
                 //mPackageName has been verified in MediaSessionService.enforcePackageName().
                 if (receiver != null && !TextUtils.equals(
                         mPackageName, receiver.getPackageName())) {
-                    EventLog.writeEvent(0x534e4554, "238177121", -1, "");
+                    EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
                     throw new IllegalArgumentException("receiver does not belong to "
                             + "package name provided to MediaSessionRecord. Pkg = " + mPackageName
                             + ", Receiver Pkg = " + receiver.getPackageName());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b7a5f70..e022f15 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -89,6 +89,7 @@
 import android.provider.Settings;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
+import android.service.voice.VoiceInteractionManagerInternal;
 import android.stats.devicepolicy.DevicePolicyEnums;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -4366,6 +4367,11 @@
         Binder.withCleanCallingIdentity(() -> {
             mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
             dispatchUserAdded(preCreatedUser, token);
+            VoiceInteractionManagerInternal vimi = LocalServices
+                    .getService(VoiceInteractionManagerInternal.class);
+            if (vimi != null) {
+                vimi.onPreCreatedUserConversion(preCreatedUser.id);
+            }
         });
         return preCreatedUser;
     }
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index d49227d..905bcf9 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -64,7 +64,7 @@
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
                 ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
-                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT_AFTER_OTA);
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
                 ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
         COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index d2a00af..cceb58d 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1020,6 +1020,7 @@
     public boolean showAssistFromActivity(IBinder token, Bundle args) {
         final long ident = Binder.clearCallingIdentity();
         try {
+            final String callingAttributionTag;
             synchronized (mGlobalLock) {
                 final ActivityRecord caller = ActivityRecord.forTokenLocked(token);
                 final Task topRootTask = mService.getTopDisplayFocusedRootTask();
@@ -1035,9 +1036,10 @@
                             + " is not visible");
                     return false;
                 }
+                callingAttributionTag = top.launchedFromFeatureId;
             }
             return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION,
-                    null /* showCallback */, token);
+                    callingAttributionTag, null /* showCallback */, token);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -1054,6 +1056,7 @@
     @Override
     public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
         Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+        final String callingAttributionTag;
         synchronized (mGlobalLock) {
             final Task topRootTask = mService.getTopDisplayFocusedRootTask();
             final ActivityRecord activity = topRootTask != null
@@ -1071,9 +1074,10 @@
                 return;
             }
             activity.pendingVoiceInteractionStart = true;
+            callingAttributionTag = activity.launchedFromFeatureId;
         }
         LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .startLocalVoiceInteraction(callingActivity, options);
+                .startLocalVoiceInteraction(callingActivity, callingAttributionTag, options);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index de2bbb6..135cf5d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4736,9 +4736,6 @@
             mPendingRemoteAnimation = options.getRemoteAnimationAdapter();
         }
         mPendingRemoteTransition = options.getRemoteTransition();
-        // Since options gets sent to client apps, remove transition information from it.
-        options.setRemoteTransition(null);
-        options.setRemoteAnimationAdapter(null);
     }
 
     void applyOptionsAnimation() {
@@ -4959,8 +4956,12 @@
     ActivityOptions takeOptions() {
         if (DEBUG_TRANSITION) Slog.i(TAG, "Taking options for " + this + " callers="
                 + Debug.getCallers(6));
+        if (mPendingOptions == null) return null;
         final ActivityOptions opts = mPendingOptions;
         mPendingOptions = null;
+        // Strip sensitive information from options before sending it to app.
+        opts.setRemoteTransition(null);
+        opts.setRemoteAnimationAdapter(null);
         return opts;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 471424b..87a4fe9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2311,16 +2311,25 @@
      * @return a list of {@link ActivityManager.RunningTaskInfo} with up to {@code maxNum} items
      */
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */);
+        return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+                INVALID_DISPLAY);
     }
 
     /**
      * @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever
      *                                 be visible in the recent task list in systemui
      */
-    @Override
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
             boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+        return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+    }
+
+    /**
+     * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+     */
+    @Override
+    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
+            boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
 
@@ -2342,7 +2351,7 @@
             final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
             flags |= (allowed ? RunningTasks.FLAG_ALLOWED : 0);
             mRootWindowContainer.getRunningTasks(
-                    maxNum, list, flags, callingUid, callingProfileIds);
+                    maxNum, list, flags, callingUid, callingProfileIds, displayId);
         }
 
         return list;
@@ -2568,6 +2577,9 @@
                     }
                     task = r.getTask();
                 }
+                // If {@code isSystemCaller} is {@code true}, it means the user intends to stop
+                // pinned mode through UI; otherwise, it's called by an app and we need to stop
+                // locked or pinned mode, subject to checks.
                 getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
             }
             // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
@@ -2986,7 +2998,7 @@
 
     @Override
     public boolean requestAssistDataForTask(IAssistDataReceiver receiver, int taskId,
-            String callingPackageName) {
+            String callingPackageName, @Nullable String callingAttributionTag) {
         mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
                 "requestAssistDataForTask()");
         final long callingId = Binder.clearCallingIdentity();
@@ -3013,7 +3025,7 @@
         requester.requestAssistData(topActivityToken, true /* fetchData */,
                 false /* fetchScreenshot */, false /* fetchStructure */, true /* allowFetchData */,
                 false /* allowFetchScreenshot*/, true /* ignoreFocusCheck */,
-                Binder.getCallingUid(), callingPackageName);
+                Binder.getCallingUid(), callingPackageName, callingAttributionTag);
 
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7aa0541..25d187f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1900,8 +1900,10 @@
     /**
      * Called when the resource overlays change.
      */
-    public void onOverlayChangedLw() {
+    void onOverlayChanged() {
         updateCurrentUserResources();
+        // Update the latest display size, cutout.
+        mDisplayContent.updateDisplayInfo();
         onConfigurationChanged();
         mSystemGestures.onConfigurationChanged();
     }
@@ -2933,7 +2935,10 @@
             return;
         }
 
-        mDisplayContent.unregisterPointerEventListener(mPointerLocationView);
+        if (!mDisplayContent.isRemoved()) {
+            mDisplayContent.unregisterPointerEventListener(mPointerLocationView);
+        }
+
         final WindowManager wm = mContext.getSystemService(WindowManager.class);
         wm.removeView(mPointerLocationView);
         mPointerLocationView = null;
@@ -2958,6 +2963,9 @@
         mHandler.post(mGestureNavigationSettingsObserver::unregister);
         mHandler.post(mForceShowNavBarSettingsObserver::unregister);
         mImmersiveModeConfirmation.release();
+        if (mService.mPointerLocationEnabled) {
+            setPointerLocationEnabled(false);
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 7a055d2..f11c2a7 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -481,27 +481,24 @@
      *
      * @param task the task that requested the end of lock task mode ({@code null} for quitting app
      *             pinning mode)
-     * @param isSystemCaller indicates whether this request comes from the system via
-     *                       {@link ActivityTaskManagerService#stopSystemLockTaskMode()}. If
-     *                       {@code true}, it means the user intends to stop pinned mode through UI;
-     *                       otherwise, it's called by an app and we need to stop locked or pinned
-     *                       mode, subject to checks.
+     * @param stopAppPinning indicates whether to stop app pinning mode or to stop a task from
+     *                       being locked.
      * @param callingUid the caller that requested the end of lock task mode.
      * @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in
      *                                  foreground)
      * @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
      *                           they differ from the one that launched lock task mode.
      */
-    void stopLockTaskMode(@Nullable Task task, boolean isSystemCaller, int callingUid) {
+    void stopLockTaskMode(@Nullable Task task, boolean stopAppPinning, int callingUid) {
         if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
             return;
         }
 
-        if (isSystemCaller) {
+        if (stopAppPinning) {
             if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
                 clearLockedTasks("stopAppPinning");
             } else {
-                Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true");
+                Slog.e(TAG_LOCKTASK, "Attempted to stop app pinning while fully locked");
                 showLockTaskToast();
             }
 
@@ -642,6 +639,10 @@
      * @param callingUid the caller that requested the launch of lock task mode.
      */
     void startLockTaskMode(@NonNull Task task, boolean isSystemCaller, int callingUid) {
+        if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+            ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskMode: Can't lock due to auth");
+            return;
+        }
         if (!isSystemCaller) {
             task.mLockTaskUid = callingUid;
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
@@ -654,6 +655,11 @@
                     statusBarManager.showScreenPinningRequest(task.mTaskId);
                 }
                 return;
+            } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+                // startLockTask() called by app, and app is part of lock task allowlist.
+                // Deactivate the currently pinned task before doing so.
+                Slog.i(TAG, "Stop app pinning before entering full lock task mode");
+                stopLockTaskMode(/* task= */ null, /* stopAppPinning= */ true, callingUid);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d8c3795..7d9dc21 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3326,9 +3326,16 @@
 
     @VisibleForTesting
     void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
-            int flags, int callingUid, ArraySet<Integer> profileIds) {
-        mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, this, callingUid,
-                profileIds);
+            int flags, int callingUid, ArraySet<Integer> profileIds, int displayId) {
+        WindowContainer root = this;
+        if (displayId != INVALID_DISPLAY) {
+            root = getDisplayContent(displayId);
+            if (root == null) {
+                return;
+            }
+        }
+        mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, mService.getRecentTasks(),
+                root, callingUid, profileIds);
     }
 
     void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1ec191e..120fec0 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -60,8 +60,8 @@
     private RecentTasks mRecentTasks;
     private boolean mKeepIntentExtra;
 
-    void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
-            RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+    void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+            WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
         // Return early if there are no tasks to fetch
         if (maxNum <= 0) {
             return;
@@ -76,7 +76,7 @@
         mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
         mFilterOnlyVisibleRecents =
                 (flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
-        mRecentTasks = root.mService.getRecentTasks();
+        mRecentTasks = recentTasks;
         mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
 
         final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e65f0bb..74e1fe4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5319,7 +5319,23 @@
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
                     (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
+                boolean abort;
+                try {
+                    abort = !mTaskSupervisor.checkStartAnyActivityPermission(destIntent,
+                            parent.info, null /* resultWho */, -1 /* requestCode */, srec.getPid(),
+                            callingUid, srec.info.packageName, null /* callingFeatureId */,
+                            false /* ignoreTargetSecurity */, false /* launchingInTask */, srec.app,
+                            null /* resultRecord */, null /* resultRootTask */);
+                } catch (SecurityException e) {
+                    abort = true;
+                }
+                if (abort) {
+                    Slog.e(TAG, "Cannot navigateUpTo, intent =" + destIntent);
+                    foundParentInTask = false;
+                } else {
+                    parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
+                            srec.packageName);
+                }
             } else {
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 602579f..a4d6f70 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -17,6 +17,12 @@
 package com.android.server.wm;
 
 import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -38,6 +44,7 @@
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskFragmentOrganizerController;
 import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
 
 import com.android.internal.protolog.common.ProtoLog;
 
@@ -68,6 +75,11 @@
     private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
             new ArrayList<>();
 
+    /** Map from {@link ITaskFragmentOrganizer} to {@link TaskFragmentTransaction}. */
+    private final ArrayMap<IBinder, TaskFragmentTransaction> mTmpOrganizerToTransactionMap =
+            new ArrayMap<>();
+    private final ArrayList<ITaskFragmentOrganizer> mTmpOrganizerList = new ArrayList<>();
+
     TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
         mAtmService = atm;
         mGlobalLock = atm.mGlobalLock;
@@ -145,107 +157,138 @@
             mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
         }
 
-        void onTaskFragmentAppeared(TaskFragment tf) {
+        @NonNull
+        TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
             final TaskFragmentInfo info = tf.getTaskFragmentInfo();
-            try {
-                mOrganizer.onTaskFragmentAppeared(info);
-                mLastSentTaskFragmentInfos.put(tf, info);
-                tf.mTaskFragmentAppearedSent = true;
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
+            tf.mTaskFragmentAppearedSent = true;
+            mLastSentTaskFragmentInfos.put(tf, info);
+            final TaskFragmentTransaction.Change change =
+                    new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_APPEARED)
+                            .setTaskFragmentToken(tf.getFragmentToken())
+                            .setTaskFragmentInfo(info);
+            if (shouldSendTaskFragmentParentInfoChanged(tf)) {
+                // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task
+                final Task task = tf.getTask();
+                mLastSentTaskFragmentParentConfigs
+                        .put(tf, new Configuration(task.getConfiguration()));
+                change.setTaskId(task.mTaskId)
+                        .setTaskConfiguration(task.getConfiguration());
             }
-            onTaskFragmentParentInfoChanged(tf);
+            return change;
         }
 
-        void onTaskFragmentVanished(TaskFragment tf) {
+        @NonNull
+        TaskFragmentTransaction.Change prepareTaskFragmentVanished(@NonNull TaskFragment tf) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
-            try {
-                mOrganizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
-            }
             tf.mTaskFragmentAppearedSent = false;
             mLastSentTaskFragmentInfos.remove(tf);
             mLastSentTaskFragmentParentConfigs.remove(tf);
+            return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
+                    .setTaskFragmentToken(tf.getFragmentToken())
+                    .setTaskFragmentInfo(tf.getTaskFragmentInfo());
         }
 
-        void onTaskFragmentInfoChanged(TaskFragment tf) {
-            // Parent config may have changed. The controller will check if there is any important
-            // config change for the organizer.
-            onTaskFragmentParentInfoChanged(tf);
-
+        @Nullable
+        TaskFragmentTransaction.Change prepareTaskFragmentInfoChanged(
+                @NonNull TaskFragment tf) {
             // Check if the info is different from the last reported info.
             final TaskFragmentInfo info = tf.getTaskFragmentInfo();
             final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
             if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
                     info.getConfiguration(), lastInfo.getConfiguration())) {
-                return;
+                // Parent config may have changed. The controller will check if there is any
+                // important config change for the organizer.
+                return prepareTaskFragmentParentInfoChanged(tf);
             }
+
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
                     tf.getName());
-            try {
-                mOrganizer.onTaskFragmentInfoChanged(info);
-                mLastSentTaskFragmentInfos.put(tf, info);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
+            mLastSentTaskFragmentInfos.put(tf, info);
+            final TaskFragmentTransaction.Change change =
+                    new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_INFO_CHANGED)
+                            .setTaskFragmentToken(tf.getFragmentToken())
+                            .setTaskFragmentInfo(info);
+            if (shouldSendTaskFragmentParentInfoChanged(tf)) {
+                // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task
+                // at once.
+                // Parent config may have changed. The controller will check if there is any
+                // important config change for the organizer.
+                final Task task = tf.getTask();
+                mLastSentTaskFragmentParentConfigs
+                        .put(tf, new Configuration(task.getConfiguration()));
+                change.setTaskId(task.mTaskId)
+                        .setTaskConfiguration(task.getConfiguration());
             }
+            return change;
         }
 
-        void onTaskFragmentParentInfoChanged(TaskFragment tf) {
-            // Check if the parent info is different from the last reported parent info.
-            if (tf.getParent() == null || tf.getParent().asTask() == null) {
-                mLastSentTaskFragmentParentConfigs.remove(tf);
-                return;
+        @Nullable
+        TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
+                @NonNull TaskFragment tf) {
+            if (!shouldSendTaskFragmentParentInfoChanged(tf)) {
+                return null;
             }
-            final Task parent = tf.getParent().asTask();
+
+            final Task parent = tf.getTask();
             final Configuration parentConfig = parent.getConfiguration();
-            final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
-            if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
-                    && parentConfig.windowConfiguration.getWindowingMode()
-                    == lastParentConfig.windowConfiguration.getWindowingMode()) {
-                return;
-            }
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                     "TaskFragment parent info changed name=%s parentTaskId=%d",
                     tf.getName(), parent.mTaskId);
-            try {
-                mOrganizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
-                mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
-            }
+            mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
+            return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
+                    .setTaskFragmentToken(tf.getFragmentToken())
+                    .setTaskId(parent.mTaskId)
+                    .setTaskConfiguration(parent.getConfiguration());
         }
 
-        void onTaskFragmentError(IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
-                int opType, Throwable exception) {
+        /** Whether the system should report TaskFragment parent info changed to the organizer. */
+        private boolean shouldSendTaskFragmentParentInfoChanged(@NonNull TaskFragment tf) {
+            final Task parent = tf.getTask();
+            if (parent == null) {
+                // The TaskFragment is not attached.
+                mLastSentTaskFragmentParentConfigs.remove(tf);
+                return false;
+            }
+            // Check if the parent info is different from the last reported parent info.
+            final Configuration parentConfig = parent.getConfiguration();
+            final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
+            return !configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
+                    || parentConfig.windowConfiguration.getWindowingMode()
+                    != lastParentConfig.windowConfiguration.getWindowingMode();
+        }
+
+        @NonNull
+        TaskFragmentTransaction.Change prepareTaskFragmentError(
+                @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+                int opType, @NonNull Throwable exception) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                     "Sending TaskFragment error exception=%s", exception.toString());
             final TaskFragmentInfo info =
                     taskFragment != null ? taskFragment.getTaskFragmentInfo() : null;
             final Bundle errorBundle = putErrorInfoInBundle(exception, info, opType);
-            try {
-                mOrganizer.onTaskFragmentError(errorCallbackToken, errorBundle);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
-            }
+            return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_ERROR)
+                    .setErrorCallbackToken(errorCallbackToken)
+                    .setErrorBundle(errorBundle);
         }
 
-        void onActivityReparentToTask(ActivityRecord activity) {
+        @Nullable
+        TaskFragmentTransaction.Change prepareActivityReparentToTask(
+                @NonNull ActivityRecord activity) {
             if (activity.finishing) {
                 Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
-                return;
+                return null;
             }
             final Task task = activity.getTask();
             if (task == null || task.effectiveUid != mOrganizerUid) {
                 Slog.d(TAG, "Reparent activity=" + activity.token
                         + " is not in a task belong to the organizer app.");
-                return;
+                return null;
             }
             if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
                 Slog.d(TAG, "Reparent activity=" + activity.token
                         + " is not allowed to be embedded.");
-                return;
+                return null;
             }
 
             final IBinder activityToken;
@@ -268,11 +311,10 @@
             }
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
                     activity.token, task.mTaskId);
-            try {
-                mOrganizer.onActivityReparentToTask(task.mTaskId, activity.intent, activityToken);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Exception sending onActivityReparentToTask callback", e);
-            }
+            return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENT_TO_TASK)
+                    .setTaskId(task.mTaskId)
+                    .setActivityIntent(activity.intent)
+                    .setActivityToken(activityToken);
         }
     }
 
@@ -375,7 +417,7 @@
      */
     @Nullable
     public RemoteAnimationDefinition getRemoteAnimationDefinition(
-            ITaskFragmentOrganizer organizer, int taskId) {
+            @NonNull ITaskFragmentOrganizer organizer, int taskId) {
         synchronized (mGlobalLock) {
             final TaskFragmentOrganizerState organizerState =
                     mTaskFragmentOrganizerState.get(organizer.asBinder());
@@ -385,12 +427,13 @@
         }
     }
 
-    int getTaskFragmentOrganizerUid(ITaskFragmentOrganizer organizer) {
+    int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         return state.mOrganizerUid;
     }
 
-    void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+    void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         if (!state.addTaskFragment(taskFragment)) {
             return;
@@ -406,19 +449,20 @@
         }
     }
 
-    void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+    void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment) {
         handleTaskFragmentInfoChanged(organizer, taskFragment,
                 PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
     }
 
-    void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
-            TaskFragment taskFragment) {
+    void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment) {
         handleTaskFragmentInfoChanged(organizer, taskFragment,
                 PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
     }
 
-    private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
-            TaskFragment taskFragment, int eventType) {
+    private void handleTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment, int eventType) {
         validateAndGetState(organizer);
         if (!taskFragment.mTaskFragmentAppearedSent) {
             // Skip if TaskFragment still not appeared.
@@ -444,7 +488,8 @@
         mPendingTaskFragmentEvents.add(pendingEvent);
     }
 
-    void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+    void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragment taskFragment) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
             PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
@@ -467,8 +512,9 @@
         state.removeTaskFragment(taskFragment);
     }
 
-    void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
-            TaskFragment taskFragment, int opType, Throwable exception) {
+    void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer,
+            @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+            int opType, @NonNull Throwable exception) {
         validateAndGetState(organizer);
         Slog.w(TAG, "onTaskFragmentError ", exception);
         final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
@@ -483,7 +529,7 @@
         mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
     }
 
-    void onActivityReparentToTask(ActivityRecord activity) {
+    void onActivityReparentToTask(@NonNull ActivityRecord activity) {
         final ITaskFragmentOrganizer organizer;
         if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
             // If the activity is previously embedded in an organized TaskFragment.
@@ -515,11 +561,11 @@
         mPendingTaskFragmentEvents.add(pendingEvent);
     }
 
-    boolean isOrganizerRegistered(ITaskFragmentOrganizer organizer) {
+    boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
         return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
     }
 
-    private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+    private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         // remove all of the children of the organized TaskFragment
         state.dispose();
@@ -539,7 +585,9 @@
      * we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
      * {@link TaskFragment} after the organizer process died.
      */
-    private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
+    @NonNull
+    private TaskFragmentOrganizerState validateAndGetState(
+            @NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state =
                 mTaskFragmentOrganizerState.get(organizer.asBinder());
         if (state == null) {
@@ -672,7 +720,7 @@
     }
 
     @Nullable
-    private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
+    private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) {
         for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
             PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
             if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
@@ -683,7 +731,7 @@
     }
 
     @Nullable
-    private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
+    private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
             int type) {
         for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
             PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
@@ -731,16 +779,36 @@
             candidateEvents.add(event);
         }
         final int numEvents = candidateEvents.size();
+        if (numEvents == 0) {
+            return;
+        }
+
+        mTmpOrganizerToTransactionMap.clear();
+        mTmpOrganizerList.clear();
         for (int i = 0; i < numEvents; i++) {
-            dispatchEvent(candidateEvents.get(i));
+            final PendingTaskFragmentEvent event = candidateEvents.get(i);
+            if (!mTmpOrganizerToTransactionMap.containsKey(event.mTaskFragmentOrg.asBinder())) {
+                mTmpOrganizerToTransactionMap.put(event.mTaskFragmentOrg.asBinder(),
+                        new TaskFragmentTransaction());
+                mTmpOrganizerList.add(event.mTaskFragmentOrg);
+            }
+            mTmpOrganizerToTransactionMap.get(event.mTaskFragmentOrg.asBinder())
+                    .addChange(prepareChange(event));
         }
-        if (numEvents > 0) {
-            mPendingTaskFragmentEvents.removeAll(candidateEvents);
+        final int numOrganizers = mTmpOrganizerList.size();
+        for (int i = 0; i < numOrganizers; i++) {
+            final ITaskFragmentOrganizer organizer = mTmpOrganizerList.get(i);
+            dispatchTransactionInfo(organizer,
+                    mTmpOrganizerToTransactionMap.get(organizer.asBinder()));
         }
+        mPendingTaskFragmentEvents.removeAll(candidateEvents);
+        mTmpOrganizerToTransactionMap.clear();
+        mTmpOrganizerList.clear();
     }
 
-    private static boolean isTaskVisible(Task task, ArrayList<Task> knownVisibleTasks,
-            ArrayList<Task> knownInvisibleTasks) {
+    private static boolean isTaskVisible(@NonNull Task task,
+            @NonNull ArrayList<Task> knownVisibleTasks,
+            @NonNull ArrayList<Task> knownInvisibleTasks) {
         if (knownVisibleTasks.contains(task)) {
             return true;
         }
@@ -756,44 +824,57 @@
         }
     }
 
-    void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
-        PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
+    void dispatchPendingInfoChangedEvent(@NonNull TaskFragment taskFragment) {
+        final PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
                 PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
         if (event == null) {
             return;
         }
 
-        dispatchEvent(event);
+        final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
+        transaction.addChange(prepareChange(event));
+        dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
         mPendingTaskFragmentEvents.remove(event);
     }
 
-    private void dispatchEvent(PendingTaskFragmentEvent event) {
+    private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull TaskFragmentTransaction transaction) {
+        if (transaction.isEmpty()) {
+            return;
+        }
+        try {
+            organizer.onTransactionReady(transaction);
+        } catch (RemoteException e) {
+            Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
+        }
+    }
+
+    @Nullable
+    private TaskFragmentTransaction.Change prepareChange(
+            @NonNull PendingTaskFragmentEvent event) {
         final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
         final TaskFragment taskFragment = event.mTaskFragment;
         final TaskFragmentOrganizerState state =
                 mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
         if (state == null) {
-            return;
+            return null;
         }
         switch (event.mEventType) {
             case PendingTaskFragmentEvent.EVENT_APPEARED:
-                state.onTaskFragmentAppeared(taskFragment);
-                break;
+                return state.prepareTaskFragmentAppeared(taskFragment);
             case PendingTaskFragmentEvent.EVENT_VANISHED:
-                state.onTaskFragmentVanished(taskFragment);
-                break;
+                return state.prepareTaskFragmentVanished(taskFragment);
             case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
-                state.onTaskFragmentInfoChanged(taskFragment);
-                break;
+                return state.prepareTaskFragmentInfoChanged(taskFragment);
             case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
-                state.onTaskFragmentParentInfoChanged(taskFragment);
-                break;
+                return state.prepareTaskFragmentParentInfoChanged(taskFragment);
             case PendingTaskFragmentEvent.EVENT_ERROR:
-                state.onTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType,
-                        event.mException);
-                break;
+                return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
+                        event.mOpType, event.mException);
             case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
-                state.onActivityReparentToTask(event.mActivity);
+                return state.prepareActivityReparentToTask(event.mActivity);
+            default:
+                throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6527bbb..79037ab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7041,13 +7041,14 @@
     }
 
     public void onOverlayChanged() {
-        synchronized (mGlobalLock) {
-            mRoot.forAllDisplays(displayContent -> {
-                displayContent.getDisplayPolicy().onOverlayChangedLw();
-                displayContent.updateDisplayInfo();
-            });
-            requestTraversal();
-        }
+        // Post to display thread so it can get the latest display info.
+        mH.post(() -> {
+            synchronized (mGlobalLock) {
+                mAtmService.deferWindowLayout();
+                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().onOverlayChanged());
+                mAtmService.continueWindowLayout();
+            }
+        });
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3bc1e35..283010e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2452,8 +2452,7 @@
             dc.setImeLayeringTarget(null);
             dc.computeImeTarget(true /* updateImeTarget */);
         }
-        if (dc.getImeInputTarget() == this
-                && (mActivityRecord == null || !mActivityRecord.isRelaunching())) {
+        if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) {
             dc.updateImeInputAndControlTarget(null);
         }
 
@@ -2580,7 +2579,10 @@
                 // usually unnoticeable (e.g. covered by rotation animation) and the animation
                 // bounds could be inconsistent, such as depending on when the window applies
                 // its draw transaction with new rotation.
-                final boolean allowExitAnimation = !getDisplayContent().inTransition();
+                final boolean allowExitAnimation = !getDisplayContent().inTransition()
+                        // There will be a new window so the exit animation may not be visible or
+                        // look weird if its orientation is changed.
+                        && !inRelaunchingActivity();
 
                 if (wasVisible) {
                     final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
@@ -3870,7 +3872,7 @@
         // If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
         // since it will be destroyed anyway. This also prevents the client from receiving
         // windowing mode change before it is destroyed.
-        if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
+        if (inRelaunchingActivity()) {
             return;
         }
         // If this is an activity or wallpaper and is invisible or going invisible, don't report
@@ -3956,6 +3958,10 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
+    boolean inRelaunchingActivity() {
+        return mActivityRecord != null && mActivityRecord.isRelaunching();
+    }
+
     boolean isClientLocal() {
         return mClient instanceof IWindow.Stub;
     }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bbb21f8..72e7e65 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -46,6 +46,7 @@
 import android.view.InsetsState;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams.WindowType;
 import android.window.WindowContext;
 
@@ -558,6 +559,12 @@
             // The window may be detached or detaching.
             return;
         }
+        if (mTransitionController.isShellTransitionsEnabled()
+                && asActivityRecord() != null && isVisible()) {
+            // Trigger an activity level rotation transition.
+            mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_CHANGE, this);
+            mTransitionController.setReady(this);
+        }
         final int originalRotation = getWindowConfiguration().getRotation();
         onConfigurationChanged(parent.getConfiguration());
         onCancelFixedRotationTransform(originalRotation);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 74ed3df..891bc0f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -462,10 +462,10 @@
     mInputManager->getReader().dump(dump);
     dump += "\n";
 
-    mInputManager->getUnwantedInteractionBlocker().dump(dump);
+    mInputManager->getBlocker().dump(dump);
     dump += "\n";
 
-    mInputManager->getClassifier().dump(dump);
+    mInputManager->getProcessor().dump(dump);
     dump += "\n";
 
     mInputManager->getDispatcher().dump(dump);
@@ -702,7 +702,7 @@
 
 void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
     ATRACE_CALL();
-    mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices);
+    mInputManager->getBlocker().notifyInputDevicesChanged(inputDevices);
     JNIEnv* env = jniEnv();
 
     size_t count = inputDevices.size();
@@ -1483,7 +1483,7 @@
 }
 
 void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
-    mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
+    mInputManager->getProcessor().setMotionClassifierEnabled(enabled);
 }
 
 bool NativeInputManager::isPerDisplayTouchModeEnabled() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b4cade3..c1c7bce 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3552,11 +3552,6 @@
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
 
-        if (mInjector.getPackageManagerInternal().filterAppAccess(packageName, caller.getUid(),
-                userHandle)) {
-            return false;
-        }
-
         synchronized (getLockObject()) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index a8b340c..e4f9eaf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -218,8 +218,8 @@
         }).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
 
         mRunningTaskInfos = new ArrayList<>();
-        when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
-                mRunningTaskInfos);
+        when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+                .thenReturn(mRunningTaskInfos);
 
 
         final UserHandle userHandle = new UserHandle(USER_ID);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
new file mode 100644
index 0000000..6a18dc1
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.util.FloatProperty;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerControllerTest {
+    private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
+    private static final int DISPLAY_ID = 42;
+
+    private OffsettableClock mClock;
+    private TestLooper mTestLooper;
+    private Handler mHandler;
+    private DisplayPowerController.Injector mInjector;
+    private Context mContextSpy;
+
+    @Mock
+    private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
+    @Mock
+    private SensorManager mSensorManagerMock;
+    @Mock
+    private DisplayBlanker mDisplayBlankerMock;
+    @Mock
+    private LogicalDisplay mLogicalDisplayMock;
+    @Mock
+    private DisplayDevice mDisplayDeviceMock;
+    @Mock
+    private BrightnessTracker mBrightnessTrackerMock;
+    @Mock
+    private BrightnessSetting mBrightnessSettingMock;
+    @Mock
+    private WindowManagerPolicy mWindowManagerPolicyMock;
+    @Mock
+    private PowerManager mPowerManagerMock;
+    @Mock
+    private Resources mResourcesMock;
+    @Mock
+    private DisplayDeviceConfig mDisplayDeviceConfigMock;
+    @Mock
+    private DisplayPowerState mDisplayPowerStateMock;
+    @Mock
+    private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;
+
+    @Captor
+    private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+        mHandler = new Handler(mTestLooper.getLooper());
+        mInjector = new DisplayPowerController.Injector() {
+            @Override
+            DisplayPowerController.Clock getClock() {
+                return mClock::now;
+            }
+
+            @Override
+            DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+                    int displayId, int displayState) {
+                return mDisplayPowerStateMock;
+            }
+
+            @Override
+            DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+                    FloatProperty<DisplayPowerState> firstProperty,
+                    FloatProperty<DisplayPowerState> secondProperty) {
+                return mDualRampAnimatorMock;
+            }
+        };
+
+        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+
+        when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+        when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+    }
+
+    @Test
+    public void testReleaseProxSuspendBlockersOnExit() throws Exception {
+        setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+        Sensor proxSensor = setUpProxSensor();
+
+        DisplayPowerController dpc = new DisplayPowerController(
+                mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+                mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+                mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+        });
+
+        when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
+        // send a display power request
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        dpr.useProximitySensor = true;
+        dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+        // Run updatePowerState to start listener for the prox sensor
+        advanceTime(1);
+
+        SensorEventListener listener = getSensorEventListener(proxSensor);
+        assertNotNull(listener);
+
+        listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
+        advanceTime(1);
+
+        // two times, one for unfinished business and one for proximity
+        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+                dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+        verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+                dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+
+        dpc.stop();
+        advanceTime(1);
+
+        // two times, one for unfinished business and one for proximity
+        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+                dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+        verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+                dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+    }
+
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
+    }
+
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
+    private Sensor setUpProxSensor() throws Exception {
+        Sensor proxSensor = TestUtils.createSensor(
+                Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+        when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
+                .thenReturn(List.of(proxSensor));
+        return proxSensor;
+    }
+
+    private SensorEventListener getSensorEventListener(Sensor sensor) {
+        verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
+                eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
+        return mSensorEventListenerCaptor.getValue();
+    }
+
+    private void setUpDisplay(int displayId, String uniqueId) {
+        DisplayInfo info = new DisplayInfo();
+        DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+
+        when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
+        when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
+        when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
+        when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
+        when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+        when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
+        when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
+        when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
+        when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
+                new DisplayDeviceConfig.SensorData() {
+                    {
+                        type = Sensor.STRING_TYPE_PROXIMITY;
+                        name = null;
+                    }
+                });
+        when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 5f9f1b2..bcdfc35 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -24,6 +24,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 import static com.android.server.job.JobSchedulerService.sSystemClock;
 
@@ -185,13 +186,17 @@
         JobStatus js = JobStatus.createFromJobInfo(
                 jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
         js.serviceInfo = mock(ServiceInfo.class);
+        js.setStandbyBucket(FREQUENT_INDEX);
         // Make sure Doze and background-not-restricted don't affect tests.
         js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(),
                 /* state */ true, /* allowlisted */false);
         js.setBackgroundNotRestrictedConstraintSatisfied(
                 sElapsedRealtimeClock.millis(), true, false);
+        js.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
         js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+        js.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), true);
         js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true);
+        js.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
         return js;
     }
 
@@ -294,6 +299,7 @@
         verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
         assertFalse(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertFalse(job.isReady());
     }
 
     @Test
@@ -309,6 +315,7 @@
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
         verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
         assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(job.isReady());
     }
 
     @Test
@@ -331,19 +338,25 @@
         inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
                 .onControllerStateChanged(any());
         assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobPending.isReady());
         assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobRunning.isReady());
         setUidBias(uid, JobInfo.BIAS_TOP_APP);
         // Processing happens on the handler, so wait until we're sure the change has been processed
         inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
                 .onControllerStateChanged(any());
         // Already running job should continue but pending job must wait.
         assertFalse(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertFalse(jobPending.isReady());
         assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobRunning.isReady());
         setUidBias(uid, JobInfo.BIAS_DEFAULT);
         inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
                 .onControllerStateChanged(any());
         assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobPending.isReady());
         assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobRunning.isReady());
     }
 
     @Test
@@ -367,11 +380,13 @@
         verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
         assertFalse(jobNonWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertFalse(jobNonWidget.isReady());
 
         when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID))
                 .thenReturn(true);
         trackJobs(jobWidget);
         assertTrue(jobWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobWidget.isReady());
     }
 
     @Test
@@ -390,6 +405,7 @@
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
         verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
         assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobStatus.isReady());
 
         mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
                 SOURCE_PACKAGE, sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
@@ -401,6 +417,7 @@
                         anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
                         anyLong(), eq(TAG_PREFETCH), any(), any());
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertFalse(jobStatus.isReady());
     }
 
     @Test
@@ -418,6 +435,7 @@
         inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertFalse(jobStatus.isReady());
 
         mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
                 SOURCE_PACKAGE, sSystemClock.millis() + MINUTE_IN_MILLIS);
@@ -426,6 +444,7 @@
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
         verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
         assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobStatus.isReady());
     }
 
     @Test
@@ -448,6 +467,7 @@
                         anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
                         anyLong(), eq(TAG_PREFETCH), any(), any());
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertFalse(jobStatus.isReady());
 
         mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
                 SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS);
@@ -456,6 +476,7 @@
                 .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
         verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
         assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+        assertTrue(jobStatus.isReady());
 
         sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
     }
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index a9ee84f..1921ce7 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -135,8 +135,9 @@
 java_library {
     name: "servicestests-core-utils",
     srcs: [
-        "src/com/android/server/pm/PackageSettingBuilder.java",
         "src/com/android/server/am/DeviceConfigSession.java",
+        "src/com/android/server/display/TestUtils.java",
+        "src/com/android/server/pm/PackageSettingBuilder.java",
     ],
     static_libs: [
         "services.core",
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index c80547c..966df4f 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -124,6 +124,8 @@
     private VirtualDeviceImpl mDeviceImpl;
     private InputController mInputController;
     private AssociationInfo mAssociationInfo;
+    private VirtualDeviceManagerService mVdms;
+    private VirtualDeviceManagerInternal mLocalService;
     @Mock
     private InputController.NativeWrapper mNativeWrapperMock;
     @Mock
@@ -139,6 +141,8 @@
     @Mock
     private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
     @Mock
+    private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
+    @Mock
     IPowerManager mIPowerManagerMock;
     @Mock
     IThermalService mIThermalServiceMock;
@@ -215,6 +219,9 @@
         mAssociationInfo = new AssociationInfo(1, 0, null,
                 MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
 
+        mVdms = new VirtualDeviceManagerService(mContext);
+        mLocalService = mVdms.getLocalServiceInstance();
+
         VirtualDeviceParams params = new VirtualDeviceParams
                 .Builder()
                 .setBlockedActivities(getBlockedActivities())
@@ -235,6 +242,28 @@
     }
 
     @Test
+    public void onVirtualDisplayCreatedLocked_listenersNotified() throws RemoteException {
+        mLocalService.registerVirtualDisplayListener(mDisplayListener);
+
+        mLocalService.onVirtualDisplayCreated(DISPLAY_ID);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID);
+    }
+
+    @Test
+    public void onVirtualDisplayRemovedLocked_listenersNotified() throws RemoteException {
+        mLocalService.registerVirtualDisplayListener(mDisplayListener);
+        mDeviceImpl.onVirtualDisplayCreatedLocked(
+                mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
+
+        mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID);
+    }
+
+    @Test
     public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
         verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 2e85897..06b4ad9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -87,6 +87,7 @@
 
     private static final int TEST_UID = 0;
     private static final String TEST_PACKAGE = "";
+    private static final String TEST_ATTRIBUTION_TAG = "";
 
     private AssistDataRequester mDataRequester;
     private Callbacks mCallbacks;
@@ -148,9 +149,9 @@
             boolean assistScreenshotAllowed) throws Exception {
         doReturn(currentActivityAssistAllowed).when(mAtm).isAssistDataAllowedOnCurrentActivity();
         doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
-                .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
+                .noteOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString(), any(), any());
         doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
-                .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
+                .noteOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString(), any(), any());
     }
 
     @Test
@@ -160,7 +161,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(5, 5, 1, 1);
     }
 
@@ -170,7 +172,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 0, 0, 0);
     }
 
@@ -180,7 +183,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 1, 0, 1);
     }
 
@@ -191,7 +195,8 @@
 
         mCallbacks.mCanHandleReceivedData = false;
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertEquals(5, mDataRequester.getPendingDataCount());
         assertEquals(1, mDataRequester.getPendingScreenshotCount());
         mGate.countDown();
@@ -226,7 +231,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 0, 0, 1);
     }
 
@@ -236,7 +242,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         // Expect a single null data when the appops is denied
         assertReceivedDataCount(0, 1, 0, 1);
     }
@@ -249,7 +256,8 @@
                 anyBoolean(), anyBoolean());
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         // Expect a single null data when requestAssistContextExtras() fails
         assertReceivedDataCount(0, 1, 0, 1);
     }
@@ -261,7 +269,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(5, 5, 0, 0);
     }
 
@@ -272,7 +281,8 @@
                 !CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         // Expect a single null screenshot when the appops is denied
         assertReceivedDataCount(5, 5, 0, 1);
     }
@@ -284,7 +294,8 @@
 
         mCallbacks.mCanHandleReceivedData = false;
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         mGate.countDown();
         waitForIdle(mHandler);
         assertThat(mCallbacks.mReceivedData).isEmpty();
@@ -297,7 +308,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 1, 0, 1);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 83f375f..adf694c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1531,10 +1531,10 @@
         public boolean mLastAllowed;
 
         @Override
-        void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
-                RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+        void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+                WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
             mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
-            super.getTasks(maxNum, list, flags, root, callingUid, profileIds);
+            super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 33b2366..b1acae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -35,6 +35,8 @@
 
 import androidx.test.filters.MediumTest;
 
+import com.google.common.truth.Correspondence;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +54,9 @@
 public class RunningTasksTest extends WindowTestsBase {
 
     private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
+    private static final Correspondence<RunningTaskInfo, Integer> TASKINFO_HAS_ID =
+            Correspondence.transforming((RunningTaskInfo t) -> t.taskId, "has id");
+
 
     private RunningTasks mRunningTasks;
 
@@ -91,8 +96,8 @@
         // collected from all tasks across all the stacks
         final int numFetchTasks = 5;
         ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
-        mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+        mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < numFetchTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -102,7 +107,7 @@
         // and does not crash
         tasks.clear();
         mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
-                mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numTasks);
         for (int i = 0; i < numTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -126,7 +131,7 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
-                mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < tasks.size(); i++) {
             final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -151,8 +156,8 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, tasks,
-                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < tasks.size(); i++) {
             final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -184,8 +189,8 @@
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
         mRunningTasks.getTasks(numFetchTasks, fetchTasks,
-                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
-                -1 /* callingUid */, PROFILE_IDS);
+                FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
         assertThat(fetchTasks).hasSize(numFetchTasks);
         assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId);
         assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId);
@@ -210,4 +215,46 @@
         task.intent = activity.intent;
         return task;
     }
+
+    @Test
+    public void testMultipleDisplays() {
+        final DisplayContent display0 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+        final DisplayContent display1 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+        final int numTasks = 10;
+        final ArrayList<Task> tasks = new ArrayList<>();
+        for (int i = 0; i < numTasks; i++) {
+            final Task stack = new TaskBuilder(mSupervisor)
+                    .setDisplay(i % 2 == 0 ? display0 : display1)
+                    .setOnTop(true)
+                    .build();
+            final Task task = createTask(stack, ".Task" + i, i, i, null);
+            tasks.add(task);
+        }
+
+        final int numFetchTasks = numTasks;
+        final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
+
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), display0, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks / 2);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(0, 2, 4, 6, 8);
+
+        fetchTasks.clear();
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), display1, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks / 2);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(1, 3, 5, 7, 9);
+
+        fetchTasks.clear();
+        mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+                FLAG_ALLOWED | FLAG_CROSS_USERS,
+                mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+        assertThat(fetchTasks).hasSize(numTasks);
+        assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+                .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index eba2755..f38731b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -199,9 +199,11 @@
         final Task parent = mock(Task.class);
         final Configuration parentConfig = new Configuration();
         parentConfig.smallestScreenWidthDp = 10;
-        doReturn(parent).when(mTaskFragment).getParent();
+        doReturn(parent).when(mTaskFragment).getTask();
         doReturn(parentConfig).when(parent).getConfiguration();
-        doReturn(parent).when(parent).asTask();
+        // Task needs to be visible
+        parent.lastActiveTime = 100;
+        doReturn(true).when(parent).shouldBeVisible(any());
 
         mTaskFragment.mTaskFragmentAppearedSent = true;
         mController.onTaskFragmentParentInfoChanged(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7699262..4b9b4a9 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -102,6 +102,7 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.soundtrigger.SoundTriggerInternal;
+import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -134,18 +135,21 @@
     private final RemoteCallbackList<IVoiceInteractionSessionListener>
             mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
 
+    // TODO(b/226201975): remove once RoleService supports pre-created users
+    private final ArrayList<UserHandle> mIgnoredPreCreatedUsers = new ArrayList<>();
+
     public VoiceInteractionManagerService(Context context) {
         super(context);
         mContext = context;
         mResolver = context.getContentResolver();
+        mUserManagerInternal = Objects.requireNonNull(
+                LocalServices.getService(UserManagerInternal.class));
         mDbHelper = new DatabaseHelper(context);
         mServiceStub = new VoiceInteractionManagerServiceStub();
         mAmInternal = Objects.requireNonNull(
                 LocalServices.getService(ActivityManagerInternal.class));
         mAtmInternal = Objects.requireNonNull(
                 LocalServices.getService(ActivityTaskManagerInternal.class));
-        mUserManagerInternal = Objects.requireNonNull(
-                LocalServices.getService(UserManagerInternal.class));
 
         LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
                 LegacyPermissionManagerInternal.class);
@@ -223,12 +227,13 @@
 
     class LocalService extends VoiceInteractionManagerInternal {
         @Override
-        public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+        public void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+                @Nullable String attributionTag, @NonNull Bundle options) {
             if (DEBUG) {
                 Slog.i(TAG, "startLocalVoiceInteraction " + callingActivity);
             }
             VoiceInteractionManagerService.this.mServiceStub.startLocalVoiceInteraction(
-                    callingActivity, options);
+                    callingActivity, attributionTag, options);
         }
 
         @Override
@@ -300,6 +305,25 @@
             }
             return hotwordDetectionConnection.mIdentity;
         }
+
+        @Override
+        public void onPreCreatedUserConversion(int userId) {
+            Slogf.d(TAG, "onPreCreatedUserConversion(%d)", userId);
+
+            for (int i = 0; i < mIgnoredPreCreatedUsers.size(); i++) {
+                UserHandle preCreatedUser = mIgnoredPreCreatedUsers.get(i);
+                if (preCreatedUser.getIdentifier() == userId) {
+                    Slogf.d(TAG, "Updating role on pre-created user %d", userId);
+                    mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
+                            preCreatedUser);
+                    mIgnoredPreCreatedUsers.remove(i);
+                    return;
+                }
+            }
+            Slogf.w(TAG, "onPreCreatedUserConversion(%d): not available on "
+                    + "mIgnoredPreCreatedUserIds (%s)", userId, mIgnoredPreCreatedUsers);
+        }
+
     }
 
     // implementation entry point and binder service
@@ -317,10 +341,12 @@
         private boolean mTemporarilyDisabled;
 
         private final boolean mEnableService;
+        // TODO(b/226201975): remove reference once RoleService supports pre-created users
+        private final RoleObserver mRoleObserver;
 
         VoiceInteractionManagerServiceStub() {
             mEnableService = shouldEnableService(mContext);
-            new RoleObserver(mContext.getMainExecutor());
+            mRoleObserver = new RoleObserver(mContext.getMainExecutor());
         }
 
         void handleUserStop(String packageName, int userHandle) {
@@ -383,14 +409,15 @@
         }
 
         // TODO: VI Make sure the caller is the current user or profile
-        void startLocalVoiceInteraction(final IBinder token, Bundle options) {
+        void startLocalVoiceInteraction(@NonNull final IBinder token,
+                @Nullable String attributionTag, @NonNull Bundle options) {
             if (mImpl == null) return;
 
             final int callingUid = Binder.getCallingUid();
             final long caller = Binder.clearCallingIdentity();
             try {
                 mImpl.showSessionLocked(options,
-                        VoiceInteractionSession.SHOW_SOURCE_ACTIVITY,
+                        VoiceInteractionSession.SHOW_SOURCE_ACTIVITY, attributionTag,
                         new IVoiceInteractionSessionShowCallback.Stub() {
                             @Override
                             public void onFailed() {
@@ -898,13 +925,13 @@
         }
 
         @Override
-        public void showSession(Bundle args, int flags) {
+        public void showSession(@NonNull Bundle args, int flags, @Nullable String attributionTag) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
 
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    mImpl.showSessionLocked(args, flags, null, null);
+                    mImpl.showSessionLocked(args, flags, attributionTag, null, null);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -929,7 +956,8 @@
         }
 
         @Override
-        public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) {
+        public boolean showSessionFromSession(@NonNull IBinder token, @NonNull Bundle sessionArgs,
+                int flags, @Nullable String attributionTag) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "showSessionFromSession without running voice interaction service");
@@ -937,7 +965,7 @@
                 }
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.showSessionLocked(sessionArgs, flags, null, null);
+                    return mImpl.showSessionLocked(sessionArgs, flags, attributionTag, null, null);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -961,8 +989,8 @@
         }
 
         @Override
-        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType,
-                String callingFeatureId) {
+        public int startVoiceActivity(@NonNull IBinder token, @NonNull Intent intent,
+                @Nullable String resolvedType, @Nullable String attributionTag) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startVoiceActivity without running voice interaction service");
@@ -980,8 +1008,8 @@
                     } else {
                         Slog.w(TAG, "Cannot find ActivityInfo in startVoiceActivity.");
                     }
-                    return mImpl.startVoiceActivityLocked(
-                            callingFeatureId, callingPid, callingUid, token, intent, resolvedType);
+                    return mImpl.startVoiceActivityLocked(attributionTag, callingPid, callingUid,
+                            token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -989,8 +1017,8 @@
         }
 
         @Override
-        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType,
-                String callingFeatureId) {
+        public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
+                @Nullable String resolvedType, @Nullable String attributionTag) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1000,7 +1028,7 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startAssistantActivityLocked(callingFeatureId, callingPid,
+                    return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
                             callingUid, token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
@@ -1669,8 +1697,10 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
         @Override
-        public boolean showSessionForActiveService(Bundle args, int sourceFlags,
-                IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+        public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+                @Nullable String attributionTag,
+                @Nullable IVoiceInteractionSessionShowCallback showCallback,
+                @Nullable IBinder activityToken) {
             if (DEBUG_USER) Slog.d(TAG, "showSessionForActiveService()");
 
             synchronized (this) {
@@ -1691,7 +1721,7 @@
                             sourceFlags
                                     | VoiceInteractionSession.SHOW_WITH_ASSIST
                                     | VoiceInteractionSession.SHOW_WITH_SCREENSHOT,
-                            showCallback, activityToken);
+                            attributionTag, showCallback, activityToken);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -1882,6 +1912,7 @@
                 pw.println("  mTemporarilyDisabled: " + mTemporarilyDisabled);
                 pw.println("  mCurUser: " + mCurUser);
                 pw.println("  mCurUserSupported: " + mCurUserSupported);
+                pw.println("  mIgnoredPreCreatedUsers: " + mIgnoredPreCreatedUsers);
                 dumpSupportedUsers(pw, "  ");
                 mDbHelper.dump(pw);
                 if (mImpl == null) {
@@ -1995,6 +2026,23 @@
 
                 List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user);
 
+                // TODO(b/226201975): this method is beling called when a pre-created user is added,
+                // at which point it doesn't have any role holders. But it's not called again when
+                // the actual user is added (i.e., when the  pre-created user is converted), so we
+                // need to save the user id and call this method again when it's converted
+                // (at onPreCreatedUserConversion()).
+                // Once RoleService properly handles pre-created users, this workaround should be
+                // removed.
+                if (roleHolders.isEmpty()) {
+                    UserInfo userInfo = mUserManagerInternal.getUserInfo(user.getIdentifier());
+                    if (userInfo != null && userInfo.preCreated) {
+                        Slogf.d(TAG, "onRoleHoldersChanged(): ignoring pre-created user %s for now",
+                                userInfo.toFullString());
+                        mIgnoredPreCreatedUsers.add(user);
+                        return;
+                    }
+                }
+
                 int userId = user.getIdentifier();
                 if (roleHolders.isEmpty()) {
                     Settings.Secure.putStringForUser(getContext().getContentResolver(),
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b9793ca..9f66059 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -244,8 +244,10 @@
                 /* direct= */ true);
     }
 
-    public boolean showSessionLocked(Bundle args, int flags,
-            IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+    public boolean showSessionLocked(@NonNull Bundle args, int flags,
+            @Nullable String attributionTag,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
         if (mActiveSession == null) {
             mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
                     mSessionComponentName, mUser, mContext, this,
@@ -269,8 +271,8 @@
         } else {
             visibleActivities = allVisibleActivities;
         }
-        return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
-                visibleActivities);
+        return mActiveSession.showLocked(args, flags, attributionTag, mDisabledShowContext,
+                showCallback, visibleActivities);
     }
 
     public void getActiveServiceSupportedActions(List<String> commands,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
index 9bdf4e4..80f5dc5 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
@@ -114,8 +114,8 @@
 
         try {
             Bundle args = new Bundle();
-            boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0, callback,
-                    /* activityToken= */ null);
+            boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0,
+                     /* attributionTag= */ null, callback, /* activityToken= */ null);
 
             if (!ok) {
                 pw.println("showSessionForActiveService() returned false");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 093e976..63781cc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -29,6 +29,8 @@
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
@@ -263,9 +265,9 @@
         return flags;
     }
 
-    public boolean showLocked(Bundle args, int flags, int disabledContext,
-            IVoiceInteractionSessionShowCallback showCallback,
-            List<ActivityAssistInfo> topActivities) {
+    public boolean showLocked(@NonNull Bundle args, int flags, @Nullable String attributionTag,
+            int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @NonNull List<ActivityAssistInfo> topActivities) {
         if (mBound) {
             if (!mFullyBound) {
                 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -291,12 +293,15 @@
                 for (int i = 0; i < topActivitiesCount; i++) {
                     topActivitiesToken.add(topActivities.get(i).getActivityToken());
                 }
+
                 mAssistDataRequester.requestAssistData(topActivitiesToken,
                         fetchData,
                         fetchScreenshot,
                         (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
                         (disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
-                        mCallingUid, mSessionComponentName.getPackageName());
+                        mCallingUid,
+                        mSessionComponentName.getPackageName(),
+                        attributionTag);
 
                 boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
                         || mAssistDataRequester.getPendingScreenshotCount() > 0;
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 4469ffc..7eec86a 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -115,15 +115,15 @@
         /** @hide */
         public static @RadioAccessNetworkType int fromString(@NonNull String str) {
             switch (str.toUpperCase()) {
-                case "GERAN" : return GERAN;
-                case "UTRAN" : return UTRAN;
-                case "EUTRAN" : return EUTRAN;
-                case "CDMA2000" : return CDMA2000;
-                case "IWLAN" : return IWLAN;
-                case "NGRAN" : return NGRAN;
+                case "UNKNOWN": return UNKNOWN;
+                case "GERAN": return GERAN;
+                case "UTRAN": return UTRAN;
+                case "EUTRAN": return EUTRAN;
+                case "CDMA2000": return CDMA2000;
+                case "IWLAN": return IWLAN;
+                case "NGRAN": return NGRAN;
                 default:
-                    Rlog.e(TAG, "Invalid access network type " + str);
-                    return UNKNOWN;
+                    throw new IllegalArgumentException("Invalid access network type " + str);
             }
         }
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6deb6f8..4d18dfe 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8596,6 +8596,13 @@
      * IWLAN handover rules that determine whether handover is allowed or disallowed between
      * cellular and IWLAN.
      *
+     * Rule syntax: "source=[GERAN|UTRAN|EUTRAN|NGRAN|IWLAN|UNKNOWN], target=[GERAN|UTRAN|EUTRAN
+     * |NGRAN|IWLAN], type=[allowed|disallowed], roaming=[true|false], capabilities=[INTERNET|MMS
+     * |FOTA|IMS|CBS|SUPL|EIMS|XCAP|DUN]"
+     *
+     * Note that UNKNOWN can be only specified in the source access network and can be only used
+     * in the disallowed rule.
+     *
      * The handover rules will be matched in the order. Here are some sample rules.
      * <string-array name="iwlan_handover_rules" num="5">
      *     <!-- Handover from IWLAN to 2G/3G is not allowed -->
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
new file mode 100644
index 0000000..bd0ae4d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.provider.MediaStore
+import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.IComponentMatcher
+
+class CameraAppHelper @JvmOverloads constructor(
+        instrumentation: Instrumentation,
+        private val pkgManager: PackageManager = instrumentation.context.packageManager
+) : StandardAppHelper(instrumentation, getCameraLauncherName(pkgManager),
+        getCameraComponent(pkgManager)){
+    companion object{
+        private fun getCameraIntent(): Intent {
+            return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
+        }
+
+        private fun getResolveInfo(pkgManager: PackageManager): ResolveInfo {
+            val intent = getCameraIntent()
+            return pkgManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+                    ?: error("unable to resolve camera activity")
+        }
+
+        private fun getCameraComponent(pkgManager: PackageManager): IComponentMatcher {
+            val resolveInfo = getResolveInfo(pkgManager)
+            return ComponentMatcher(resolveInfo.activityInfo.packageName,
+                    className = resolveInfo.activityInfo.name)
+        }
+
+        private fun getCameraLauncherName(pkgManager: PackageManager): String {
+            val resolveInfo = getResolveInfo(pkgManager)
+            return resolveInfo.activityInfo.name
+        }
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
new file mode 100644
index 0000000..3ff59e9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.CameraAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching an app after cold opening camera
+ *
+ * To run this test: `atest FlickerTests:OpenAppAfterCameraTest`
+ *
+ * Notes:
+ * Some default assertions are inherited [OpenAppTransition]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+open class OpenAppAfterCameraTest(
+    testSpec: FlickerTestParameter
+) : OpenAppFromLauncherTransition(testSpec) {
+    private val cameraApp = CameraAppHelper(instrumentation) /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            super.transition(this)
+            setup {
+                test{
+                    tapl.setExpectedRotationCheckEnabled(false)
+                }
+                eachRun {
+                    // 1. Open camera - cold -> close it first
+                    cameraApp.exit(wmHelper)
+                    cameraApp.launchViaIntent(wmHelper)
+                    // 2. Press home button (button nav mode) / swipe up to home (gesture nav mode)
+                    tapl.goHome()
+                }
+            }
+            teardown {
+                eachRun {
+                    testApp.exit(wmHelper)
+                }
+            }
+            transitions {
+                testApp.launchViaIntent(wmHelper)
+            }
+        }
+
+    /** {@inheritDoc} */
+    @FlakyTest(bugId = 206753786)
+    @Test
+    override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun focusChanges() = super.focusChanges()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() =
+            super.appWindowReplacesLauncherAsTopWindow()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun entireScreenCovered() = super.entireScreenCovered()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsVisibleAtStartAndEnd() =
+            super.statusBarLayerIsVisibleAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    /** {@inheritDoc} */
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+         * repetitions, screen orientation and navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance()
+                    .getConfigNonRotationTests()
+        }
+    }
+}