Merge changes from topic "fix-b-188932434-common-types-2"

* changes:
  Move common audio AIDL types to audio.media.audio.common
  MediaFormat: Add more audio MIME types
  Add android.media.audio.common.AidlConversion class
diff --git a/Android.bp b/Android.bp
index c156774..b8c060d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -346,6 +346,8 @@
         "modules-utils-preconditions",
         "modules-utils-os",
         "framework-permission-aidl-java",
+        "spatializer-aidl-java",
+        "audiopolicy-types-aidl-java",
     ],
 }
 
diff --git a/OWNERS b/OWNERS
index 03cfac9..ab5bd18 100644
--- a/OWNERS
+++ b/OWNERS
@@ -23,6 +23,9 @@
 # via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
 per-file */api/*current.txt = *
 
+# Test mapping changes can be made by anyone
+per-file */TEST_MAPPING = *
+
 # Support bulk translation updates
 per-file */res*/values*/*.xml = byi@google.com, delphij@google.com
 
diff --git a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
new file mode 100644
index 0000000..f3d7390
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.LongArrayMultiStateCounter;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LongArrayMultiStateCounterPerfTest {
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * A complete line-for-line reimplementation of
+     * {@link }com.android.internal.os.CpuTimeInFreqMultiStateCounter}, only in Java instead of
+     * native.
+     */
+    private static class TestLongArrayMultiStateCounter {
+        private final int mStateCount;
+        private final int mArrayLength;
+        private int mCurrentState;
+        private long mLastStateChangeTimestampMs;
+        private long mLastUpdateTimestampMs;
+
+        private static class State {
+            private long mTimeInStateSinceUpdate;
+            private long[] mCounter;
+        }
+
+        private final State[] mStates;
+        private final long[] mLastTimeInFreq;
+        private final long[] mDelta;
+
+        TestLongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
+                long timestampMs) {
+            mStateCount = stateCount;
+            mArrayLength = arrayLength;
+            mCurrentState = initialState;
+            mLastStateChangeTimestampMs = timestampMs;
+            mLastUpdateTimestampMs = timestampMs;
+            mStates = new State[stateCount];
+            for (int i = 0; i < mStateCount; i++) {
+                mStates[i] = new State();
+                mStates[i].mCounter = new long[mArrayLength];
+            }
+            mLastTimeInFreq = new long[mArrayLength];
+            mDelta = new long[mArrayLength];
+        }
+
+        public void setState(int state, long timestampMs) {
+            if (timestampMs >= mLastStateChangeTimestampMs) {
+                mStates[mCurrentState].mTimeInStateSinceUpdate +=
+                        timestampMs - mLastStateChangeTimestampMs;
+            } else {
+                for (int i = 0; i < mStateCount; i++) {
+                    mStates[i].mTimeInStateSinceUpdate = 0;
+                }
+            }
+            mCurrentState = state;
+            mLastStateChangeTimestampMs = timestampMs;
+        }
+
+        public void updateValue(long[] timeInFreq, long timestampMs) {
+            setState(mCurrentState, timestampMs);
+
+            if (timestampMs > mLastUpdateTimestampMs) {
+                if (delta(mLastTimeInFreq, timeInFreq, mDelta)) {
+                    long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs;
+                    for (int i = 0; i < mStateCount; i++) {
+                        long timeInState = mStates[i].mTimeInStateSinceUpdate;
+                        if (timeInState > 0) {
+                            add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate);
+                            mStates[i].mTimeInStateSinceUpdate = 0;
+                        }
+                    }
+                } else {
+                    throw new RuntimeException();
+                }
+            } else if (timestampMs < mLastUpdateTimestampMs) {
+                throw new RuntimeException();
+            }
+            System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength);
+            mLastUpdateTimestampMs = timestampMs;
+        }
+
+        private boolean delta(long[] timeInFreq1, long[] timeInFreq2, long[] delta) {
+            if (delta.length != mArrayLength) {
+                throw new RuntimeException();
+            }
+
+            boolean is_delta_valid = true;
+            for (int i = 0; i < mStateCount; i++) {
+                if (timeInFreq2[i] >= timeInFreq1[i]) {
+                    delta[i] = timeInFreq2[i] - timeInFreq1[i];
+                } else {
+                    delta[i] = 0;
+                    is_delta_valid = false;
+                }
+            }
+
+            return is_delta_valid;
+        }
+
+        private void add(long[] counter, long[] delta, long numerator, long denominator) {
+            if (numerator != denominator) {
+                for (int i = 0; i < mArrayLength; i++) {
+                    counter[i] += delta[i] * numerator / denominator;
+                }
+            } else {
+                for (int i = 0; i < mArrayLength; i++) {
+                    counter[i] += delta[i];
+                }
+            }
+        }
+    }
+
+    @Test
+    public void javaImplementation() {
+        TestLongArrayMultiStateCounter counter =
+                new TestLongArrayMultiStateCounter(2, 4, 0, 1000);
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        long time = 1000;
+        long[] timeInFreq = {100, 200, 300, 400};
+        while (state.keepRunning()) {
+            counter.setState(1, time);
+            counter.setState(0, time + 1000);
+            counter.updateValue(timeInFreq, time + 2000);
+            time += 10000;
+        }
+    }
+
+    @Test
+    public void nativeImplementation() {
+        LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000);
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        long time = 1000;
+        LongArrayMultiStateCounter.LongArrayContainer timeInFreq =
+                new LongArrayMultiStateCounter.LongArrayContainer(4);
+        timeInFreq.setValues(new long[]{100, 200, 300, 400});
+        while (state.keepRunning()) {
+            counter.setState(1, time);
+            counter.setState(0, time + 1000);
+            counter.updateValues(timeInFreq, time + 2000);
+            time += 10000;
+        }
+    }
+}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 7c81f09..90a5d49 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -1339,7 +1339,7 @@
         mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
     }
 
-    if (mAnimation != nullptr && mAnimation->dynamicColoringEnabled) {
+    if (mAnimation->dynamicColoringEnabled) {
         initDynamicColors();
     }
 
diff --git a/core/api/current.txt b/core/api/current.txt
index dc62931..2f01265 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2013,6 +2013,9 @@
   public static final class R.id {
     ctor public R.id();
     field public static final int accessibilityActionContextClick = 16908348; // 0x102003c
+    field public static final int accessibilityActionDragCancel;
+    field public static final int accessibilityActionDragDrop;
+    field public static final int accessibilityActionDragStart;
     field public static final int accessibilityActionHideTooltip = 16908357; // 0x1020045
     field public static final int accessibilityActionImeEnter = 16908372; // 0x1020054
     field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042
@@ -10681,6 +10684,8 @@
     field public static final String PERFORMANCE_HINT_SERVICE = "performance_hint";
     field public static final String POWER_SERVICE = "power";
     field public static final String PRINT_SERVICE = "print";
+    field public static final int RECEIVER_EXPORTED = 2; // 0x2
+    field public static final int RECEIVER_NOT_EXPORTED = 4; // 0x4
     field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
     field public static final String RESTRICTIONS_SERVICE = "restrictions";
     field public static final String ROLE_SERVICE = "role";
@@ -50712,6 +50717,9 @@
     method public void setPackageName(CharSequence);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
     field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
     field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
     field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -51010,6 +51018,9 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_COPY;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CUT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_CANCEL;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_DROP;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_START;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 96476a5..4f03b4f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -8411,7 +8411,7 @@
      * @hide
      */
     public final boolean isVisibleForAutofill() {
-        return mStopped;
+        return !mStopped;
     }
 
     /**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8cf1f80..4e4c2219 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -236,14 +236,14 @@
     void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
     void clearCrossProfileIntentFilters(in ComponentName admin);
 
-    boolean setPermittedAccessibilityServices(in ComponentName admin,in List packageList);
-    List getPermittedAccessibilityServices(in ComponentName admin);
-    List getPermittedAccessibilityServicesForUser(int userId);
+    boolean setPermittedAccessibilityServices(in ComponentName admin,in List<String> packageList);
+    List<String> getPermittedAccessibilityServices(in ComponentName admin);
+    List<String> getPermittedAccessibilityServicesForUser(int userId);
     boolean isAccessibilityServicePermittedByAdmin(in ComponentName admin, String packageName, int userId);
 
-    boolean setPermittedInputMethods(in ComponentName admin,in List packageList, boolean parent);
-    List getPermittedInputMethods(in ComponentName admin, boolean parent);
-    List getPermittedInputMethodsForCurrentUser();
+    boolean setPermittedInputMethods(in ComponentName admin,in List<String> packageList, boolean parent);
+    List<String> getPermittedInputMethods(in ComponentName admin, boolean parent);
+    List<String> getPermittedInputMethodsForCurrentUser();
     boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId, boolean parent);
 
     boolean setPermittedCrossProfileNotificationListeners(in ComponentName admin, in List<String> packageList);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ad1163f..f5a0c43 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -534,8 +534,8 @@
                     | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
-            RECEIVER_VISIBLE_TO_INSTANT_APPS
+    @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE" }, value = {
+            RECEIVER_VISIBLE_TO_INSTANT_APPS, RECEIVER_EXPORTED, RECEIVER_NOT_EXPORTED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface RegisterReceiverFlags {}
@@ -546,6 +546,18 @@
     public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 0x1;
 
     /**
+     * Flag for {@link #registerReceiver}: The receiver can receive broadcasts from other Apps.
+     * Has the same behavior as marking a statically registered receiver with "exported=true"
+     */
+    public static final int RECEIVER_EXPORTED = 0x2;
+
+    /**
+     * Flag for {@link #registerReceiver}: The receiver cannot receive broadcasts from other Apps.
+     * Has the same behavior as marking a statically registered receiver with "exported=false"
+     */
+    public static final int RECEIVER_NOT_EXPORTED = 0x4;
+
+    /**
      * Returns an AssetManager instance for the application's package.
      * <p>
      * <strong>Note:</strong> Implementations of this method should return
@@ -2989,8 +3001,12 @@
      *
      * @param receiver The BroadcastReceiver to handle the broadcast.
      * @param filter Selects the Intent broadcasts to be received.
-     * @param flags Additional options for the receiver. May be 0 or
-     *      {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}.
+     * @param flags Additional options for the receiver. As of
+     * {@link android.os.Build.VERSION_CODES#TIRAMISU}, either {@link #RECEIVER_EXPORTED} or
+     * {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
+     *            for protected broadcasts, and may additionally specify
+     *            {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS} if {@link #RECEIVER_EXPORTED} is
+     *            specified.
      *
      * @return The first sticky intent found that matches <var>filter</var>,
      *         or null if there are none.
@@ -3062,8 +3078,12 @@
      *      no permission is required.
      * @param scheduler Handler identifying the thread that will receive
      *      the Intent.  If null, the main thread of the process will be used.
-     * @param flags Additional options for the receiver. May be 0 or
-     *      {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}.
+     * @param flags Additional options for the receiver. As of
+     * {@link android.os.Build.VERSION_CODES#TIRAMISU}, either {@link #RECEIVER_EXPORTED} or
+     * {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
+     *            for protected broadcasts, and may additionally specify
+     *            {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS} if {@link #RECEIVER_EXPORTED} is
+     *            specified.
      *
      * @return The first sticky intent found that matches <var>filter</var>,
      *         or null if there are none.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3253246..546abf8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -30,6 +30,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.bluetooth.BluetoothDevice;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -8281,7 +8282,10 @@
      *         needed.
      */
     public @Nullable String resolveTypeIfNeeded(@NonNull ContentResolver resolver) {
-        if (mComponent != null) {
+        // Match logic in PackageManagerService#applyEnforceIntentFilterMatching(...)
+        if (mComponent != null && (Process.myUid() == Process.ROOT_UID
+                || Process.myUid() == Process.SYSTEM_UID
+                || mComponent.getPackageName().equals(ActivityThread.currentPackageName()))) {
             return mType;
         }
         return resolveType(resolver);
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index e319d2c..a0f3d7a 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -40,7 +40,7 @@
      *         requested user, an empty map is returned.
      */
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    Map getAllOverlays(in int userId);
+    Map<String, List<OverlayInfo>> getAllOverlays(in int userId);
 
     /**
      * Returns information about all overlays for the given target package for
@@ -52,7 +52,7 @@
      * @return A list of OverlayInfo objects; if no overlays exist for the
      *         requested package, an empty list is returned.
      */
-    List getOverlayInfosForTarget(in String targetPackageName, in int userId);
+    List<OverlayInfo> getOverlayInfosForTarget(in String targetPackageName, in int userId);
 
     /**
      * Returns information about the overlay with the given package name for the
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 5a89708..76e9fcb 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -343,7 +343,7 @@
     }
 
     public boolean isDemo() {
-        return UserManager.isUserTypeDemo(userType);
+        return UserManager.isUserTypeDemo(userType) || (flags & FLAG_DEMO) != 0;
     }
 
     public boolean isFull() {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 45bdb11..c1d5a28 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1051,9 +1051,8 @@
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
-            conversationSenders = getNotificationPolicySenders(
-                    zenPolicy.getPriorityConversationSenders(),
-                    conversationSenders);
+            conversationSenders = getConversationSendersWithDefault(
+                    zenPolicy.getPriorityConversationSenders(), conversationSenders);
         }
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
@@ -1166,6 +1165,17 @@
         }
     }
 
+    private int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
+            int defaultPolicySender) {
+        switch (senders) {
+            case ZenPolicy.CONVERSATION_SENDERS_ANYONE:
+            case ZenPolicy.CONVERSATION_SENDERS_IMPORTANT:
+            case ZenPolicy.CONVERSATION_SENDERS_NONE:
+                return senders;
+            default:
+                return defaultPolicySender;
+        }
+    }
 
     /**
      * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType
@@ -1216,7 +1226,8 @@
         }
         priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
         priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
-        priorityConversationSenders = allowConversationsFrom;
+        priorityConversationSenders = getConversationSendersWithDefault(
+                allowConversationsFrom, priorityConversationSenders);
 
         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
                 suppressedVisualEffects, areChannelsBypassingDnd
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 4816d2c..2b579d0 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -47,6 +47,11 @@
      */
     @Nullable
     private IBinder windowToken;
+    /**
+     * Used to cache IWindow from the windowToken so we don't need to convert every time getWindow
+     * is called.
+     */
+    private IWindow window;
 
     // The window name.
     public String name;
@@ -145,7 +150,7 @@
                 .append(", visible=").append(visible)
                 .append(", scaleFactor=").append(scaleFactor)
                 .append(", transform=").append(transform)
-                .append(", windowToken=").append(getWindow())
+                .append(", windowToken=").append(windowToken)
                 .toString();
 
     }
@@ -180,9 +185,14 @@
 
     public void setWindowToken(IWindow iwindow) {
         windowToken = iwindow.asBinder();
+        window = iwindow;
     }
 
     public IWindow getWindow() {
-        return IWindow.Stub.asInterface(windowToken);
+        if (window != null) {
+            return window;
+        }
+        window = IWindow.Stub.asInterface(windowToken);
+        return window;
     }
 }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 47546848..eb6bc58 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -320,6 +320,19 @@
     int TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE = 27;
 
     /**
+     * A window in a new task fragment is being opened.
+     * @hide
+     */
+    int TRANSIT_OLD_TASK_FRAGMENT_OPEN = 28;
+
+    /**
+     * A window in the top-most activity of task fragment is being closed to reveal the activity
+     * below.
+     * @hide
+     */
+    int TRANSIT_OLD_TASK_FRAGMENT_CLOSE = 29;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_OLD_" }, value = {
@@ -344,7 +357,9 @@
             TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
             TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
             TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
-            TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE
+            TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
+            TRANSIT_OLD_TASK_FRAGMENT_OPEN,
+            TRANSIT_OLD_TASK_FRAGMENT_CLOSE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionOldType {}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 40da86a..ff6f8ac 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -613,6 +613,36 @@
     public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000040;
 
     /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * A drag has started while accessibility is enabled. This is either via an
+     * AccessibilityAction, or via touch events. This is sent from the source that initiated the
+     * drag.
+     *
+     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START
+     */
+    public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 0x00000080;
+
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * A drag in with accessibility enabled has ended. This means the content has been
+     * successfully dropped. This is sent from the target that accepted the dragged content.
+     *
+     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_DROP
+     */
+    public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0x00000100;
+
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * A drag in with accessibility enabled has ended. This means the content has been
+     * unsuccessfully dropped, the user has canceled the action via an AccessibilityAction, or
+     * no drop has been detected within a timeout and the drag was automatically cancelled. This is
+     * sent from the source that initiated the drag.
+     *
+     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL
+     */
+    public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x0000200;
+
+    /**
      * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
      * The window was added.
      */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 085eb81..587a270 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -27,6 +27,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ClipData;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Build;
@@ -4353,6 +4354,14 @@
             case R.id.accessibilityActionImeEnter:
                 return "ACTION_IME_ENTER";
             default:
+                // TODO(197520937): Use finalized constants in switch
+                if (action == R.id.accessibilityActionDragStart) {
+                    return "ACTION_DRAG";
+                } else if (action == R.id.accessibilityActionDragCancel) {
+                    return "ACTION_CANCEL_DRAG";
+                } else if (action == R.id.accessibilityActionDragDrop) {
+                    return "ACTION_DROP";
+                }
                 return "ACTION_UNKNOWN";
         }
     }
@@ -4995,6 +5004,46 @@
         @NonNull public static final AccessibilityAction ACTION_IME_ENTER =
                 new AccessibilityAction(R.id.accessibilityActionImeEnter);
 
+        /**
+         * Action to start a drag.
+         * <p>
+         * This action initiates a drag & drop within the system. The source's dragged content is
+         * prepared before the drag begins. In View, this action should prepare the arguments to
+         * {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} and then
+         * call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}. The
+         * equivalent should be performed for other UI toolkits.
+         * </p>
+         *
+         * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED
+         */
+        @NonNull public static final AccessibilityAction ACTION_DRAG_START =
+                new AccessibilityAction(R.id.accessibilityActionDragStart);
+
+        /**
+         * Action to trigger a drop of the content being dragged.
+         * <p>
+         * This action is added to potential drop targets if the source started a drag with
+         * {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted
+         * {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an
+         * {@link View.OnDragListener}.
+         * </p>
+         *
+         * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED
+         */
+        @NonNull public static final AccessibilityAction ACTION_DRAG_DROP =
+                new AccessibilityAction(R.id.accessibilityActionDragDrop);
+
+        /**
+         * Action to cancel a drag.
+         * <p>
+         * This action is added to the source that started a drag with {@link #ACTION_DRAG_START}.
+         * </p>
+         *
+         * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_CANCELLED
+         */
+        @NonNull public static final AccessibilityAction ACTION_DRAG_CANCEL =
+                new AccessibilityAction(R.id.accessibilityActionDragCancel);
+
         private final int mActionId;
         private final CharSequence mLabel;
 
diff --git a/core/java/android/view/autofill/AutofillClientController.java b/core/java/android/view/autofill/AutofillClientController.java
index 7056444..c47f6f7 100644
--- a/core/java/android/view/autofill/AutofillClientController.java
+++ b/core/java/android/view/autofill/AutofillClientController.java
@@ -324,7 +324,7 @@
 
     @Override
     public boolean autofillClientIsVisibleForAutofill() {
-        return !mActivity.isVisibleForAutofill();
+        return mActivity.isVisibleForAutofill();
     }
 
     @Override
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index bca647a..f5c4b1f 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -20,6 +20,10 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Bundle;
+
+import java.util.Map;
+import java.util.HashMap;
 
 /** Class for capability discovery information.
  *  @hide */
@@ -88,6 +92,95 @@
     /** Time used to compute when to query again. */
     private long mCapTimestamp = 0;
 
+    private Map<String, String> mCapInfoMap = new HashMap<String, String>();
+
+    /** IM session feature tag key. */
+    public static final String INSTANT_MSG =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.im\"";
+    /** File transfer feature tag key. */
+    public static final String FILE_TRANSFER =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.ft\"";
+    /** File transfer Thumbnail feature tag key. */
+    public static final String FILE_TRANSFER_THUMBNAIL =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftthumb\"";
+    /** File transfer Store and forward feature tag key. */
+    public static final String FILE_TRANSFER_SNF =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftstandfw\"";
+    /** File transfer HTTP feature tag key. */
+    public static final String FILE_TRANSFER_HTTP =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"";
+    /** Image sharing feature tag key. */
+    public static final String IMAGE_SHARE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.gsma-is\"";
+    /** Video sharing during a CS call feature tag key-- IR-74. */
+    public static final String VIDEO_SHARE_DURING_CS = "+g.3gpp.cs-voice";
+    /** Video sharing outside of voice call feature tag key-- IR-84. */
+    public static final String VIDEO_SHARE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.gsma-vs\"";
+    /** Social presence feature tag key. */
+    public static final String SOCIAL_PRESENCE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.sp\"";
+    /** Presence discovery feature tag key. */
+    public static final String CAPDISC_VIA_PRESENCE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.dp\"";
+    /** IP voice call feature tag key (IR-92/IR-58). */
+    public static final String IP_VOICE =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
+    /** IP video call feature tag key (IR-92/IR-58). */
+    public static final String IP_VIDEO =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\";video";
+    /** IP Geo location Pull using File Transfer feature tag key. */
+    public static final String GEOPULL_FT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopullft\"";
+    /** IP Geo location Pull feature tag key. */
+    public static final String GEOPULL =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopull\"";
+    /** IP Geo location Push feature tag key. */
+    public static final String GEOPUSH =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopush\"";
+    /** Standalone messaging feature tag key. */
+    public static final String STANDALONE_MSG =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg;" +
+      "urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"";
+    /** Full Store and Forward Group Chat information feature tag key. */
+    public static final String FULL_SNF_GROUPCHAT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fullsfgroupchat\"";
+    /** RCS IP Voice call feature tag key.  */
+    public static final String RCS_IP_VOICE_CALL =
+      "+g.gsma.rcs.ipcall";
+    /** RCS IP Video call feature tag key.  */
+    public static final String RCS_IP_VIDEO_CALL =
+      "+g.gsma.rcs.ipvideocall";
+    /** RCS IP Video only call feature tag key.  */
+    public static final String RCS_IP_VIDEO_ONLY_CALL =
+      "+g.gsma.rcs.ipvideoonlycall";
+    /** IP Geo location Push using SMS feature tag key. */
+    public static final String GEOSMS =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.geosms\"";
+    /** RCS call composer feature tag key. */
+    public static final String CALLCOMPOSER =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.callcomposer\"";
+    /** RCS post-call feature tag key. */
+    public static final String POSTCALL =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.callunanswered\"";
+    /** Shared map feature tag key. */
+    public static final String SHAREDMAP =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.sharedmap\"";
+    /** Shared Sketch feature tag key. */
+    public static final String SHAREDSKETCH =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.sharedsketch\"";
+    /** Chatbot communication feature tag key. */
+    public static final String CHATBOT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.chatbot\"";
+    /** Chatbot role feature tag key. */
+    public static final String CHATBOTROLE = "+g.gsma.rcs.isbot";
+    /** Standalone Chatbot communication feature tag key. */
+    public static final String STANDALONE_CHATBOT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot.sa\"";
+    /** MMtel based call composer feature tag key. */
+    public static final String MMTEL_CALLCOMPOSER = "+g.gsma.callcomposer";
+
+
 
     /**
      * Constructor for the CapInfo class.
@@ -99,6 +192,7 @@
 
     /**
      * Checks whether IM is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isImSupported() {
@@ -107,6 +201,7 @@
 
     /**
      * Sets IM as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setImSupported(boolean imSupported) {
@@ -115,6 +210,7 @@
 
     /**
      * Checks whether FT Thumbnail is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFtThumbSupported() {
@@ -123,16 +219,16 @@
 
     /**
      * Sets FT thumbnail as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFtThumbSupported(boolean ftThumbSupported) {
         this.mFtThumbSupported = ftThumbSupported;
     }
 
-
-
     /**
      * Checks whether FT Store and Forward is supported
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFtSnFSupported() {
@@ -141,6 +237,7 @@
 
     /**
      * Sets FT Store and Forward as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFtSnFSupported(boolean  ftSnFSupported) {
@@ -149,6 +246,7 @@
 
    /**
     * Checks whether File transfer HTTP is supported.
+    * @deprecated Use {@link #isCapabilitySupported(String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public boolean isFtHttpSupported() {
@@ -157,6 +255,7 @@
 
    /**
     * Sets File transfer HTTP as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void setFtHttpSupported(boolean  ftHttpSupported) {
@@ -165,6 +264,7 @@
 
     /**
      * Checks whether FT is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFtSupported() {
@@ -173,6 +273,7 @@
 
     /**
      * Sets FT as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFtSupported(boolean ftSupported) {
@@ -181,6 +282,7 @@
 
     /**
      * Checks whether IS is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isIsSupported() {
@@ -189,6 +291,7 @@
 
     /**
      * Sets IS as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setIsSupported(boolean isSupported) {
@@ -197,6 +300,7 @@
 
     /**
      * Checks whether video sharing is supported during a CS call.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isVsDuringCSSupported() {
@@ -204,8 +308,9 @@
     }
 
     /**
-     *  Sets video sharing as supported or not supported during a CS
-     *  call.
+     * Sets video sharing as supported or not supported during a CS
+     * call.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setVsDuringCSSupported(boolean vsDuringCSSupported) {
@@ -213,8 +318,9 @@
     }
 
     /**
-     *  Checks whether video sharing outside a voice call is
-     *   supported.
+     * Checks whether video sharing outside a voice call is
+     *  supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isVsSupported() {
@@ -223,6 +329,7 @@
 
     /**
      * Sets video sharing as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setVsSupported(boolean vsSupported) {
@@ -231,6 +338,7 @@
 
     /**
      * Checks whether social presence is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isSpSupported() {
@@ -239,6 +347,7 @@
 
     /**
      * Sets social presence as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setSpSupported(boolean spSupported) {
@@ -248,6 +357,7 @@
     /**
      * Checks whether capability discovery via presence is
      * supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isCdViaPresenceSupported() {
@@ -257,6 +367,7 @@
     /**
      * Sets capability discovery via presence as supported or not
      * supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setCdViaPresenceSupported(boolean cdViaPresenceSupported) {
@@ -265,6 +376,7 @@
 
     /**
      * Checks whether IP voice call is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isIpVoiceSupported() {
@@ -273,6 +385,7 @@
 
     /**
      * Sets IP voice call as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setIpVoiceSupported(boolean ipVoiceSupported) {
@@ -281,6 +394,7 @@
 
     /**
      * Checks whether IP video call is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isIpVideoSupported() {
@@ -289,6 +403,7 @@
 
     /**
      * Sets IP video call as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setIpVideoSupported(boolean ipVideoSupported) {
@@ -298,6 +413,7 @@
    /**
     * Checks whether Geo location Pull using File Transfer is
     * supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public boolean isGeoPullFtSupported() {
@@ -307,6 +423,7 @@
    /**
     * Sets Geo location Pull using File Transfer as supported or
     * not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void setGeoPullFtSupported(boolean geoPullFtSupported) {
@@ -315,6 +432,7 @@
 
     /**
      * Checks whether Geo Pull is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isGeoPullSupported() {
@@ -323,6 +441,7 @@
 
     /**
      * Sets Geo Pull as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setGeoPullSupported(boolean geoPullSupported) {
@@ -331,6 +450,7 @@
 
     /**
      * Checks whether Geo Push is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isGeoPushSupported() {
@@ -339,6 +459,7 @@
 
     /**
      * Sets Geo Push as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setGeoPushSupported(boolean geoPushSupported) {
@@ -347,6 +468,7 @@
 
     /**
      * Checks whether short messaging is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isSmSupported() {
@@ -355,6 +477,7 @@
 
     /**
      * Sets short messaging as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setSmSupported(boolean smSupported) {
@@ -363,22 +486,32 @@
 
     /**
      * Checks whether store/forward and group chat are supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFullSnFGroupChatSupported() {
         return mFullSnFGroupChatSupported;
     }
 
+    /**
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isRcsIpVoiceCallSupported() {
         return mRcsIpVoiceCallSupported;
     }
 
+    /**
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isRcsIpVideoCallSupported() {
         return mRcsIpVideoCallSupported;
     }
 
+    /**
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isRcsIpVideoOnlyCallSupported() {
         return mRcsIpVideoOnlyCallSupported;
@@ -386,20 +519,32 @@
 
     /**
      * Sets store/forward and group chat supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFullSnFGroupChatSupported(boolean fullSnFGroupChatSupported) {
         this.mFullSnFGroupChatSupported = fullSnFGroupChatSupported;
     }
 
+    /**
+     * @deprecated Use {@link #addCapability(String, String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setRcsIpVoiceCallSupported(boolean rcsIpVoiceCallSupported) {
         this.mRcsIpVoiceCallSupported = rcsIpVoiceCallSupported;
     }
+
+    /**
+     * @deprecated Use {@link #addCapability(String, String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setRcsIpVideoCallSupported(boolean rcsIpVideoCallSupported) {
         this.mRcsIpVideoCallSupported = rcsIpVideoCallSupported;
     }
+
+    /**
+     * @deprecated Use {@link #addCapability(String, String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setRcsIpVideoOnlyCallSupported(boolean rcsIpVideoOnlyCallSupported) {
         this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported;
@@ -407,6 +552,7 @@
 
     /**
      * Checks whether Geo Push via SMS is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isGeoSmsSupported() {
         return mGeoSmsSupported;
@@ -414,6 +560,7 @@
 
     /**
      * Sets Geolocation Push via SMS as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setGeoSmsSupported(boolean geoSmsSupported) {
          this.mGeoSmsSupported = geoSmsSupported;
@@ -421,6 +568,7 @@
 
     /**
      * Checks whether RCS call composer is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isCallComposerSupported() {
         return mCallComposerSupported;
@@ -428,6 +576,7 @@
 
     /**
      * Sets call composer as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setCallComposerSupported(boolean callComposerSupported) {
         this.mCallComposerSupported = callComposerSupported;
@@ -435,6 +584,7 @@
 
     /**
      * Checks whether post call is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isPostCallSupported(){
         return mPostCallSupported;
@@ -442,13 +592,15 @@
 
     /**
      * Sets post call as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
-     public void setPostCallSupported(boolean postCallSupported) {
-         this.mPostCallSupported = postCallSupported;
-     }
+    public void setPostCallSupported(boolean postCallSupported) {
+        this.mPostCallSupported = postCallSupported;
+    }
 
     /**
      * Checks whether shared map is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isSharedMapSupported() {
         return mSharedMapSupported;
@@ -456,6 +608,7 @@
 
     /**
      * Sets shared map as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setSharedMapSupported(boolean sharedMapSupported) {
         this.mSharedMapSupported = sharedMapSupported;
@@ -463,6 +616,7 @@
 
     /**
      * Checks whether shared sketch is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isSharedSketchSupported() {
         return mSharedSketchSupported;
@@ -470,6 +624,7 @@
 
     /**
      * Sets shared sketch as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setSharedSketchSupported(boolean sharedSketchSupported) {
         this.mSharedSketchSupported = sharedSketchSupported;
@@ -477,6 +632,7 @@
 
     /**
      * Checks whether chatbot communication is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isChatbotSupported() {
         return mChatbotSupported;
@@ -484,6 +640,7 @@
 
     /**
      * Sets chatbot communication as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setChatbotSupported(boolean chatbotSupported) {
         this.mChatbotSupported = chatbotSupported;
@@ -491,6 +648,7 @@
 
     /**
      * Checks whether chatbot role is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isChatbotRoleSupported() {
         return mChatbotRoleSupported;
@@ -498,6 +656,7 @@
 
     /**
      * Sets chatbot role as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setChatbotRoleSupported(boolean chatbotRoleSupported) {
         this.mChatbotRoleSupported = chatbotRoleSupported;
@@ -505,6 +664,7 @@
 
     /**
      * Checks whether standalone chatbot communication is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isSmChatbotSupported() {
         return mSmChatbotSupported;
@@ -512,6 +672,7 @@
 
     /**
      * Sets standalone chatbot communication as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setSmChatbotSupported(boolean smChatbotSupported) {
         this.mSmChatbotSupported = smChatbotSupported;
@@ -519,6 +680,7 @@
 
     /**
      * Checks whether Mmtel based call composer is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isMmtelCallComposerSupported() {
         return mMmtelCallComposerSupported;
@@ -526,6 +688,7 @@
 
     /**
      * Sets Mmtel based call composer as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setMmtelCallComposerSupported(boolean mmtelCallComposerSupported) {
         this.mMmtelCallComposerSupported = mmtelCallComposerSupported;
@@ -555,6 +718,84 @@
         this.mCapTimestamp = capTimestamp;
     }
 
+    /**
+     * Adds the feature tag string with supported versions to
+     * the mCapInfoMap.
+     * Map<String featureType, String versions>
+     * Versions format:
+     *    "+g.gsma.rcs.botversion=\"#=1"        -> Version 1 supported
+     *    "+g.gsma.rcs.botversion=\"#=1,#=2\""  -> Versions 1 and 2 are supported
+     *
+     * Example #1: Add standard feature tag with one version support
+     * addCapability(CapInfo.STANDALONE_CHATBOT, "+g.gsma.rcs.botversion=\"#=1");
+     * The above example indicates standalone chatbot feature tag is supported
+     * in version 1.
+     *
+     * Example #2: Add standard feature tag with multiple version support
+     * addCapability(CapInfo.CHATBOT, "+g.gsma.rcs.botversion=\"#=1,#=2\"");
+     * The above example indicates session based chatbot feature tag is supported
+     * in versions 1 and 2.
+     *
+     * Example #3: Add standard feature tag with no version support
+     * addCapability(CapInfo.INSTANT_MSG, "");
+     * The above example indicates im feature tag does not have version support.
+     *
+     * Example #4: Add custom/extension feature tag with no version support
+     * addCapability("+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.custom_im\"",
+     *               "");
+     * Call setNewFeatureTag(int presenceServiceHdl, String featureTag,
+     *           in PresServiceInfo serviceInfo, int userData) API
+     * in IPresenceService.aidl before calling addCapability() API
+     */
+    public void addCapability(String featureTagName, String versions) {
+        this.mCapInfoMap.put(featureTagName, versions);
+    }
+
+    /**
+     * Returns String of versions of the feature tag passed.
+     * Returns "" if versioning support is not present for the feature tag passed.
+     * Returns null if feature tag is not present.
+     *
+     * Example # 1:
+     * getCapabilityVersions(CapInfo.STANDALONE_CHATBOT);
+     * The above returns String in this format "+g.gsma.rcs.botversion=\"#=1,#=2\"",
+     * indicating more than one versions are supported for standalone chatbot feature tag
+     *
+     * Example # 2:
+     * getCapabilityVersions(CapInfo.INSTANT_MSG);
+     * The above returns empty String in this format "",
+     * indicating versions support is not present for im feature tag
+     *
+     * Example #3:
+     * getCapabilityVersions(
+     *   "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.custom_im\");
+     * The above returns String "",
+     * indicating version supported is not present for the custom feature tag passed.
+     */
+    public String getCapabilityVersions(String featureTagName) {
+        return mCapInfoMap.get(featureTagName);
+    }
+
+    /** Removes the entry of the feature tag passed, from the Map. */
+    public void removeCapability(String featureTagName) {
+        this.mCapInfoMap.remove(featureTagName);
+    }
+
+    /** Sets Map of feature tag string and string of supported versions. */
+    public void setCapInfoMap(Map<String, String> capInfoMap) {
+        this.mCapInfoMap = capInfoMap;
+    }
+
+    /** Gets Map of feature tag string and string of supported versions. */
+    public Map<String, String> getCapInfoMap() {
+        return mCapInfoMap;
+    }
+
+    /** Checks whether the featureTag is supported or not. */
+    public boolean isCapabilitySupported(String featureTag) {
+       return mCapInfoMap.containsKey(featureTag);
+    }
+
     public int describeContents() {
         // TODO Auto-generated method stub
         return 0;
@@ -594,6 +835,12 @@
         dest.writeInt(mRcsIpVideoOnlyCallSupported ? 1 : 0);
         dest.writeStringArray(mExts);
         dest.writeLong(mCapTimestamp);
+
+        Bundle capInfoBundle = new Bundle();
+        for (Map.Entry<String, String> entry : mCapInfoMap.entrySet()) {
+          capInfoBundle.putString(entry.getKey(), entry.getValue());
+        }
+        dest.writeBundle(capInfoBundle);
     }
 
     public static final Parcelable.Creator<CapInfo> CREATOR = new Parcelable.Creator<CapInfo>() {
@@ -646,5 +893,10 @@
 
         mExts = source.createStringArray();
         mCapTimestamp = source.readLong();
+
+        Bundle capInfoBundle = source.readBundle();
+        for (String key: capInfoBundle.keySet()) {
+          mCapInfoMap.put(key, capInfoBundle.getString(key));
+        }
     }
 }
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index acea0f0..3242081 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -30,6 +30,7 @@
     private int mSipResponseCode = 0;
     private int mRetryAfter = 0;
     private String mReasonPhrase = "";
+    private String mReasonHeader = "";
 
     /**
      * Gets the Options command ID.
@@ -117,6 +118,22 @@
     }
 
     /**
+     * Gets the reason header associated with the SIP response code.
+     * @hide
+     */
+    public String getReasonHeader() {
+        return mReasonHeader;
+    }
+
+    /**
+     * Sets the SIP response code reason phrase.
+     * @hide
+     */
+    public void setReasonHeader(String reasonHeader) {
+        this.mReasonHeader = reasonHeader;
+    }
+
+    /**
      * Constructor for the OptionsSipResponse class.
      * @hide
      */
@@ -138,6 +155,7 @@
         dest.writeString(mReasonPhrase);
         dest.writeParcelable(mCmdId, flags);
         dest.writeInt(mRetryAfter);
+        dest.writeString(mReasonHeader);
     }
 
     /** @hide */
@@ -164,5 +182,6 @@
         mReasonPhrase = source.readString();
         mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader());
         mRetryAfter = source.readInt();
+        mReasonHeader = source.readString();
     }
 }
diff --git a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 9549152..5e394ef 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -29,6 +29,7 @@
     private int mSipResponseCode = 0;
     private int mRetryAfter = 0;
     private String mReasonPhrase = "";
+    private String mReasonHeader = "";
 
     /**
      * Gets the Presence command ID.
@@ -123,6 +124,23 @@
     }
 
     /**
+     * Gets the reason header associated with the SIP response
+     * code.
+     * @hide
+     */
+    public String getReasonHeader() {
+        return mReasonHeader;
+    }
+
+    /**
+     * Sets the SIP response code reason header.
+     * @hide
+     */
+    public void setReasonHeader(String reasonHeader) {
+        this.mReasonHeader = reasonHeader;
+    }
+
+    /**
      * Constructor for the PresSipResponse class.
      * @hide
      */
@@ -141,6 +159,7 @@
         dest.writeString(mReasonPhrase);
         dest.writeParcelable(mCmdId, flags);
         dest.writeInt(mRetryAfter);
+        dest.writeString(mReasonHeader);
     }
 
     /** @hide */
@@ -168,5 +187,6 @@
         mReasonPhrase = source.readString();
         mCmdId = source.readParcelable(PresCmdId.class.getClassLoader());
         mRetryAfter = source.readInt();
+        mReasonHeader = source.readString();
     }
 }
\ No newline at end of file
diff --git a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
index 34a7b1e..ce3d568 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
@@ -27,6 +27,7 @@
     private String mFeatureTag = "";
     private String mContactUri = "";
     private String mTimestamp = "";
+    private String mVersion = "";
 
 
     /**
@@ -80,6 +81,22 @@
     }
 
     /**
+     * Gets the version.
+     * @hide
+     */
+    public String getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Sets the version.
+     * @hide
+     */
+    public void setVersion(String version) {
+        this.mVersion = version;
+    }
+
+    /**
      * Constructor for the PresTupleInfo class.
      * @hide
      */
@@ -96,6 +113,7 @@
         dest.writeString(mFeatureTag);
         dest.writeString(mContactUri);
         dest.writeString(mTimestamp);
+        dest.writeString(mVersion);
     }
 
     /** @hide */
@@ -121,5 +139,6 @@
         mFeatureTag = source.readString();
         mContactUri = source.readString();
         mTimestamp = source.readString();
+        mVersion = source.readString();
     }
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 02cd8db..35d25ca 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -458,56 +458,19 @@
 
     private class ChooserHandler extends Handler {
         private static final int CHOOSER_TARGET_SERVICE_RESULT = 1;
-        private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT = 2;
-        private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT = 3;
         private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 4;
         private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5;
         private static final int LIST_VIEW_UPDATE_MESSAGE = 6;
 
-        private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 1000;
-        private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 300;
-
-        private boolean mMinTimeoutPassed = false;
         private boolean mReceivedDirectShareTargets = false;
 
         private void removeAllMessages() {
             removeMessages(LIST_VIEW_UPDATE_MESSAGE);
-            removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT);
-            removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT);
             removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
             removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT);
             removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED);
         }
 
-        private void restartServiceRequestTimer() {
-            mMinTimeoutPassed = false;
-            removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT);
-            removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT);
-
-            if (DEBUG) {
-                Log.d(TAG, "queryTargets setting watchdog timer for "
-                        + WATCHDOG_TIMEOUT_MAX_MILLIS + "ms");
-            }
-
-            sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT,
-                    WATCHDOG_TIMEOUT_MIN_MILLIS);
-            sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT,
-                    WATCHDOG_TIMEOUT_MAX_MILLIS);
-        }
-
-        private void maybeStopServiceRequestTimer() {
-            // Set a minimum timeout threshold, to ensure both apis, sharing shortcuts
-            // and older-style direct share services, have had time to load, otherwise
-            // just checking mServiceConnections could force us to end prematurely
-            if (mMinTimeoutPassed && mServiceConnections.isEmpty() && mReceivedDirectShareTargets) {
-                logDirectShareTargetReceived(
-                        MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE);
-                sendVoiceChoicesIfNeeded();
-                mChooserMultiProfilePagerAdapter.getActiveListAdapter()
-                        .completeServiceTargetLoading();
-            }
-        }
-
         @Override
         public void handleMessage(Message msg) {
             if (mChooserMultiProfilePagerAdapter.getActiveListAdapter() == null || isDestroyed()) {
@@ -532,7 +495,7 @@
                         if (adapterForUserHandle != null) {
                             adapterForUserHandle.addServiceResults(sri.originalTarget,
                                     sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET,
-                                    /* directShareShortcutInfoCache */ null, mServiceConnections);
+                                    /* directShareShortcutInfoCache */ null);
                             if (!sri.resultTargets.isEmpty() && sri.originalTarget != null) {
                                 mChooserTargetComponentNameCache.put(
                                         sri.resultTargets.get(0).getComponentName(),
@@ -543,21 +506,6 @@
                     unbindService(sri.connection);
                     sri.connection.destroy();
                     mServiceConnections.remove(sri.connection);
-                    maybeStopServiceRequestTimer();
-                    break;
-
-                case CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT:
-                    mMinTimeoutPassed = true;
-                    maybeStopServiceRequestTimer();
-                    break;
-
-                case CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT:
-                    mMinTimeoutPassed = true;
-                    if (!mServiceConnections.isEmpty()) {
-                        getChooserActivityLogger().logSharesheetDirectLoadTimeout();
-                    }
-                    unbindRemainingServices();
-                    maybeStopServiceRequestTimer();
                     break;
 
                 case LIST_VIEW_UPDATE_MESSAGE:
@@ -580,20 +528,19 @@
                         if (adapterForUserHandle != null) {
                             adapterForUserHandle.addServiceResults(
                                     resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1,
-                                    mDirectShareShortcutInfoCache, mServiceConnections);
+                                    mDirectShareShortcutInfoCache);
                         }
                     }
                     break;
 
                 case SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED:
-                    mReceivedDirectShareTargets = true;
-
                     logDirectShareTargetReceived(
                             MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER);
                     sendVoiceChoicesIfNeeded();
                     getChooserActivityLogger().logSharesheetDirectLoadComplete();
 
-                    maybeStopServiceRequestTimer();
+                    mChooserMultiProfilePagerAdapter.getActiveListAdapter()
+                            .completeServiceTargetLoading();
                     break;
 
                 default:
@@ -1702,7 +1649,7 @@
                     /* origTarget */ null,
                     Lists.newArrayList(mCallerChooserTargets),
                     TARGET_TYPE_DEFAULT,
-                    /* directShareShortcutInfoCache */ null, mServiceConnections);
+                    /* directShareShortcutInfoCache */ null);
         }
     }
 
@@ -2028,8 +1975,6 @@
                 break;
             }
         }
-
-        mChooserHandler.restartServiceRequestTimer();
     }
 
     private IntentFilter getTargetIntentFilter() {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 87737ca..07c4050 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -491,9 +491,7 @@
      */
     public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
             @ChooserActivity.ShareTargetType int targetType,
-            Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos,
-            List<ChooserActivity.ChooserTargetServiceConnection>
-                    pendingChooserTargetServiceConnections) {
+            Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos) {
         if (DEBUG) {
             Log.d(TAG, "addServiceResults " + origTarget.getResolvedComponentName() + ", "
                     + targets.size()
diff --git a/core/java/com/android/internal/app/TEST_MAPPING b/core/java/com/android/internal/app/TEST_MAPPING
index 8bd7912..08e1d57 100644
--- a/core/java/com/android/internal/app/TEST_MAPPING
+++ b/core/java/com/android/internal/app/TEST_MAPPING
@@ -3,17 +3,14 @@
     {
       "name": "CtsSuspendAppsTestCases",
       "file_patterns": ["(/|^)SuspendedAppActivity\\.java"]
-    }
-  ],
-  "postsubmit": [
+    },
     {
       "name": "FrameworksCoreTests",
       "options": [
         {
         "include-filter": "com.android.internal.app."
         },
-
-        // Many tests failing - do not enable for continuous execution 
+        // Exclude currently failing tests from presubmit
         {
         "exclude-filter": "com.android.internal.app.IntentForwarderActivityTest"
         },
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
new file mode 100644
index 0000000..1a3b29d
--- /dev/null
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Performs per-state counting of multi-element values over time. The class' behavior is illustrated
+ * by this example:
+ * <pre>
+ *   // At 0 ms, the state of the tracked object is 0
+ *   counter.setState(0, 0);
+ *
+ *   // At 1000 ms, the state changes to 1
+ *   counter.setState(1, 1000);
+ *
+ *   // At 3000 ms, the tracked values are updated to {100, 200}
+ *   arrayContainer.setValues(new long[]{{30, 300}};
+ *   counter.updateValues(arrayContainer, 3000);
+ *
+ *   // The values are distributed between states 0 and 1 according to the time
+ *   // spent in those respective states. In this specific case, 1000 and 2000 ms.
+ *   counter.getValues(arrayContainer, 0);
+ *   // arrayContainer now has values {10, 100}
+ *   counter.getValues(arrayContainer, 1);
+ *   // arrayContainer now has values {20, 200}
+ * </pre>
+ *
+ * The tracked values are expected to increase monotonically.
+ *
+ * @hide
+ */
+public class LongArrayMultiStateCounter {
+
+    /**
+     * Container for a native equivalent of a long[].
+     */
+    public static class LongArrayContainer {
+        private static final NativeAllocationRegistry sRegistry =
+                NativeAllocationRegistry.createMalloced(
+                        LongArrayContainer.class.getClassLoader(), native_getReleaseFunc());
+
+        private final long mNativeObject;
+        private final int mLength;
+
+        public LongArrayContainer(int length) {
+            mLength = length;
+            mNativeObject = native_init(length);
+            sRegistry.registerNativeAllocation(this, mNativeObject);
+        }
+
+        /**
+         * Copies the supplied values into the underlying native array.
+         */
+        public void setValues(long[] array) {
+            if (array.length != mLength) {
+                throw new IllegalArgumentException(
+                        "Invalid array length: " + mLength + ", expected: " + mLength);
+            }
+            native_setValues(mNativeObject, array);
+        }
+
+        /**
+         * Copies the underlying native array values to the supplied array.
+         */
+        public void getValues(long[] array) {
+            if (array.length != mLength) {
+                throw new IllegalArgumentException(
+                        "Invalid array length: " + mLength + ", expected: " + mLength);
+            }
+            native_getValues(mNativeObject, array);
+        }
+
+        @CriticalNative
+        private static native long native_init(int length);
+
+        @CriticalNative
+        private static native long native_getReleaseFunc();
+
+        @FastNative
+        private native void native_setValues(long nativeObject, long[] array);
+
+        @FastNative
+        private native void native_getValues(long nativeObject, long[] array);
+    }
+
+    private static final NativeAllocationRegistry sRegistry =
+            NativeAllocationRegistry.createMalloced(
+                    LongArrayMultiStateCounter.class.getClassLoader(), native_getReleaseFunc());
+
+    private final int mStateCount;
+    private final int mLength;
+    private final long mNativeObject;
+
+    public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
+            long timestampMs) {
+        mStateCount = stateCount;
+        mLength = arrayLength;
+        mNativeObject = native_init(stateCount, arrayLength, initialState, timestampMs);
+        sRegistry.registerNativeAllocation(this, mNativeObject);
+    }
+
+    /**
+     * Sets the current state to the supplied value.
+     */
+    public void setState(int state, long timestampMs) {
+        if (state < 0 || state >= mStateCount) {
+            throw new IllegalArgumentException(
+                    "State: " + state + ", outside the range: [0-" + mStateCount + "]");
+        }
+        native_setState(mNativeObject, state, timestampMs);
+    }
+
+    /**
+     * Sets the new values.  The delta between the previously set values and these values
+     * is distributed among the state according to the time the object spent in those states
+     * since the previous call to updateValues.
+     */
+    public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) {
+        if (longArrayContainer.mLength != mLength) {
+            throw new IllegalArgumentException(
+                    "Invalid array length: " + longArrayContainer.mLength + ", expected: "
+                            + mLength);
+        }
+        native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs);
+    }
+
+    /**
+     * Populates longArrayContainer with the accumulated counts for the specified state.
+     */
+    public void getCounts(LongArrayContainer longArrayContainer, int state) {
+        if (state < 0 || state >= mStateCount) {
+            throw new IllegalArgumentException(
+                    "State: " + state + ", outside the range: [0-" + mStateCount + "]");
+        }
+        native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state);
+    }
+
+    @Override
+    public String toString() {
+        return native_toString(mNativeObject);
+    }
+
+    @CriticalNative
+    private static native long native_init(int stateCount, int arrayLength, int initialState,
+            long timestampMs);
+
+    @CriticalNative
+    private static native long native_getReleaseFunc();
+
+    @CriticalNative
+    private static native void native_setState(long nativeObject, int state, long timestampMs);
+
+    @CriticalNative
+    private static native void native_updateValues(long nativeObject,
+            long longArrayContainerNativeObject, long timestampMs);
+
+    @CriticalNative
+    private static native void native_getCounts(long nativeObject,
+            long longArrayContainerNativeObject, int state);
+
+    @FastNative
+    private native String native_toString(long nativeObject);
+}
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index b4a2b63..c527c06 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -175,8 +175,11 @@
         final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
                 systemUid, BatteryStats.STATS_SINCE_CHARGED);
 
-        return uCtoMah(consumptionUC) * systemServiceModeledPowerMah
-                / systemUidModeledPowerMah;
+        if (systemUidModeledPowerMah > 0) {
+            return uCtoMah(consumptionUC) * systemServiceModeledPowerMah / systemUidModeledPowerMah;
+        } else {
+            return 0;
+        }
     }
 
     private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) {
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 5b68159..100a605d 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,6 +1,7 @@
 per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
 per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file Protocol* = etancohen@google.com, lorenzo@google.com
 per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file DataClass* = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 4f76aad..21a327c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -217,6 +217,7 @@
                 "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
                 "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
                 "com_android_internal_os_KernelSingleUidTimeReader.cpp",
+                "com_android_internal_os_LongArrayMultiStateCounter.cpp",
                 "com_android_internal_os_Zygote.cpp",
                 "com_android_internal_os_ZygoteCommandBuffer.cpp",
                 "com_android_internal_os_ZygoteInit.cpp",
@@ -245,9 +246,12 @@
                 "android.media.audio.common.types-V1-cpp",
                 "audioclient-types-aidl-cpp",
                 "audioflinger-aidl-cpp",
+                "audiopolicy-types-aidl-cpp",
+                "spatializer-aidl-cpp",
                 "av-types-aidl-cpp",
                 "android.hardware.camera.device@3.2",
                 "libandroidicu",
+                "libbattery",
                 "libbpf_android",
                 "libnetdbpf",
                 "libnetdutils",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 09d5cb1..91179c2 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -204,6 +204,7 @@
 extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
 extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
 extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
+extern int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv* env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
@@ -1586,6 +1587,7 @@
         REG_JNI(register_com_android_internal_content_om_OverlayConfig),
         REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
         REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
+        REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
         REG_JNI(register_com_android_internal_os_Zygote),
         REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
         REG_JNI(register_com_android_internal_os_ZygoteInit),
diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp
index 2a16dce..6879a60 100644
--- a/core/jni/android_media_AudioDeviceAttributes.cpp
+++ b/core/jni/android_media_AudioDeviceAttributes.cpp
@@ -24,6 +24,11 @@
 
 static jclass gAudioDeviceAttributesClass;
 static jmethodID gAudioDeviceAttributesCstor;
+static struct {
+    jfieldID mAddress;
+    jfieldID mNativeType;
+    // other fields unused by JNI
+} gAudioDeviceAttributesFields;
 
 namespace android {
 
@@ -33,12 +38,25 @@
     jint jNativeType = (jint)devTypeAddr->mType;
     ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress()));
 
-    *jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
-            jNativeType, jAddress.get());
+    *jAudioDeviceAttributes =
+        env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
+                       jNativeType, jAddress.get());
 
     return jStatus;
 }
 
+jint createAudioDeviceTypeAddrFromJava(JNIEnv *env, AudioDeviceTypeAddr *devTypeAddr,
+                                       const jobject jAudioDeviceAttributes) {
+    devTypeAddr->mType = (audio_devices_t)env->GetIntField(jAudioDeviceAttributes,
+                         gAudioDeviceAttributesFields.mNativeType);
+
+    jstring jAddress = (jstring)env->GetObjectField(jAudioDeviceAttributes,
+                       gAudioDeviceAttributesFields.mAddress);
+    devTypeAddr->setAddress(ScopedUtfChars(env, jAddress).c_str());
+
+    return AUDIO_JAVA_SUCCESS;
+}
+
 } // namespace android
 
 int register_android_media_AudioDeviceAttributes(JNIEnv *env) {
@@ -48,5 +66,10 @@
     gAudioDeviceAttributesCstor =
             GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>", "(ILjava/lang/String;)V");
 
+    gAudioDeviceAttributesFields.mNativeType =
+            GetFieldIDOrDie(env, gAudioDeviceAttributesClass, "mNativeType", "I");
+    gAudioDeviceAttributesFields.mAddress =
+            GetFieldIDOrDie(env, gAudioDeviceAttributesClass, "mAddress", "Ljava/lang/String;");
+
     return 0;
 }
diff --git a/core/jni/android_media_AudioDeviceAttributes.h b/core/jni/android_media_AudioDeviceAttributes.h
index b49d9ba..4a1f40d 100644
--- a/core/jni/android_media_AudioDeviceAttributes.h
+++ b/core/jni/android_media_AudioDeviceAttributes.h
@@ -28,6 +28,9 @@
 
 extern jint createAudioDeviceAttributesFromNative(JNIEnv *env, jobject *jAudioDeviceAttributes,
                                         const AudioDeviceTypeAddr *devTypeAddr);
+
+extern jint createAudioDeviceTypeAddrFromJava(JNIEnv *env, AudioDeviceTypeAddr *devTypeAddr,
+                                        const jobject jAudioDeviceAttributes);
 } // namespace android
 
 #endif
\ No newline at end of file
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 8a1f1a0..268871b 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -27,6 +27,8 @@
 #include "core_jni_helpers.h"
 
 #include <android/media/AudioVibratorInfo.h>
+#include <android/media/INativeSpatializerCallback.h>
+#include <android/media/ISpatializer.h>
 #include <audiomanager/AudioManager.h>
 #include <media/AudioContainers.h>
 #include <media/AudioPolicy.h>
@@ -2025,6 +2027,18 @@
     AudioSystem::setRoutingCallback(android_media_AudioSystem_routing_callback);
 }
 
+void javaAudioFormatToNativeAudioConfig(JNIEnv *env, audio_config_t *nConfig,
+                                       const jobject jFormat, bool isInput) {
+    *nConfig = AUDIO_CONFIG_INITIALIZER;
+    nConfig->format = audioFormatToNative(env->GetIntField(jFormat, gAudioFormatFields.mEncoding));
+    nConfig->sample_rate = env->GetIntField(jFormat, gAudioFormatFields.mSampleRate);
+    jint jChannelMask = env->GetIntField(jFormat, gAudioFormatFields.mChannelMask);
+    if (isInput) {
+        nConfig->channel_mask = inChannelMaskToNative(jChannelMask);
+    } else {
+        nConfig->channel_mask = outChannelMaskToNative(jChannelMask);
+    }
+}
 
 static jint convertAudioMixToNative(JNIEnv *env,
                                     AudioMix *nAudioMix,
@@ -2045,13 +2059,7 @@
     nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);
 
     jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
-    nAudioMix->mFormat = AUDIO_CONFIG_INITIALIZER;
-    nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat,
-                                                     gAudioFormatFields.mSampleRate);
-    nAudioMix->mFormat.channel_mask = outChannelMaskToNative(env->GetIntField(jFormat,
-                                                     gAudioFormatFields.mChannelMask));
-    nAudioMix->mFormat.format = audioFormatToNative(env->GetIntField(jFormat,
-                                                     gAudioFormatFields.mEncoding));
+    javaAudioFormatToNativeAudioConfig(env, &nAudioMix->mFormat, jFormat, false /*isInput*/);
     env->DeleteLocalRef(jFormat);
 
     jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
@@ -2714,6 +2722,58 @@
     return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos));
 }
 
+static jobject android_media_AudioSystem_getSpatializer(JNIEnv *env, jobject thiz,
+                                                       jobject jISpatializerCallback) {
+    sp<media::INativeSpatializerCallback> nISpatializerCallback
+            = interface_cast<media::INativeSpatializerCallback>(
+                    ibinderForJavaObject(env, jISpatializerCallback));
+    sp<media::ISpatializer> nSpatializer;
+    status_t status = AudioSystem::getSpatializer(nISpatializerCallback,
+                                        &nSpatializer);
+    if (status != NO_ERROR) {
+        return nullptr;
+    }
+    return javaObjectForIBinder(env, IInterface::asBinder(nSpatializer));
+}
+
+static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject thiz,
+                                                       jobject jaa, jobject jFormat,
+                                                       jobjectArray jDeviceArray) {
+    JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
+    jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
+    if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+       return false;
+    }
+
+    AudioDeviceTypeAddrVector nDevices;
+
+    const size_t numDevices = env->GetArrayLength(jDeviceArray);
+    for (size_t i = 0;  i < numDevices; ++i) {
+        AudioDeviceTypeAddr device;
+        jobject jDevice  = env->GetObjectArrayElement(jDeviceArray, i);
+        if (jDevice == nullptr) {
+            return false;
+        }
+        jStatus = createAudioDeviceTypeAddrFromJava(env, &device, jDevice);
+        if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+            return false;
+        }
+        nDevices.push_back(device);
+    }
+
+    audio_config_t nConfig;
+    javaAudioFormatToNativeAudioConfig(env, &nConfig, jFormat, false /*isInput*/);
+
+    bool canBeSpatialized;
+    status_t status =
+            AudioSystem::canBeSpatialized(paa.get(), &nConfig, nDevices, &canBeSpatialized);
+    if (status != NO_ERROR) {
+        ALOGW("%s native returned error %d", __func__, status);
+        return false;
+    }
+    return canBeSpatialized;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] =
@@ -2850,7 +2910,15 @@
           (void *)android_media_AudioSystem_removeUserIdDeviceAffinities},
          {"setCurrentImeUid", "(I)I", (void *)android_media_AudioSystem_setCurrentImeUid},
          {"setVibratorInfos", "(Ljava/util/List;)I",
-          (void *)android_media_AudioSystem_setVibratorInfos}};
+          (void *)android_media_AudioSystem_setVibratorInfos},
+         {"nativeGetSpatializer",
+          "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;",
+          (void *)android_media_AudioSystem_getSpatializer},
+         {"canBeSpatialized",
+          "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
+          "[Landroid/media/AudioDeviceAttributes;)Z",
+          (void *)android_media_AudioSystem_canBeSpatialized}};
+
 
 static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
new file mode 100644
index 0000000..953902a
--- /dev/null
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <cstring>
+#include "LongArrayMultiStateCounter.h"
+#include "core_jni_helpers.h"
+
+namespace android {
+
+static jlong native_init(jint stateCount, jint arrayLength, jint initialState, jlong timestamp) {
+    battery::LongArrayMultiStateCounter *counter =
+            new battery::LongArrayMultiStateCounter(stateCount, initialState,
+                                                    std::vector<uint64_t>(arrayLength), timestamp);
+    return reinterpret_cast<jlong>(counter);
+}
+
+static void native_dispose(void *nativePtr) {
+    battery::LongArrayMultiStateCounter *counter =
+            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+    delete counter;
+}
+
+static jlong native_getReleaseFunc() {
+    return reinterpret_cast<jlong>(native_dispose);
+}
+
+static void native_setState(jlong nativePtr, jint state, jlong timestamp) {
+    battery::LongArrayMultiStateCounter *counter =
+            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+    counter->setState(state, timestamp);
+}
+
+static void native_updateValues(jlong nativePtr, jlong longArrayContainerNativePtr,
+                                jlong timestamp) {
+    battery::LongArrayMultiStateCounter *counter =
+            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+    std::vector<uint64_t> *vector =
+            reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
+
+    counter->updateValue(*vector, timestamp);
+}
+
+static void native_getCounts(jlong nativePtr, jlong longArrayContainerNativePtr, jint state) {
+    battery::LongArrayMultiStateCounter *counter =
+            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+    std::vector<uint64_t> *vector =
+            reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
+
+    *vector = counter->getCount(state);
+}
+
+static jobject native_toString(JNIEnv *env, jlong nativePtr, jobject self) {
+    battery::LongArrayMultiStateCounter *counter =
+            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+    return env->NewStringUTF(counter->toString().c_str());
+}
+
+static jlong native_init_LongArrayContainer(jint length) {
+    return reinterpret_cast<jlong>(new std::vector<uint64_t>(length));
+}
+
+static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
+        // @CriticalNative
+        {"native_init", "(IIIJ)J", (void *)native_init},
+        // @CriticalNative
+        {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc},
+        // @CriticalNative
+        {"native_setState", "(JIJ)V", (void *)native_setState},
+        // @CriticalNative
+        {"native_updateValues", "(JJJ)V", (void *)native_updateValues},
+        // @CriticalNative
+        {"native_getCounts", "(JJI)V", (void *)native_getCounts},
+        // @FastNative
+        {"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
+};
+
+/////////////////////// LongArrayMultiStateCounter.LongArrayContainer ////////////////////////
+
+static void native_dispose_LongArrayContainer(jlong nativePtr) {
+    std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
+    delete vector;
+}
+
+static jlong native_getReleaseFunc_LongArrayContainer() {
+    return reinterpret_cast<jlong>(native_dispose_LongArrayContainer);
+}
+
+static void native_setValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr,
+                                                jlongArray jarray) {
+    std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
+    ScopedLongArrayRO scopedArray(env, jarray);
+    const uint64_t *array = reinterpret_cast<const uint64_t *>(scopedArray.get());
+    uint8_t size = scopedArray.size();
+
+    // Boundary checks are performed in the Java layer
+    std::copy(array, array + size, vector->data());
+}
+
+static void native_getValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr,
+                                                jlongArray jarray) {
+    std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
+    ScopedLongArrayRW scopedArray(env, jarray);
+
+    // Boundary checks are performed in the Java layer
+    std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get());
+}
+
+static const JNINativeMethod g_LongArrayContainer_methods[] = {
+        // @CriticalNative
+        {"native_init", "(I)J", (void *)native_init_LongArrayContainer},
+        // @CriticalNative
+        {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc_LongArrayContainer},
+        // @FastNative
+        {"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer},
+        // @FastNative
+        {"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer},
+};
+
+int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) {
+    // 0 represents success, thus "|" and not "&"
+    return RegisterMethodsOrDie(env, "com/android/internal/os/LongArrayMultiStateCounter",
+                                g_LongArrayMultiStateCounter_methods,
+                                NELEM(g_LongArrayMultiStateCounter_methods)) |
+            RegisterMethodsOrDie(env,
+                                 "com/android/internal/os/LongArrayMultiStateCounter"
+                                 "$LongArrayContainer",
+                                 g_LongArrayContainer_methods, NELEM(g_LongArrayContainer_methods));
+}
+
+} // namespace android
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index c4838b8..84f82fd 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -254,6 +254,15 @@
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER}. -->
   <item type="id" name="accessibilityActionImeEnter" />
 
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START}. -->
+  <item type="id" name="accessibilityActionDragStart" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_DROP}. -->
+  <item type="id" name="accessibilityActionDragDrop" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL}. -->
+  <item type="id" name="accessibilityActionDragCancel" />
+
   <!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
   <item type="id" name="remote_views_next_child" />
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fd03d72..a20477c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3224,6 +3224,9 @@
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01fe0000">
+    <public name="accessibilityActionDragStart" />
+    <public name="accessibilityActionDragDrop" />
+    <public name="accessibilityActionDragCancel" />
   </staging-public-group>
 
   <staging-public-group type="style" first-id="0x01fd0000">
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 6e34a33..b5ab282 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -269,7 +269,7 @@
         onView(withId(R.id.content_preview_thumbnail)).check(matches(isDisplayed()));
     }
 
-    @Test
+    @Test @Ignore
     public void twoOptionsAndUserSelectsOne() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -298,7 +298,7 @@
         assertThat(chosen[0], is(toChoose));
     }
 
-    @Test
+    @Test @Ignore
     public void fourOptionsStackedIntoOneTarget() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
 
@@ -351,7 +351,7 @@
         }
     }
 
-    @Test
+    @Test @Ignore
     public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -424,7 +424,7 @@
         assertThat(activity.isFinishing(), is(true));
     }
 
-    @Test
+    @Test @Ignore
     public void hasOtherProfileOneOption() throws Exception {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -461,7 +461,7 @@
         assertThat(chosen[0], is(toChoose));
     }
 
-    @Test
+    @Test @Ignore
     public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -500,7 +500,7 @@
         assertThat(chosen[0], is(toChoose));
     }
 
-    @Test
+    @Test @Ignore
     public void hasLastChosenActivityAndOtherProfile() throws Exception {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -564,7 +564,7 @@
         assertEquals(mActivityRule.getActivityResult().getResultCode(), RESULT_OK);
     }
 
-    @Test
+    @Test @Ignore
     public void copyTextToClipboardLogging() throws Exception {
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -595,7 +595,7 @@
     }
 
 
-    @Test
+    @Test @Ignore
     public void testNearbyShareLogging() throws Exception {
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -643,7 +643,7 @@
 
 
 
-    @Test
+    @Test @Ignore
     public void testEditImageLogs() throws Exception {
         Intent sendIntent = createSendImageIntent(
                 Uri.parse("android.resource://com.android.frameworks.coretests/"
@@ -1129,7 +1129,7 @@
     }
 
     // This test is too long and too slow and should not be taken as an example for future tests.
-    @Test
+    @Test @Ignore
     public void testDirectTargetSelectionLogging() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         // We need app targets for direct targets to get displayed
@@ -1162,8 +1162,7 @@
                                 /* resolveInfoPresentationGetter */ null),
                         serviceTargets,
                         TARGET_TYPE_CHOOSER_TARGET,
-                        directShareToShortcutInfos,
-                        List.of())
+                        directShareToShortcutInfos)
         );
 
         // Thread.sleep shouldn't be a thing in an integration test but it's
@@ -1200,7 +1199,7 @@
     }
 
     // This test is too long and too slow and should not be taken as an example for future tests.
-    @Test
+    @Test @Ignore
     public void testDirectTargetLoggingWithRankedAppTarget() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         // We need app targets for direct targets to get displayed
@@ -1234,8 +1233,7 @@
                                 /* resolveInfoPresentationGetter */ null),
                         serviceTargets,
                         TARGET_TYPE_CHOOSER_TARGET,
-                        directShareToShortcutInfos,
-                        List.of())
+                        directShareToShortcutInfos)
         );
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
@@ -1266,7 +1264,7 @@
                 .getAllValues().get(2).getTaggedData(MetricsEvent.FIELD_RANKED_POSITION), is(0));
     }
 
-    @Test
+    @Test @Ignore
     public void testShortcutTargetWithApplyAppLimits() throws InterruptedException {
         // Set up resources
         sOverrides.resources = Mockito.spy(
@@ -1305,8 +1303,7 @@
                                 /* resolveInfoPresentationGetter */ null),
                         serviceTargets,
                         TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE,
-                        directShareToShortcutInfos,
-                        List.of())
+                        directShareToShortcutInfos)
         );
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
@@ -1323,7 +1320,7 @@
                 activity.getAdapter().getItem(0).getDisplayLabel(), is("testTitle0"));
     }
 
-    @Test
+    @Test @Ignore
     public void testShortcutTargetWithoutApplyAppLimits() throws InterruptedException {
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
@@ -1366,8 +1363,7 @@
                                 /* resolveInfoPresentationGetter */ null),
                         serviceTargets,
                         TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE,
-                        directShareToShortcutInfos,
-                        List.of())
+                        directShareToShortcutInfos)
         );
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
@@ -1387,13 +1383,13 @@
     }
 
     // This test is too long and too slow and should not be taken as an example for future tests.
-    @Test
+    @Test @Ignore
     public void testDirectTargetLoggingWithAppTargetNotRankedPortrait()
             throws InterruptedException {
         testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_PORTRAIT, 4);
     }
 
-    @Test
+    @Test @Ignore
     public void testDirectTargetLoggingWithAppTargetNotRankedLandscape()
             throws InterruptedException {
         testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_LANDSCAPE, 8);
@@ -1442,8 +1438,7 @@
                                 /* resolveInfoPresentationGetter */ null),
                         serviceTargets,
                         TARGET_TYPE_CHOOSER_TARGET,
-                        directShareToShortcutInfos,
-                        List.of())
+                        directShareToShortcutInfos)
         );
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
@@ -1554,7 +1549,7 @@
         assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
     }
 
-    @Test
+    @Test @Ignore
     public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -1719,7 +1714,7 @@
                 .check(matches(isDisplayed()));
     }
 
-    @Test
+    @Test @Ignore
     public void testAppTargetLogging() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -1775,7 +1770,7 @@
                         .SharesheetTargetSelectedEvent.SHARESHEET_APP_TARGET_SELECTED.getId()));
     }
 
-    @Test
+    @Test @Ignore
     public void testDirectTargetLogging() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         // We need app targets for direct targets to get displayed
@@ -1806,8 +1801,7 @@
                                 /* resolveInfoPresentationGetter */ null),
                         serviceTargets,
                         TARGET_TYPE_CHOOSER_TARGET,
-                        directShareToShortcutInfos,
-                        null)
+                        directShareToShortcutInfos)
         );
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
@@ -1856,7 +1850,7 @@
                         .SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()));
     }
 
-    @Test
+    @Test @Ignore
     public void testEmptyDirectRowLogging() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         // We need app targets for direct targets to get displayed
@@ -1907,7 +1901,7 @@
                         .SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW.getId()));
     }
 
-    @Test
+    @Test @Ignore
     public void testCopyTextToClipboardLogging() throws Exception {
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -1953,7 +1947,7 @@
                         .SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()));
     }
 
-    @Test
+    @Test @Ignore
     public void testSwitchProfileLogging() throws InterruptedException {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
new file mode 100644
index 0000000..26296f1
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LongArrayMultiStateCounterTest {
+
+    @Test
+    public void setStateAndUpdateValue() {
+        LongArrayMultiStateCounter.LongArrayContainer longArrayContainer =
+                new LongArrayMultiStateCounter.LongArrayContainer(4);
+        LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000);
+        counter.setState(1, 2000);
+        counter.setState(0, 4000);
+        longArrayContainer.setValues(new long[]{100, 200, 300, 400});
+        counter.updateValues(longArrayContainer, 9000);
+        counter.getCounts(longArrayContainer, 0);
+
+        long[] result = new long[4];
+        longArrayContainer.getValues(result);
+        assertThat(result).isEqualTo(new long[]{75, 150, 225, 300});
+
+        counter.getCounts(longArrayContainer, 1);
+        longArrayContainer.getValues(result);
+        assertThat(result).isEqualTo(new long[]{25, 50, 75, 100});
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/OWNERS b/core/tests/coretests/src/com/android/internal/util/OWNERS
new file mode 100644
index 0000000..d832745
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/OWNERS
@@ -0,0 +1,2 @@
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index f3cfcf1..67358c4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -579,7 +579,7 @@
         //
         // Note: mNamespace == KeyProperties.NAMESPACE_APPLICATION implies that the target domain
         // is Domain.APP and Domain.SELINUX is the target domain otherwise.
-        if (alias != descriptor.alias
+        if (!alias.equals(descriptor.alias)
                 || descriptor.domain != targetDomain
                 || (descriptor.domain == Domain.SELINUX && descriptor.nspace != targetNamespace)) {
             throw new KeyStoreException("Can only replace keys with same alias: " + alias
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index c32dddf..3b9bcd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -93,6 +93,7 @@
     private final Rect mDividerBounds = new Rect();
     private final Rect mBounds1 = new Rect();
     private final Rect mBounds2 = new Rect();
+    private final Rect mTmpBounds = new Rect();
     private final SplitLayoutHandler mSplitLayoutHandler;
     private final SplitWindowManager mSplitWindowManager;
     private final DisplayImeController mDisplayImeController;
@@ -188,9 +189,10 @@
         final int rotation = configuration.windowConfiguration.getRotation();
         final Rect rootBounds = configuration.windowConfiguration.getBounds();
         if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+            mTmpBounds.set(mRootBounds);
             mRootBounds.set(rootBounds);
             mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
-            resetDividerPosition();
+            initDividerPosition(mTmpBounds);
             affectsLayout = true;
         }
 
@@ -202,6 +204,19 @@
         return affectsLayout;
     }
 
+    private void initDividerPosition(Rect oldBounds) {
+        final float snapRatio = (float) mDividePosition
+                / (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
+        // Estimate position by previous ratio.
+        final float length =
+                (float) (isLandscape() ? mRootBounds.width() : mRootBounds.height());
+        final int estimatePosition = (int) (length * snapRatio);
+        // Init divider position by estimated position using current bounds snap algorithm.
+        mDividePosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
+                estimatePosition).position;
+        updateBounds(mDividePosition);
+    }
+
     /** Updates recording bounds of divider window and both of the splits. */
     private void updateBounds(int position) {
         mDividerBounds.set(mRootBounds);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 5bd6891..60c9e1c 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
 import android.bluetooth.BluetoothCodecConfig;
@@ -2000,6 +2001,46 @@
      */
     public static native int setVibratorInfos(@NonNull List<Vibrator> vibrators);
 
+    /**
+     * @hide
+     * If a spatializer effect is present on the platform, this will return an
+     * ISpatializer interface to control this feature.
+     * If no spatializer is present, a null interface is returned.
+     * The INativeSpatializerCallback passed must not be null.
+     * Only one ISpatializer interface can exist at a given time. The native audio policy
+     * service will reject the request if an interface was already acquired and previous owner
+     * did not die or call ISpatializer.release().
+     * @param callback the callback to receive state updates if the ISpatializer
+     *        interface is acquired.
+     * @return the ISpatializer interface made available to control the
+     *        platform spatializer
+     */
+    @Nullable
+    public static ISpatializer getSpatializer(INativeSpatializerCallback callback) {
+        return ISpatializer.Stub.asInterface(nativeGetSpatializer(callback));
+    }
+    private static native IBinder nativeGetSpatializer(INativeSpatializerCallback callback);
+
+    /**
+     * @hide
+     * Queries if some kind of spatialization will be performed if the audio playback context
+     * described by the provided arguments is present.
+     * The context is made of:
+     * - The audio attributes describing the playback use case.
+     * - The audio configuration describing the audio format, channels, sampling rate ...
+     * - The devices describing the sink audio device selected for playback.
+     * All arguments are optional and only the specified arguments are used to match against
+     * supported criteria. For instance, supplying no argument will tell if spatialization is
+     * supported or not in general.
+     * @param attributes audio attributes describing the playback use case
+     * @param format audio configuration describing the audio format, channels, sampling rate...
+     * @param devices the sink audio device selected for playback
+     * @return true if spatialization is enabled for this context, false otherwise.
+     */
+    public static native boolean canBeSpatialized(AudioAttributes attributes,
+                                              AudioFormat format,
+                                              AudioDeviceAttributes[] devices);
+
     // Items shared with audio service
 
     /**
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 86ed50b..72ee00f 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -102,6 +102,13 @@
         mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
     };
 
+    /** @hide */
+    public int getPlayerIId() {
+        synchronized (mLock) {
+            return mPlayerIId;
+        }
+    }
+
     /**
      * Call from derived class when instantiation / initialization is successful
      */
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 6016aaf..28c6166 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -68,16 +68,6 @@
             lockScreenWeight="400"
         />
     </FrameLayout>
-    <FrameLayout
-        android:id="@+id/keyguard_smartspace_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="@dimen/below_clock_padding_start"
-        android:paddingEnd="@dimen/below_clock_padding_end"
-        android:layout_alignParentStart="true"
-        android:layout_below="@id/lockscreen_clock_view"
-        />
-    <!-- either keyguard_status_area or keyguard_smartspace_container is visible -->
     <include layout="@layout/keyguard_status_area"
         android:id="@+id/keyguard_status_area"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2b5076b..a2b4ac122 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -91,18 +91,6 @@
                  android:layout_height="match_parent"
                  android:visibility="gone"/>
 
-        <FrameLayout
-            android:id="@+id/split_shade_smartspace_container"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:paddingStart="@dimen/notification_side_paddings"
-            android:paddingEnd="@dimen/notification_side_paddings"
-            systemui:layout_constraintStart_toStartOf="@id/qs_edge_guideline"
-            systemui:layout_constraintEnd_toEndOf="parent"
-            systemui:layout_constraintTop_toTopOf="parent"
-            android:visibility="gone">
-        </FrameLayout>
-
         <include layout="@layout/dock_info_overlay"/>
 
         <FrameLayout
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index f1288e3..d9a5670 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -62,8 +62,7 @@
     <com.android.systemui.statusbar.LightRevealScrim
             android:id="@+id/light_reveal_scrim"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="gone" />
+            android:layout_height="match_parent" />
 
     <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 0b78ddb..382e895 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -17,13 +17,13 @@
 package com.android.keyguard;
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 
 import android.app.WallpaperManager;
 import android.text.TextUtils;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 
@@ -93,7 +93,8 @@
 
     private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
 
-    private ViewGroup mSmartspaceContainer;
+    // If set, will replace keyguard_status_area
+    private View mSmartspaceView;
 
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private SmartspaceTransitionController mSmartspaceTransitionController;
@@ -147,8 +148,6 @@
 
         mClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
         mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
-        mSmartspaceContainer = mView.findViewById(R.id.keyguard_smartspace_container);
-        mSmartspaceController.setKeyguardStatusContainer(mSmartspaceContainer);
 
         mClockViewController =
                 new AnimatableClockController(
@@ -190,25 +189,35 @@
         }
         updateAodIcons();
 
-        if (mSmartspaceController.isSmartspaceEnabled()) {
-            // "Enabled" doesn't mean smartspace is displayed here - inside mSmartspaceContainer -
-            // it might be a part of another view when in split shade. But it means that it CAN be
-            // displayed here, so we want to hide keyguard_status_area and set views relations
-            // accordingly.
+        if (mSmartspaceController.isEnabled()) {
+            mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
 
             View ksa = mView.findViewById(R.id.keyguard_status_area);
-            // we show either keyguard_status_area or smartspace, so when smartspace can be visible,
-            // keyguard_status_area should be hidden
+            int ksaIndex = mView.indexOfChild(ksa);
             ksa.setVisibility(View.GONE);
 
+            // Place smartspace view below normal clock...
+            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+                    MATCH_PARENT, WRAP_CONTENT);
+            lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
+
+            mView.addView(mSmartspaceView, ksaIndex, lp);
+            int startPadding = getContext().getResources()
+                    .getDimensionPixelSize(R.dimen.below_clock_padding_start);
+            int endPadding = getContext().getResources()
+                    .getDimensionPixelSize(R.dimen.below_clock_padding_end);
+            mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
+
             updateClockLayout();
 
-            View nic = mView.findViewById(R.id.left_aligned_notification_icon_container);
-            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
-            lp.addRule(RelativeLayout.BELOW, mSmartspaceContainer.getId());
+            View nic = mView.findViewById(
+                    R.id.left_aligned_notification_icon_container);
+            lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
+            lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
             nic.setLayoutParams(lp);
-            mView.setSmartspaceView(mSmartspaceContainer);
-            mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceContainer);
+
+            mView.setSmartspaceView(mSmartspaceView);
+            mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
         }
     }
 
@@ -231,7 +240,10 @@
         // instance of this class. In order to fix this, we need to modify the plugin so that
         // (a) we get a new view each time and (b) we can properly clean up an old view by making
         // it unregister itself as a plugin listener.
-        mSmartspaceContainer.removeAllViews();
+        if (mSmartspaceView != null) {
+            mView.removeView(mSmartspaceView);
+            mSmartspaceView = null;
+        }
     }
 
     /**
@@ -244,7 +256,7 @@
     }
 
     private void updateClockLayout() {
-        if (mSmartspaceController.isSmartspaceEnabled()) {
+        if (mSmartspaceController.isEnabled()) {
             RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
                     MATCH_PARENT);
             lp.topMargin = getContext().getResources().getDimensionPixelSize(
@@ -309,8 +321,8 @@
         PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_Y,
                 scale, props, animate);
 
-        if (mSmartspaceContainer != null) {
-            PropertyAnimator.setProperty(mSmartspaceContainer, AnimatableProperty.TRANSLATION_X,
+        if (mSmartspaceView != null) {
+            PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
                     x, props, animate);
 
             // If we're unlocking with the SmartSpace shared element transition, let the controller
@@ -328,8 +340,8 @@
     public void setChildrenAlphaExcludingSmartspace(float alpha) {
         final Set<View> excludedViews = new HashSet<>();
 
-        if (mSmartspaceContainer != null) {
-            excludedViews.add(mSmartspaceContainer);
+        if (mSmartspaceView != null) {
+            excludedViews.add(mSmartspaceView);
         }
 
         setChildrenAlphaExcluding(alpha, excludedViews);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 867f117..1804ad4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -80,7 +80,7 @@
         mKeyguardStateController = keyguardStateController;
         mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
                 keyguardStateController, dozeParameters, unlockedScreenOffAnimationController,
-                /* animateYPos= */ true);
+                /* animateYPos= */ true, /* visibleOnCommunal= */ false);
         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
         mSmartspaceTransitionController = smartspaceTransitionController;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index a71b4f4..56fdb89 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -43,6 +43,7 @@
     private final KeyguardStateController mKeyguardStateController;
     private final DozeParameters mDozeParameters;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+    private final boolean mVisibleOnCommunal;
     private boolean mAnimateYPos;
     private boolean mKeyguardViewVisibilityAnimating;
     private boolean mLastOccludedState = false;
@@ -53,13 +54,15 @@
             KeyguardStateController keyguardStateController,
             DozeParameters dozeParameters,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
-            boolean animateYPos) {
+            boolean animateYPos,
+            boolean visibleOnCommunal) {
         mView = view;
         mCommunalStateController = communalStateController;
         mKeyguardStateController = keyguardStateController;
         mDozeParameters = dozeParameters;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mAnimateYPos = animateYPos;
+        mVisibleOnCommunal = visibleOnCommunal;
     }
 
     public boolean isVisibilityAnimating() {
@@ -79,7 +82,7 @@
         mKeyguardViewVisibilityAnimating = false;
 
         // If the communal view is showing, hide immediately
-        if (mCommunalStateController.getCommunalViewShowing()) {
+        if (!mVisibleOnCommunal && mCommunalStateController.getCommunalViewShowing()) {
             mView.setVisibility(View.GONE);
             mView.setAlpha(1f);
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index b5378cd..c323bf7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -706,15 +706,21 @@
         return mCoreLayoutParams;
     }
 
+
     private void onOrientationChanged() {
         // When the configuration changes it's almost always necessary to destroy and re-create
         // the overlay's window to pass it the new LayoutParams.
         // Hiding the overlay will destroy its window. It's safe to hide the overlay regardless
         // of whether it is already hidden.
+        final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
         hideUdfpsOverlay();
+
         // If the overlay needs to be shown, this will re-create and show the overlay with the
         // updated LayoutParams. Otherwise, the overlay will remain hidden.
         updateOverlay();
+        if (wasShowingAltAuth) {
+            mKeyguardViewManager.showGenericBouncer(true);
+        }
     }
 
     private void showUdfpsOverlay(@NonNull ServerRequest request) {
@@ -785,6 +791,7 @@
                         mLockscreenShadeTransitionController,
                         mConfigurationController,
                         mSystemClock,
+                        mKeyguardStateController,
                         this
                 );
             case IUdfpsOverlayController.REASON_AUTH_BP:
@@ -820,10 +827,14 @@
             Log.v(TAG, "hideUdfpsOverlay | removing window");
             // Reset the controller back to its starting state.
             onFingerUp();
+            boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
             mWindowManager.removeView(mView);
             mView.setOnTouchListener(null);
             mView.setOnHoverListener(null);
             mView.setAnimationViewController(null);
+            if (wasShowingAltAuth) {
+                mKeyguardViewManager.resetAlternateAuth(true);
+            }
             mAccessibilityManager.removeTouchExplorationStateChangeListener(
                     mTouchExplorationStateChangeListener);
             mView = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index b81b547..4d9675c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
@@ -53,6 +54,7 @@
     @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final SystemClock mSystemClock;
+    @NonNull private final KeyguardStateController mKeyguardStateController;
     @NonNull private final UdfpsController mUdfpsController;
 
     private boolean mShowingUdfpsBouncer;
@@ -63,6 +65,8 @@
     private float mTransitionToFullShadeProgress;
     private float mLastDozeAmount;
     private long mLastUdfpsBouncerShowTime = -1;
+    private float mStatusBarExpansion;
+    private boolean mLaunchTransitionFadingAway;
 
     /**
      * hidden amount of pin/pattern/password bouncer
@@ -84,6 +88,7 @@
             @NonNull LockscreenShadeTransitionController transitionController,
             @NonNull ConfigurationController configurationController,
             @NonNull SystemClock systemClock,
+            @NonNull KeyguardStateController keyguardStateController,
             @NonNull UdfpsController udfpsController) {
         super(view, statusBarStateController, statusBarOptional, dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -93,6 +98,7 @@
         mLockScreenShadeTransitionController = transitionController;
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
+        mKeyguardStateController = keyguardStateController;
         mUdfpsController = udfpsController;
     }
 
@@ -102,6 +108,12 @@
     }
 
     @Override
+    public void onInit() {
+        super.onInit();
+        mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+    }
+
+    @Override
     protected void onViewAttached() {
         super.onViewAttached();
         final float dozeAmount = mStatusBarStateController.getDozeAmount();
@@ -111,11 +123,16 @@
 
         mUdfpsRequested = false;
 
+        mLaunchTransitionFadingAway = mKeyguardStateController.isLaunchTransitionFadingAway();
+        mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
         mStatusBarState = mStatusBarStateController.getState();
         mQsExpanded = mKeyguardViewManager.isQsExpanded();
         mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
         mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
         mConfigurationController.addCallback(mConfigurationListener);
+        mStatusBarOptional.ifPresent(
+                statusBar -> statusBar.addExpansionChangedListener(
+                        mStatusBarExpansionChangedListener));
         updateAlpha();
         updatePauseAuth();
 
@@ -128,10 +145,14 @@
         super.onViewDetached();
         mFaceDetectRunning = false;
 
+        mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback);
         mStatusBarStateController.removeCallback(mStateListener);
         mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
         mConfigurationController.removeCallback(mConfigurationListener);
+        mStatusBarOptional.ifPresent(
+                statusBar -> statusBar.removeExpansionChangedListener(
+                        mStatusBarExpansionChangedListener));
         if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
         }
@@ -146,9 +167,11 @@
         pw.println("mQsExpanded=" + mQsExpanded);
         pw.println("mIsBouncerVisible=" + mIsBouncerVisible);
         pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
+        pw.println("mStatusBarExpansion=" + mStatusBarExpansion);
         pw.println("mAlpha=" + mView.getAlpha());
         pw.println("mUdfpsRequested=" + mUdfpsRequested);
         pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
+        pw.println("mLaunchTransitionFadingAway=" + mLaunchTransitionFadingAway);
     }
 
     /**
@@ -198,6 +221,10 @@
             return false;
         }
 
+        if (mLaunchTransitionFadingAway) {
+            return true;
+        }
+
         if (mStatusBarState != KEYGUARD) {
             return true;
         }
@@ -255,10 +282,13 @@
     }
 
     private void updateAlpha() {
-        // fade icon on transition to showing bouncer
+        // fade icon on transitions to showing the status bar, but if mUdfpsRequested, then
+        // the keyguard is occluded by some application - so instead use the input bouncer
+        // hidden amount to determine the fade
+        float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mStatusBarExpansion;
         int alpha = mShowingUdfpsBouncer ? 255
                 : (int) MathUtils.constrain(
-                    MathUtils.map(.5f, .9f, 0f, 255f, mInputBouncerHiddenAmount),
+                    MathUtils.map(.5f, .9f, 0f, 255f, expansion),
                     0f, 255f);
         alpha *= (1.0f - mTransitionToFullShadeProgress);
         mView.setUnpausedAlpha(alpha);
@@ -374,4 +404,23 @@
                     mView.updateColor();
                 }
             };
+
+    private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
+            new StatusBar.ExpansionChangedListener() {
+                @Override
+                public void onExpansionChanged(float expansion, boolean expanded) {
+                    mStatusBarExpansion = expansion;
+                    updateAlpha();
+                }
+            };
+
+    private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onLaunchTransitionFadingAwayChanged() {
+                    mLaunchTransitionFadingAway =
+                            mKeyguardStateController.isLaunchTransitionFadingAway();
+                    updatePauseAuth();
+                }
+            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
index 320c2ea..1680e42 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
@@ -24,8 +24,11 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.KeyguardVisibilityHelper;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ViewController;
 
@@ -70,6 +73,8 @@
     private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
             STATE_DOZING | STATE_BOUNCER_SHOWING | STATE_KEYGUARD_OCCLUDED;
 
+    private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+
     private ViewController<? extends View> mCommunalViewController;
 
     private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
@@ -123,6 +128,8 @@
             CommunalStateController communalStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             KeyguardStateController keyguardStateController,
+            DozeParameters dozeParameters,
+            UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             StatusBarStateController statusBarStateController, CommunalHostView view) {
         super(view);
         mCommunalStateController = communalStateController;
@@ -130,6 +137,21 @@
         mMainExecutor = mainExecutor;
         mKeyguardStateController = keyguardStateController;
         mStatusBarStateController = statusBarStateController;
+        mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
+                keyguardStateController, dozeParameters, unlockedScreenOffAnimationController,
+                /* animateYPos= */ false, /* visibleOnCommunal= */ true);
+    }
+
+    /**
+     * Set the visibility of the keyguard status view based on some new state.
+     */
+    public void setKeyguardStatusViewVisibility(
+            int statusBarState,
+            boolean keyguardFadingAway,
+            boolean goingToFullShade,
+            int oldStatusBarState) {
+        mKeyguardVisibilityHelper.setViewVisibility(
+                statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index b2db86f..55e6154 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -456,13 +456,24 @@
 
         public void updateListening() {
             if (!mConfigured || mSensor == null) return;
-            if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)
-                    && !mRegistered) {
-                mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
-                if (DEBUG) Log.d(TAG, "requestTriggerSensor " + mRegistered);
+            if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)) {
+                if (!mRegistered) {
+                    mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
+                    if (DEBUG) {
+                        Log.d(TAG, "requestTriggerSensor[" + mSensor
+                                + "] " + mRegistered);
+                    }
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG, "requestTriggerSensor[" + mSensor
+                                + "] already registered");
+                    }
+                }
             } else if (mRegistered) {
                 final boolean rt = mSensorManager.cancelTriggerSensor(this, mSensor);
-                if (DEBUG) Log.d(TAG, "cancelTriggerSensor " + rt);
+                if (DEBUG) {
+                    Log.d(TAG, "cancelTriggerSensor[" + mSensor + "] " + rt);
+                }
                 mRegistered = false;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java b/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java
new file mode 100644
index 0000000..fba1067
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.idle;
+
+import android.content.Context;
+import android.service.dreams.Sandman;
+
+import javax.inject.Inject;
+
+/**
+ * A helper class to the idle mode for requests related to the
+ * {@link DreamService}.
+ */
+public class DreamHelper {
+    @Inject
+    protected DreamHelper() {
+    }
+
+    /**
+     * Requests the system to start dreaming.
+     *
+     * @param context The context within which the dream request is sent.
+     */
+    public void startDreaming(Context context) {
+        Sandman.startDreamByUserRequest(context);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
index 38af02b..97d32c3 100644
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
@@ -31,7 +31,6 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemClock;
-import android.service.dreams.Sandman;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.View;
@@ -63,41 +62,41 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     @Retention(RetentionPolicy.RUNTIME)
-    @IntDef({STATE_IDLE_MODE_ENABLED, STATE_DOZING, STATE_KEYGUARD_SHOWING, STATE_IDLING})
+    @IntDef({STATE_IDLE_MODE_ENABLED, STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_DREAMING,
+            STATE_LOW_LIGHT, STATE_IDLING, STATE_SHOULD_START_IDLING})
     public @interface State {}
 
     // Set at construction to indicate idle mode is available.
     private static final int STATE_IDLE_MODE_ENABLED = 1 << 0;
 
-    // Set when the device has entered a system level dream.
-    private static final int STATE_DOZING = 1 << 1;
+    // Set when keyguard is present, even below a dream or AOD. AKA the device is locked.
+    private static final int STATE_KEYGUARD_SHOWING = 1 << 1;
 
-    // Set when the keyguard is showing.
-    private static final int STATE_KEYGUARD_SHOWING = 1 << 2;
+    // Set when the device has entered a dozing / low power state.
+    private static final int STATE_DOZING = 1 << 2;
 
-    // Set when input monitoring has established the device is now idling.
-    private static final int STATE_IDLING = 1 << 3;
+    // Set when the device has entered a dreaming state, which includes dozing.
+    private static final int STATE_DREAMING = 1 << 3;
 
     // Set when the device is in a low light environment.
     private static final int STATE_LOW_LIGHT = 1 << 4;
 
-    // The state the controller must be in to start recognizing idleness (lack of input
-    // interaction).
-    private static final int CONDITIONS_IDLE_MONITORING =
-            STATE_IDLE_MODE_ENABLED | STATE_KEYGUARD_SHOWING;
+    // Set when the device is idling, which is either dozing or dreaming.
+    private static final int STATE_IDLING = 1 << 5;
 
-    // The state the controller must be in before entering idle mode.
-    private static final int CONDITIONS_IDLING = CONDITIONS_IDLE_MONITORING | STATE_IDLING;
-
-    // The state the controller must be in to start listening for low light signals.
-    private static final int CONDITIONS_LOW_LIGHT_MONITORING =
-            STATE_IDLE_MODE_ENABLED | STATE_KEYGUARD_SHOWING;
+    // Set when the controller decides that the device should start idling (either dozing or
+    // dreaming).
+    private static final int STATE_SHOULD_START_IDLING = 1 << 6;
 
     // The aggregate current state.
     private int mState;
     private boolean mIdleModeActive;
     private boolean mLowLightModeActive;
     private boolean mIsMonitoringLowLight;
+    private boolean mIsMonitoringDream;
+
+    // Whether in a state waiting for dozing to complete before starting dreaming.
+    private boolean mDozeToDreamLock = false;
 
     private final Context mContext;
 
@@ -134,15 +133,21 @@
     // Choreographer to use for monitoring input.
     private final Choreographer mChoreographer;
 
+    // Helper class for DreamService related requests.
+    private final DreamHelper mDreamHelper;
+
     // Monitor for tracking touches for activity.
     private InputMonitorCompat mInputMonitor;
 
-    // Delayed callback for enabling idle mode.
+    // Intent filter for receiving dream broadcasts.
+    private IntentFilter mDreamIntentFilter;
+
+    // Delayed callback for starting idling.
     private final Runnable mEnableIdlingCallback = () -> {
         if (DEBUG) {
-            Log.d(TAG, "enabling idle");
+            Log.d(TAG, "time out, should start idling");
         }
-        setState(STATE_IDLING, true);
+        setState(STATE_SHOULD_START_IDLING, true);
     };
 
     private final KeyguardStateController.Callback mKeyguardCallback =
@@ -161,11 +166,13 @@
                 }
             };
 
-    private final BroadcastReceiver mDreamEndedReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mDreamStateReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
-                setState(STATE_IDLING, false);
+            if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
+                setState(STATE_DREAMING, true);
+            } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
+                setState(STATE_DREAMING, false);
             }
         }
     };
@@ -185,7 +192,8 @@
             @Named(IDLE_VIEW) Provider<View> idleViewProvider,
             Choreographer choreographer,
             KeyguardStateController keyguardStateController,
-            StatusBarStateController statusBarStateController) {
+            StatusBarStateController statusBarStateController,
+            DreamHelper dreamHelper) {
         super(view);
         mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -196,6 +204,7 @@
         mStatusBarStateController = statusBarStateController;
         mLooper = looper;
         mChoreographer = choreographer;
+        mDreamHelper = dreamHelper;
 
         mState = STATE_KEYGUARD_SHOWING;
 
@@ -226,6 +235,20 @@
     }
 
     private void setState(@State int state, boolean active) {
+        // If waiting for dozing to stop, ignore any state update until dozing is stopped.
+        if (mDozeToDreamLock) {
+            if (state == STATE_DOZING && !active) {
+                if (DEBUG) {
+                    Log.d(TAG, "dozing stopped, now start dreaming");
+                }
+
+                mDozeToDreamLock = false;
+                enableIdleMode(true);
+            }
+
+            return;
+        }
+
         final int oldState = mState;
 
         if (active) {
@@ -234,27 +257,81 @@
             mState &= ~state;
         }
 
-        // If we have entered doze or no longer match the preconditions for idling, remove idling.
-        if ((mState & STATE_DOZING) == STATE_DOZING
-                || (mState & CONDITIONS_IDLE_MONITORING) != CONDITIONS_IDLE_MONITORING) {
-            mState &= ~STATE_IDLING;
-        }
-
         if (oldState == mState) {
             return;
         }
 
-        if (DEBUG) {
-            Log.d(TAG, "state changed from " + oldState + " to " + mState);
+        // Updates STATE_IDLING.
+        final boolean isIdling = getState(STATE_DOZING) || getState(STATE_DREAMING);
+        if (isIdling) {
+            mState |= STATE_IDLING;
+        } else {
+            mState &= ~STATE_IDLING;
         }
 
-        enableIdleMonitoring(mState == CONDITIONS_IDLE_MONITORING);
-        enableIdleMode(mState == CONDITIONS_IDLING);
-        // Loose matching. Doesn't need to be the exact state to monitor low light, but only
-        // the specified states need to match.
-        enableLowLightMonitoring(
-                (mState & CONDITIONS_LOW_LIGHT_MONITORING) == CONDITIONS_LOW_LIGHT_MONITORING);
-        enableLowLightMode((mState & STATE_LOW_LIGHT) == STATE_LOW_LIGHT);
+        // Updates STATE_SHOULD_START_IDLING.
+        final boolean stoppedIdling = stoppedIdling(oldState);
+        if (stoppedIdling) {
+            mState &= ~STATE_SHOULD_START_IDLING;
+        } else if (shouldStartIdling(oldState)) {
+            mState |= STATE_SHOULD_START_IDLING;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "set " + getStateName(state) + " to " + active);
+            logCurrentState();
+        }
+
+        final boolean wasLowLight = getState(STATE_LOW_LIGHT, oldState);
+        final boolean isLowLight = getState(STATE_LOW_LIGHT);
+        final boolean wasIdling = getState(STATE_IDLING, oldState);
+
+        // When the device is idling and no longer in low light, wake up from dozing, wait till
+        // done, and start dreaming.
+        if (wasLowLight && !isLowLight && wasIdling && isIdling) {
+            if (DEBUG) {
+                Log.d(TAG, "idling and no longer in low light, stop dozing");
+            }
+
+            mDozeToDreamLock = true;
+
+            enableLowLightMode(false);
+            return;
+        }
+
+        final boolean inCommunalMode = getState(STATE_IDLE_MODE_ENABLED)
+                && getState(STATE_KEYGUARD_SHOWING);
+
+        enableDreamMonitoring(inCommunalMode);
+        enableLowLightMonitoring(inCommunalMode);
+        enableIdleMonitoring(inCommunalMode && !getState(STATE_IDLING));
+        enableIdleMode(inCommunalMode && !getState(STATE_LOW_LIGHT)
+                && getState(STATE_SHOULD_START_IDLING));
+        enableLowLightMode(inCommunalMode && !stoppedIdling && getState(STATE_LOW_LIGHT));
+    }
+
+    private void enableDreamMonitoring(boolean enable) {
+        if (mIsMonitoringDream == enable) {
+            return;
+        }
+
+        mIsMonitoringDream = enable;
+
+        if (DEBUG) {
+            Log.d(TAG, (enable ? "enable" : "disable") + " dream monitoring");
+        }
+
+        if (mDreamIntentFilter == null) {
+            mDreamIntentFilter = new IntentFilter();
+            mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
+            mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
+        }
+
+        if (enable) {
+            mBroadcastDispatcher.registerReceiver(mDreamStateReceiver, mDreamIntentFilter);
+        } else {
+            mBroadcastDispatcher.unregisterReceiver(mDreamStateReceiver);
+        }
     }
 
     private void enableIdleMonitoring(boolean enable) {
@@ -302,22 +379,14 @@
         }
 
         if (DEBUG) {
-            Log.d(TAG, "enable idle mode:" + enable);
+            Log.d(TAG, (enable ? "enable" : "disable") + " idle mode");
         }
 
         mIdleModeActive = enable;
 
         if (mIdleModeActive) {
-            // Track when the dream ends to cancel any timeouts.
-            final IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_DREAMING_STOPPED);
-            mBroadcastDispatcher.registerReceiver(mDreamEndedReceiver, filter);
-
             // Start dream.
-            Sandman.startDreamByUserRequest(mContext);
-        } else {
-            // Stop tracking dream end.
-            mBroadcastDispatcher.unregisterReceiver(mDreamEndedReceiver);
+            mDreamHelper.startDreaming(mContext);
         }
     }
 
@@ -329,11 +398,11 @@
         mIsMonitoringLowLight = enable;
 
         if (mIsMonitoringLowLight) {
-            if (DEBUG) Log.d(TAG, "Enabling low light monitoring.");
+            if (DEBUG) Log.d(TAG, "enable low light monitoring");
             mSensorManager.registerListener(this /*listener*/, mSensor,
                     SensorManager.SENSOR_DELAY_NORMAL);
         } else {
-            if (DEBUG) Log.d(TAG, "Disabling low light monitoring.");
+            if (DEBUG) Log.d(TAG, "disable low light monitoring");
             mSensorManager.unregisterListener(this);
         }
     }
@@ -346,13 +415,13 @@
         mLowLightModeActive = enable;
 
         if (mLowLightModeActive) {
-            if (DEBUG) Log.d(TAG, "Entering low light, start dozing.");
+            if (DEBUG) Log.d(TAG, "enter low light, start dozing");
 
             mPowerManager.goToSleep(
                     SystemClock.uptimeMillis(),
                     PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
         } else {
-            if (DEBUG) Log.d(TAG, "Exiting low light, stop dozing.");
+            if (DEBUG) Log.d(TAG, "exit low light, stop dozing");
             mPowerManager.wakeUp(SystemClock.uptimeMillis(),
                     PowerManager.WAKE_REASON_APPLICATION, "Exit low light condition");
         }
@@ -381,8 +450,12 @@
             return;
         }
 
-        final boolean isLowLight = event.values[0] < 10;
-        setState(STATE_LOW_LIGHT, isLowLight);
+        final boolean shouldBeLowLight = event.values[0] < 10;
+        final boolean isLowLight = getState(STATE_LOW_LIGHT);
+
+        if (shouldBeLowLight != isLowLight) {
+            setState(STATE_LOW_LIGHT, shouldBeLowLight);
+        }
     }
 
     @Override
@@ -391,4 +464,62 @@
             Log.d(TAG, "onAccuracyChanged accuracy=" + accuracy);
         }
     }
+
+    // Returns whether the device just stopped idling by comparing the previous state with the
+    // current one.
+    private boolean stoppedIdling(int oldState) {
+        // The device stopped idling if it's no longer dreaming or dozing.
+        return !getState(STATE_DOZING) && !getState(STATE_DREAMING)
+                && (getState(STATE_DOZING, oldState) || getState(STATE_DREAMING, oldState));
+    }
+
+    private boolean shouldStartIdling(int oldState) {
+        // Should start idling immediately if the device went in low light environment.
+        return !getState(STATE_LOW_LIGHT, oldState) && getState(STATE_LOW_LIGHT);
+    }
+
+    private String getStateName(@State int state) {
+        switch (state) {
+            case STATE_IDLE_MODE_ENABLED:
+                return "STATE_IDLE_MODE_ENABLED";
+            case STATE_KEYGUARD_SHOWING:
+                return "STATE_KEYGUARD_SHOWING";
+            case STATE_DOZING:
+                return "STATE_DOZING";
+            case STATE_DREAMING:
+                return "STATE_DREAMING";
+            case STATE_LOW_LIGHT:
+                return "STATE_LOW_LIGHT";
+            case STATE_IDLING:
+                return "STATE_IDLING";
+            case STATE_SHOULD_START_IDLING:
+                return "STATE_SHOULD_START_IDLING";
+            default:
+                return "STATE_UNKNOWN";
+        }
+    }
+
+    private boolean getState(@State int state) {
+        return getState(state, mState);
+    }
+
+    private boolean getState(@State int state, int oldState) {
+        return (oldState & state) == state;
+    }
+
+    private String getStateLog(@State int state) {
+        return getStateName(state) + " = " + getState(state);
+    }
+
+    private void logCurrentState() {
+        Log.d(TAG, "current state: {\n"
+                + "\t" + getStateLog(STATE_IDLE_MODE_ENABLED) + "\n"
+                + "\t" + getStateLog(STATE_KEYGUARD_SHOWING) + "\n"
+                + "\t" + getStateLog(STATE_DOZING) + "\n"
+                + "\t" + getStateLog(STATE_DREAMING) + "\n"
+                + "\t" + getStateLog(STATE_LOW_LIGHT) + "\n"
+                + "\t" + getStateLog(STATE_IDLING) + "\n"
+                + "\t" + getStateLog(STATE_SHOULD_START_IDLING) + "\n"
+                + "}");
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 89786ee..a617850 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -139,7 +139,7 @@
                                 + " with ducking", e);
                     }
                     player.start();
-                    if (DEBUG) { Log.d(mTag, "player.start"); }
+                    if (DEBUG) { Log.d(mTag, "player.start piid:" + player.getPlayerIId()); }
                 } catch (Exception e) {
                     if (player != null) {
                         player.release();
@@ -155,7 +155,13 @@
                     mPlayer = player;
                 }
                 if (mp != null) {
-                    if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
+                    if (DEBUG) {
+                        Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId());
+                    }
+                    mp.pause();
+                    try {
+                        Thread.sleep(100);
+                    } catch (InterruptedException ie) { }
                     mp.release();
                 }
                 this.notify();
@@ -244,6 +250,10 @@
                         try {
                             mp.stop();
                         } catch (Exception e) { }
+                        if (DEBUG) {
+                            Log.i(mTag, "About to release MediaPlayer piid:"
+                                    + mp.getPlayerIId() + " due to notif cancelled");
+                        }
                         mp.release();
                         synchronized(mQueueAudioFocusLock) {
                             if (mAudioManagerWithAudioFocus != null) {
@@ -284,7 +294,7 @@
     public void onCompletion(MediaPlayer mp) {
         synchronized(mQueueAudioFocusLock) {
             if (mAudioManagerWithAudioFocus != null) {
-                if (DEBUG) Log.d(mTag, "onCompletion() abandonning AudioFocus");
+                if (DEBUG) Log.d(mTag, "onCompletion() abandoning AudioFocus");
                 mAudioManagerWithAudioFocus.abandonAudioFocus(null);
                 mAudioManagerWithAudioFocus = null;
             } else {
@@ -310,6 +320,10 @@
             }
         }
         if (mp != null) {
+            if (DEBUG) {
+                Log.i("NotificationPlayer", "About to release MediaPlayer piid:"
+                        + mp.getPlayerIId() + " due to onCompletion");
+            }
             mp.release();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 658be72..a5fc5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -428,19 +428,6 @@
         }
     };
 
-    private final BroadcastReceiver mDebugAnyPackageChangedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String[] stringArrayExtra = intent.getStringArrayExtra(
-                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
-            Log.e("b/188806432", intent.toString()
-                    + (stringArrayExtra != null
-                            ? ", EXTRA_CHANGED_COMPONENT_NAME_LIST: " + String.join(", ",
-                            stringArrayExtra)
-                            : ""));
-        }
-    };
-
     private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -581,13 +568,6 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
 
-        // b/188806432
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        filter.addDataSchemeSpecificPart("", PatternMatcher.PATTERN_PREFIX);
-        mContext.registerReceiver(mDebugAnyPackageChangedReceiver, filter);
-
         // Listen for status bar state changes
         statusBarWinController.registerCallback(mStatusBarWindowCallback);
         mScreenshotHelper = new ScreenshotHelper(context);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 51dbd85..03d8e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -12,8 +12,10 @@
 import android.graphics.RadialGradient
 import android.graphics.Shader
 import android.util.AttributeSet
+import android.util.MathUtils.lerp
 import android.view.View
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
 import java.util.function.Consumer
 
 /**
@@ -63,12 +65,12 @@
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
         val ovalWidthIncreaseAmount =
-                LightRevealEffect.getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
+                getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
 
         val initialWidthMultiplier = (1f - OVAL_INITIAL_WIDTH_PERCENT) / 2f
 
         with(scrim) {
-            revealGradientEndColorAlpha = 1f - LightRevealEffect.getPercentPastThreshold(
+            revealGradientEndColorAlpha = 1f - getPercentPastThreshold(
                     amount, FADE_END_COLOR_OUT_THRESHOLD)
             setRevealGradientBounds(
                     scrim.width * initialWidthMultiplier +
@@ -90,26 +92,49 @@
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
 
-        // TODO(b/193801466): add alpha reveal in the beginning as well
+        scrim.startColorAlpha =
+            getPercentPastThreshold(1 - interpolatedAmount,
+                threshold = 1 - START_COLOR_REVEAL_PERCENTAGE)
+
         scrim.revealGradientEndColorAlpha =
-            1f - LightRevealEffect.getPercentPastThreshold(interpolatedAmount, threshold = 0.6f)
+            1f - getPercentPastThreshold(interpolatedAmount,
+                threshold = REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE)
+
+        // Start changing gradient bounds later to avoid harsh gradient in the beginning
+        val gradientBoundsAmount = lerp(GRADIENT_START_BOUNDS_PERCENTAGE, 1.0f, interpolatedAmount)
 
         if (isVertical) {
             scrim.setRevealGradientBounds(
-                left = scrim.width / 2 - (scrim.width / 2) * interpolatedAmount,
+                left = scrim.width / 2 - (scrim.width / 2) * gradientBoundsAmount,
                 top = 0f,
-                right = scrim.width / 2 + (scrim.width / 2) * interpolatedAmount,
+                right = scrim.width / 2 + (scrim.width / 2) * gradientBoundsAmount,
                 bottom = scrim.height.toFloat()
             )
         } else {
             scrim.setRevealGradientBounds(
                 left = 0f,
-                top = scrim.height / 2 - (scrim.height / 2) * interpolatedAmount,
+                top = scrim.height / 2 - (scrim.height / 2) * gradientBoundsAmount,
                 right = scrim.width.toFloat(),
-                bottom = scrim.height / 2 + (scrim.height / 2) * interpolatedAmount
+                bottom = scrim.height / 2 + (scrim.height / 2) * gradientBoundsAmount
             )
         }
     }
+
+    private companion object {
+        // From which percentage we should start the gradient reveal width
+        // E.g. if 0 - starts with 0px width, 0.3f - starts with 30% width
+        private const val GRADIENT_START_BOUNDS_PERCENTAGE = 0.3f
+
+        // When to start changing alpha color of the gradient scrim
+        // E.g. if 0.6f - starts fading the gradient away at 60% and becomes completely
+        // transparent at 100%
+        private const val REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE = 0.6f
+
+        // When to finish displaying start color fill that reveals the content
+        // E.g. if 0.3f - the content won't be visible at 0% and it will gradually
+        // reduce the alpha until 30% (at this point the color fill is invisible)
+        private const val START_COLOR_REVEAL_PERCENTAGE = 0.3f
+    }
 }
 
 class CircleReveal(
@@ -125,7 +150,7 @@
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         // reveal amount updates already have an interpolator, so we intentionally use the
         // non-interpolated amount
-        val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f)
+        val fadeAmount = getPercentPastThreshold(amount, 0.5f)
         val radius = startRadius + ((endRadius - startRadius) * amount)
         scrim.revealGradientEndColorAlpha = 1f - fadeAmount
         scrim.setRevealGradientBounds(
@@ -153,8 +178,7 @@
 
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN_REVERSE.getInterpolation(amount)
-        val fadeAmount =
-                LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.5f)
+        val fadeAmount = getPercentPastThreshold(interpolatedAmount, 0.5f)
 
         with(scrim) {
             revealGradientEndColorAlpha = 1f - fadeAmount
@@ -216,6 +240,23 @@
     var revealGradientWidth: Float = 0f
     var revealGradientHeight: Float = 0f
 
+    /**
+     * Alpha of the fill that can be used in the beginning of the animation to hide the content.
+     * Normally the gradient bounds are animated from small size so the content is not visible,
+     * but if the start gradient bounds allow to see some content this could be used to make the
+     * reveal smoother. It can help to add fade in effect in the beginning of the animation.
+     * The color of the fill is determined by [revealGradientEndColor].
+     *
+     * 0 - no fill and content is visible, 1 - the content is covered with the start color
+     */
+    var startColorAlpha = 0f
+        set(value) {
+            if (field != value) {
+                field = value
+                invalidate()
+            }
+        }
+
     var revealGradientEndColor: Int = Color.BLACK
         set(value) {
             if (field != value) {
@@ -309,6 +350,10 @@
             return
         }
 
+        if (startColorAlpha > 0f) {
+            canvas.drawColor(updateColorAlpha(revealGradientEndColor, startColorAlpha))
+        }
+
         with(shaderGradientMatrix) {
             setScale(revealGradientWidth, revealGradientHeight, 0f, 0f)
             postTranslate(revealGradientCenter.x, revealGradientCenter.y)
@@ -322,11 +367,15 @@
 
     private fun setPaintColorFilter() {
         gradientPaint.colorFilter = PorterDuffColorFilter(
-                Color.argb(
-                        (revealGradientEndColorAlpha * 255).toInt(),
-                        Color.red(revealGradientEndColor),
-                        Color.green(revealGradientEndColor),
-                        Color.blue(revealGradientEndColor)),
-                PorterDuff.Mode.MULTIPLY)
+            updateColorAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
+            PorterDuff.Mode.MULTIPLY)
     }
+
+    private fun updateColorAlpha(color: Int, alpha: Float): Int =
+        Color.argb(
+            (alpha * 255).toInt(),
+            Color.red(color),
+            Color.green(color),
+            Color.blue(color)
+        )
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index fdbe728..f2060b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -31,8 +31,6 @@
 import android.os.UserHandle
 import android.provider.Settings
 import android.view.View
-import android.view.View.GONE
-import android.view.View.VISIBLE
 import android.view.ViewGroup
 import com.android.settingslib.Utils
 import com.android.systemui.R
@@ -46,21 +44,14 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.AnimatableProperty
-import com.android.systemui.statusbar.notification.PropertyAnimator
-import com.android.systemui.statusbar.notification.stack.AnimationProperties
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.Execution
 import com.android.systemui.util.settings.SecureSettings
+import java.lang.RuntimeException
 import java.util.Optional
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
-private val ANIMATION_PROPERTIES = AnimationProperties()
-        .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD.toLong())
-
 /**
  * Controller for managing the smartspace view on the lockscreen
  */
@@ -81,15 +72,10 @@
     @Main private val handler: Handler,
     optionalPlugin: Optional<BcSmartspaceDataPlugin>
 ) {
-
-    var splitShadeContainer: ViewGroup? = null
-    private var singlePaneContainer: ViewGroup? = null
-
     private var session: SmartspaceSession? = null
     private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
     private lateinit var smartspaceView: SmartspaceView
 
-    // smartspace casted to View
     lateinit var view: View
         private set
 
@@ -97,60 +83,12 @@
     private var showSensitiveContentForManagedUser = false
     private var managedUserHandle: UserHandle? = null
 
-    private var isAod = false
-    private var isSplitShade = false
-
-    fun isSmartspaceEnabled(): Boolean {
+    fun isEnabled(): Boolean {
         execution.assertIsMainThread()
 
         return featureFlags.isSmartspaceEnabled && plugin != null
     }
 
-    fun setKeyguardStatusContainer(container: ViewGroup) {
-        singlePaneContainer = container
-        // reattach smartspace if necessary as this might be a new container
-        updateSmartSpaceContainer()
-    }
-
-    fun onSplitShadeChanged(splitShade: Boolean) {
-        isSplitShade = splitShade
-        updateSmartSpaceContainer()
-    }
-
-    private fun updateSmartSpaceContainer() {
-        if (!isSmartspaceEnabled()) return
-        // in AOD we always want to show smartspace on the left i.e. in singlePaneContainer
-        if (isSplitShade && !isAod) {
-            switchContainerVisibility(
-                    newParent = splitShadeContainer,
-                    oldParent = singlePaneContainer)
-        } else {
-            switchContainerVisibility(
-                    newParent = singlePaneContainer,
-                    oldParent = splitShadeContainer)
-        }
-        requestSmartspaceUpdate()
-    }
-
-    private fun switchContainerVisibility(newParent: ViewGroup?, oldParent: ViewGroup?) {
-        // it might be the case that smartspace was already attached and we just needed to update
-        // visibility, e.g. going from lockscreen -> unlocked -> lockscreen
-        if (newParent?.childCount == 0) {
-            oldParent?.removeAllViews()
-            newParent.addView(buildAndConnectView(newParent))
-        }
-        oldParent?.visibility = GONE
-        newParent?.visibility = VISIBLE
-    }
-
-    fun setSplitShadeSmartspaceAlpha(alpha: Float) {
-        // the other container's alpha is modified as a part of keyguard status view, so we don't
-        // have to do that here
-        if (splitShadeContainer?.visibility == VISIBLE) {
-            splitShadeContainer?.alpha = alpha
-        }
-    }
-
     /**
      * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
      * are idempotent until [disconnect] is called.
@@ -158,7 +96,7 @@
     fun buildAndConnectView(parent: ViewGroup): View {
         execution.assertIsMainThread()
 
-        if (!isSmartspaceEnabled()) {
+        if (!isEnabled()) {
             throw RuntimeException("Cannot build view when not enabled")
         }
 
@@ -244,6 +182,7 @@
         userTracker.removeCallback(userTrackerCallback)
         contentResolver.unregisterContentObserver(settingsObserver)
         configurationController.removeCallback(configChangeListener)
+        statusBarStateController.removeCallback(statusBarStateListener)
         session = null
 
         plugin?.onTargetsAvailable(emptyList())
@@ -259,13 +198,6 @@
         plugin?.unregisterListener(listener)
     }
 
-    fun shiftSplitShadeSmartspace(y: Int, animate: Boolean) {
-        if (splitShadeContainer?.visibility == VISIBLE) {
-            PropertyAnimator.setProperty(splitShadeContainer, AnimatableProperty.Y, y.toFloat(),
-                    ANIMATION_PROPERTIES, animate)
-        }
-    }
-
     private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
         execution.assertIsMainThread()
         val filteredTargets = targets.filter(::filterSmartspaceTarget)
@@ -301,23 +233,6 @@
             execution.assertIsMainThread()
             smartspaceView.setDozeAmount(eased)
         }
-
-        override fun onDozingChanged(isDozing: Boolean) {
-            isAod = isDozing
-            updateSmartSpaceContainer()
-        }
-
-        override fun onStateChanged(newState: Int) {
-            if (newState == StatusBarState.KEYGUARD) {
-                if (isSmartspaceEnabled()) {
-                    updateSmartSpaceContainer()
-                }
-            } else {
-                splitShadeContainer?.visibility = GONE
-                singlePaneContainer?.visibility = GONE
-                disconnect()
-            }
-        }
     }
 
     private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index a0ef1b6..e26fa04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -28,8 +28,6 @@
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
 
-import static java.util.Objects.requireNonNull;
-
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.util.ArrayMap;
@@ -344,14 +342,8 @@
         mPipelineState.incrementTo(STATE_GROUP_STABILIZING);
         stabilizeGroupingNotifs(mNotifList);
 
-        // Step 5: Sort
-        // Assign each top-level entry a section, then sort the list by section and then within
-        // section by our list of custom comparators
-        dispatchOnBeforeSort(mReadOnlyNotifList);
-        mPipelineState.incrementTo(STATE_SORTING);
-        sortList();
 
-        // Step 6: Filter out entries after pre-group filtering, grouping, promoting and sorting
+        // Step 5: Filter out entries after pre-group filtering, grouping and promoting
         // Now filters can see grouping information to determine whether to filter or not.
         dispatchOnBeforeFinalizeFilter(mReadOnlyNotifList);
         mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
@@ -359,6 +351,13 @@
         applyNewNotifList();
         pruneIncompleteGroups(mNotifList);
 
+        // Step 6: Sort
+        // Assign each top-level entry a section, then sort the list by section and then within
+        // section by our list of custom comparators
+        dispatchOnBeforeSort(mReadOnlyNotifList);
+        mPipelineState.incrementTo(STATE_SORTING);
+        sortList();
+
         // Step 7: Lock in our group structure and log anything that's changed since the last run
         mPipelineState.incrementTo(STATE_FINALIZING);
         logChanges();
@@ -837,8 +836,8 @@
     private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
 
         int cmp = Integer.compare(
-                requireNonNull(o1.getSection()).getIndex(),
-                requireNonNull(o2.getSection()).getIndex());
+                o1.getSectionIndex(),
+                o2.getSectionIndex());
 
         if (cmp == 0) {
             for (int i = 0; i < mNotifComparators.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 798bfe7..027ac0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -82,8 +82,8 @@
     public static final int STATE_GROUPING = 4;
     public static final int STATE_TRANSFORMING = 5;
     public static final int STATE_GROUP_STABILIZING = 6;
-    public static final int STATE_SORTING = 7;
-    public static final int STATE_FINALIZE_FILTERING = 8;
+    public static final int STATE_FINALIZE_FILTERING = 7;
+    public static final int STATE_SORTING = 8;
     public static final int STATE_FINALIZING = 9;
 
     @IntDef(prefix = { "STATE_" }, value = {
@@ -94,8 +94,8 @@
             STATE_GROUPING,
             STATE_TRANSFORMING,
             STATE_GROUP_STABILIZING,
-            STATE_SORTING,
             STATE_FINALIZE_FILTERING,
+            STATE_SORTING,
             STATE_FINALIZING,
     })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index c1a63e9..ef1d75e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -53,6 +53,9 @@
             notifList.firstOrNull()?.section?.headerController?.let {
                 children.add(NodeSpecImpl(this, it))
             }
+            notifList.firstOrNull()?.let {
+                children.add(buildNotifNode(it, this))
+            }
             notifList.asSequence().zipWithNext().forEach { (prev, entry) ->
                 // Insert new header if the section has changed between two entries
                 entry.section.takeIf { it != prev.section }?.headerController?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 19c2585..f77c052 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -33,11 +33,23 @@
  */
 public class KeyguardClockPositionAlgorithm {
     /**
+     * How much the clock height influences the shade position.
+     * 0 means nothing, 1 means move the shade up by the height of the clock
+     * 0.5f means move the shade up by half of the size of the clock.
+     */
+    private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
+
+    /**
      * Margin between the bottom of the status view and the notification shade.
      */
     private int mStatusViewBottomMargin;
 
     /**
+     * Height of the parent view - display size in px.
+     */
+    private int mHeight;
+
+    /**
      * Height of {@link KeyguardStatusView}.
      */
     private int mKeyguardStatusHeight;
@@ -56,6 +68,21 @@
     private int mUserSwitchPreferredY;
 
     /**
+     * Whether or not there is a custom clock face on keyguard.
+     */
+    private boolean mHasCustomClock;
+
+    /**
+     * Whether or not the NSSL contains any visible notifications.
+     */
+    private boolean mHasVisibleNotifs;
+
+    /**
+     * Height of notification stack: Sum of height of each notification.
+     */
+    private int mNotificationStackHeight;
+
+    /**
      * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
      * avatar.
      */
@@ -121,7 +148,6 @@
     private int mUnlockedStackScrollerPadding;
 
     private boolean mIsSplitShade;
-    private int mSplitShadeSmartspaceHeight;
 
     /**
      * Refreshes the dimension values.
@@ -144,25 +170,28 @@
      * Sets up algorithm values.
      */
     public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom,
-            float panelExpansion,
-            int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY, float dark,
+            int notificationStackHeight, float panelExpansion, int parentHeight,
+            int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
+            boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
             float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
-            float qsExpansion, int cutoutTopInset, int splitShadeSmartspaceHeight,
-            boolean isSplitShade) {
+            float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
         mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
                 userSwitchHeight);
         mMaxShadeBottom = maxShadeBottom;
+        mNotificationStackHeight = notificationStackHeight;
         mPanelExpansion = panelExpansion;
+        mHeight = parentHeight;
         mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
         mUserSwitchHeight = userSwitchHeight;
         mUserSwitchPreferredY = userSwitchPreferredY;
+        mHasCustomClock = hasCustomClock;
+        mHasVisibleNotifs = hasVisibleNotifs;
         mDarkAmount = dark;
         mOverStretchAmount = overStrechAmount;
         mBypassEnabled = bypassEnabled;
         mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
         mQsExpansion = qsExpansion;
         mCutoutTopInset = cutoutTopInset;
-        mSplitShadeSmartspaceHeight = splitShadeSmartspaceHeight;
         mIsSplitShade = isSplitShade;
     }
 
@@ -184,7 +213,7 @@
         if (mBypassEnabled) {
             return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
         } else if (mIsSplitShade) {
-            return clockYPosition + mSplitShadeSmartspaceHeight;
+            return clockYPosition;
         } else {
             return clockYPosition + mKeyguardStatusHeight;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index ef3dced..3e604ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -205,6 +205,14 @@
         mView.setTopClipping(notificationPanelTop - mView.getTop());
     }
 
+    /**
+     * Updates the {@link KeyguardStatusBarView} state based on the provided values.
+     */
+    public void updateViewState(float alpha, int visibility) {
+        mView.setAlpha(alpha);
+        mView.setVisibility(visibility);
+    }
+
     /** */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardStatusBarView:");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index c71842b..b5eb90e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -117,7 +117,6 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.idle.IdleHostView;
 import com.android.systemui.idle.IdleHostViewController;
 import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.media.KeyguardMediaController;
@@ -148,7 +147,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -241,7 +239,6 @@
     private final HeightListener mHeightListener = new HeightListener();
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
     private final SettingsChangeObserver mSettingsChangeObserver;
-    private final LockscreenSmartspaceController mLockscreenSmartspaceController;
 
     @VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
             new StatusBarStateListener();
@@ -376,8 +373,6 @@
     private IdleHostViewController mIdleHostViewController;
     private LockIconViewController mLockIconViewController;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
-    private FrameLayout mSplitShadeSmartspaceContainer;
-
     private boolean mAnimateNextPositionUpdate;
     private float mQuickQsOffsetHeight;
     private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -808,7 +803,6 @@
             @Main Executor uiExecutor,
             SecureSettings secureSettings,
             SplitShadeHeaderController splitShadeHeaderController,
-            LockscreenSmartspaceController lockscreenSmartspaceController,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             LockscreenGestureLogger lockscreenGestureLogger,
             NotificationRemoteInputManager remoteInputManager,
@@ -852,7 +846,6 @@
         mQSDetailDisplayer = qsDetailDisplayer;
         mFragmentService = fragmentService;
         mSettingsChangeObserver = new SettingsChangeObserver(handler);
-        mLockscreenSmartspaceController = lockscreenSmartspaceController;
         mShouldUseSplitNotificationShade =
                 Utils.shouldUseSplitNotificationShade(mResources);
         mView.setWillNotDraw(!DEBUG);
@@ -949,9 +942,6 @@
         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
         mBigClockContainer = mView.findViewById(R.id.big_clock_container);
         mCommunalView = mView.findViewById(R.id.communal_host);
-        mSplitShadeSmartspaceContainer = mView.findViewById(R.id.split_shade_smartspace_container);
-        mLockscreenSmartspaceController.setSplitShadeContainer(mSplitShadeSmartspaceContainer);
-        mLockscreenSmartspaceController.onSplitShadeChanged(mShouldUseSplitNotificationShade);
 
         UserAvatarView userAvatarView = null;
         KeyguardUserSwitcherView keyguardUserSwitcherView = null;
@@ -971,12 +961,16 @@
                         .getKeyguardStatusBarViewController();
         mKeyguardStatusBarViewController.init();
 
+        IdleViewComponent idleViewComponent = mIdleViewComponentFactory
+                .build(mView.findViewById(R.id.idle_host_view));
+        mIdleHostViewController = idleViewComponent.getIdleHostViewController();
+        mIdleHostViewController.init();
+
         updateViewControllers(
                 mView.findViewById(R.id.keyguard_status_view),
                 userAvatarView,
                 keyguardUserSwitcherView,
-                mCommunalView,
-                mView.findViewById(R.id.idle_host_view));
+                mCommunalView);
         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
                 R.id.notification_stack_scroller);
@@ -1068,18 +1062,13 @@
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
             UserAvatarView userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView,
-            CommunalHostView communalView,
-            IdleHostView idleHostView) {
+            CommunalHostView communalView) {
         // Re-associate the KeyguardStatusViewController
         KeyguardStatusViewComponent statusViewComponent =
                 mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
         mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
         mKeyguardStatusViewController.init();
 
-        IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
-        mIdleHostViewController = idleViewComponent.getIdleHostViewController();
-        mIdleHostViewController.init();
-
         if (communalView != null) {
             CommunalViewComponent communalViewComponent =
                     mCommunalViewComponentFactory.build(communalView);
@@ -1174,7 +1163,7 @@
         mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
 
         updateKeyguardStatusViewAlignment(/* animate= */false);
-        mLockscreenSmartspaceController.onSplitShadeChanged(mShouldUseSplitNotificationShade);
+
         mKeyguardMediaController.refreshMediaPosition();
     }
 
@@ -1251,8 +1240,7 @@
 
         mBigClockContainer.removeAllViews();
         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
-                keyguardUserSwitcherView, mCommunalView,
-                mView.findViewById(R.id.idle_host_view));
+                keyguardUserSwitcherView, mCommunalView);
 
         // Update keyguard bottom area
         int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1441,15 +1429,16 @@
                         ? 1.0f : mInterpolatedDarkAmount;
         mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
                 totalHeight - bottomPadding,
+                mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
                 expandedFraction,
+                totalHeight,
                 mKeyguardStatusViewController.getLockscreenHeight(),
                 userIconHeight,
-                userSwitcherPreferredY,
-                darkamount, mOverStretchAmount,
+                userSwitcherPreferredY, hasCustomClock(),
+                hasVisibleNotifications, darkamount, mOverStretchAmount,
                 bypassEnabled, getUnlockedStackScrollerPadding(),
                 computeQsExpansionFraction(),
                 mDisplayTopInset,
-                mSplitShadeSmartspaceContainer.getHeight(),
                 mShouldUseSplitNotificationShade);
         mClockPositionAlgorithm.run(mClockPositionResult);
         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
@@ -1469,9 +1458,6 @@
                     mClockPositionResult.userSwitchY,
                     animateClock);
         }
-        // no need to translate in X axis - horizontal position is determined by constraints
-        mLockscreenSmartspaceController
-                .shiftSplitShadeSmartspace(mClockPositionResult.clockY, animateClock);
         updateNotificationTranslucency();
         updateClock();
     }
@@ -1647,7 +1633,6 @@
         if (mKeyguardUserSwitcherController != null) {
             mKeyguardUserSwitcherController.setAlpha(alpha);
         }
-        mLockscreenSmartspaceController.setSplitShadeSmartspaceAlpha(alpha);
     }
 
     public void animateToFullShade(long delay) {
@@ -3840,7 +3825,6 @@
     public void dozeTimeTick() {
         mKeyguardBottomArea.dozeTimeTick();
         mKeyguardStatusViewController.dozeTimeTick();
-        mLockscreenSmartspaceController.requestSmartspaceUpdate();
         if (mInterpolatedDarkAmount > 0) {
             positionClockAndNotifications();
         }
@@ -4583,6 +4567,15 @@
                     keyguardFadingAway,
                     goingToFullShade,
                     mBarState);
+
+            if (mCommunalViewController != null) {
+                mCommunalViewController.setKeyguardStatusViewVisibility(
+                        statusBarState,
+                        keyguardFadingAway,
+                        goingToFullShade,
+                        mBarState);
+            }
+
             setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
 
             mBarState = statusBarState;
@@ -4606,8 +4599,9 @@
                     }
                 }
             } else {
-                mKeyguardStatusBar.setAlpha(1f);
-                mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
+                mKeyguardStatusBarViewController.updateViewState(
+                        /* alpha= */ 1f,
+                        keyguardShowing ? View.VISIBLE : View.INVISIBLE);
                 if (keyguardShowing && oldState != mBarState) {
                     if (mQs != null) {
                         mQs.hideImmediately();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e3319e5..87d4543 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -402,7 +402,7 @@
         if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
             mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
             scheduleUpdate();
-        } else if ((oldState == ScrimState.AOD  // leaving doze
+        } else if (((oldState == ScrimState.AOD || oldState == ScrimState.PULSING)  // leaving doze
                 && (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED))
                 || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
             // Scheduling a frame isn't enough when:
@@ -696,7 +696,8 @@
             }
             if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
                 // We're unoccluding the keyguard and don't want to have a bright flash.
-                mNotificationsAlpha = 0f;
+                mNotificationsAlpha = KEYGUARD_SCRIM_ALPHA;
+                mNotificationsTint = ScrimState.KEYGUARD.getNotifTint();
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index e33c9f8..850b986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -229,7 +229,8 @@
                     ? mKeyguardFadingAwayDuration
                     : StatusBar.FADE_KEYGUARD_DURATION;
 
-            mAnimateChange = !mLaunchingAffordanceWithPreview;
+            boolean fromAod = previousState == AOD || previousState == PULSING;
+            mAnimateChange = !mLaunchingAffordanceWithPreview && !fromAod;
 
             mFrontTint = Color.TRANSPARENT;
             mBehindTint = Color.BLACK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index be01308..f07f619 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1028,7 +1028,7 @@
                 mNotificationShadeWindowViewController,
                 mNotificationPanelViewController,
                 mAmbientIndicationContainer);
-        mDozeParameters.addCallback(this::updateLightRevealScrimVisibility);
+        updateLightRevealScrimVisibility();
 
         mConfigurationController.addCallback(mConfigurationListener);
 
@@ -1135,6 +1135,10 @@
                     mStatusBarView.setPanel(mNotificationPanelViewController);
                     mStatusBarView.setScrimController(mScrimController);
                     mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
+                    for (ExpansionChangedListener listener : mExpansionChangedListeners) {
+                        sendInitialExpansionAmount(listener);
+                    }
+
                     mPhoneStatusBarViewController =
                             new PhoneStatusBarViewController(mStatusBarView, mCommandQueue);
                     mPhoneStatusBarViewController.init();
@@ -4161,6 +4165,14 @@
 
     public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
         mExpansionChangedListeners.add(listener);
+        sendInitialExpansionAmount(listener);
+    }
+
+    private void sendInitialExpansionAmount(ExpansionChangedListener expansionChangedListener) {
+        if (mStatusBarView != null) {
+            expansionChangedListener.onExpansionChanged(mStatusBarView.getExpansionFraction(),
+                    mStatusBarView.isExpanded());
+        }
     }
 
     public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
@@ -4174,12 +4186,6 @@
         }
 
         mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
-        if (mFeatureFlags.useNewLockscreenAnimations()
-                && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
-            mLightRevealScrim.setVisibility(View.VISIBLE);
-        } else {
-            mLightRevealScrim.setVisibility(View.GONE);
-        }
     }
 
     private final KeyguardUpdateMonitorCallback mUpdateCallback =
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 119eff6..4316ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -308,8 +308,10 @@
      * Sets a new alt auth interceptor.
      */
     public void setAlternateAuthInterceptor(@NonNull AlternateAuthInterceptor authInterceptor) {
-        mAlternateAuthInterceptor = authInterceptor;
-        resetAlternateAuth(false);
+        if (!Objects.equals(mAlternateAuthInterceptor, authInterceptor)) {
+            mAlternateAuthInterceptor = authInterceptor;
+            resetAlternateAuth(false);
+        }
     }
 
     private void registerListeners() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index dd38f98..a29b72c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -137,7 +137,8 @@
         mStatusBarStateController = statusBarStateController;
         mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
                 keyguardStateController, dozeParameters,
-                unlockedScreenOffAnimationController,  /* animateYPos= */ false);
+                unlockedScreenOffAnimationController,  /* animateYPos= */ false,
+                /* visibleOnCommunal= */ false);
         mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 742b1ab..7bf1601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -251,5 +251,11 @@
          * {@link KeyguardStateController#isFaceAuthEnabled()}.
          */
         default void onFaceAuthEnabledChanged() {}
+
+        /**
+         * Triggered when the notification panel is starting or has finished
+         * fading away on transition to an app.
+         */
+        default void onLaunchTransitionFadingAwayChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 64750bd..f787ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -343,6 +343,7 @@
     @Override
     public void setLaunchTransitionFadingAway(boolean fadingAway) {
         mLaunchTransitionFadingAway = fadingAway;
+        new ArrayList<>(mCallbacks).forEach(Callback::onLaunchTransitionFadingAwayChanged);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 3240ad2..c4b5961 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -176,7 +176,8 @@
                 mUserSwitcherController, this);
         mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
                 keyguardStateController, dozeParameters,
-                unlockedScreenOffAnimationController, /* animateYPos= */ false);
+                unlockedScreenOffAnimationController, /* animateYPos= */ false,
+                /* visibleOnCommunal= */ false);
         mBackground = new KeyguardUserSwitcherScrim(context);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index ce65733..06b0bb2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -28,7 +29,6 @@
 import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 
@@ -104,8 +104,6 @@
     private AnimatableClockView mLargeClockView;
     @Mock
     private FrameLayout mLargeClockFrame;
-    @Mock
-    private ViewGroup mSmartspaceContainer;
 
     private final View mFakeSmartspaceView = new View(mContext);
 
@@ -125,8 +123,6 @@
         when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView);
         when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView);
         when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame);
-        when(mView.findViewById(R.id.keyguard_smartspace_container))
-                .thenReturn(mSmartspaceContainer);
         when(mClockView.getContext()).thenReturn(getContext());
         when(mLargeClockView.getContext()).thenReturn(getContext());
 
@@ -214,7 +210,7 @@
 
     @Test
     public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
-        when(mSmartspaceController.isSmartspaceEnabled()).thenReturn(true);
+        when(mSmartspaceController.isEnabled()).thenReturn(true);
         when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
         mController.init();
 
@@ -223,7 +219,7 @@
 
     @Test
     public void testSmartspaceDisabledShowsKeyguardStatusArea() {
-        when(mSmartspaceController.isSmartspaceEnabled()).thenReturn(false);
+        when(mSmartspaceController.isEnabled()).thenReturn(false);
         mController.init();
 
         assertEquals(View.VISIBLE, mStatusArea.getVisibility());
@@ -231,16 +227,17 @@
 
     @Test
     public void testDetachRemovesSmartspaceView() {
-        when(mSmartspaceController.isSmartspaceEnabled()).thenReturn(true);
+        when(mSmartspaceController.isEnabled()).thenReturn(true);
         when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
         mController.init();
+        verify(mView).addView(eq(mFakeSmartspaceView), anyInt(), any());
 
         ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
         verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
 
         listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
-        verify(mSmartspaceContainer).removeAllViews();
+        verify(mView).removeView(mFakeSmartspaceView);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java
index 548b655..1efcbd6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java
@@ -56,7 +56,7 @@
         when(mTargetView.animate()).thenReturn(mViewPropertyAnimator);
         mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mTargetView,
                 mCommunalStateController, mKeyguardStateController, mDozeParameters,
-                mUnlockedScreenOffAnimationController, false);
+                mUnlockedScreenOffAnimationController, false, false);
     }
 
     @Test
@@ -74,4 +74,19 @@
                 false, StatusBarState.KEYGUARD);
         verify(mTargetView).setVisibility(View.VISIBLE);
     }
+
+    @Test
+    public void testVisibleOnCommunal() {
+        when(mCommunalStateController.getCommunalViewShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+        // Verify that helpers constructed with visibility on communal are not hidden when communal
+        // is present.
+        mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mTargetView,
+                mCommunalStateController, mKeyguardStateController, mDozeParameters,
+                mUnlockedScreenOffAnimationController, false, true);
+        mKeyguardVisibilityHelper.setViewVisibility(StatusBarState.KEYGUARD, false,
+                false, StatusBarState.KEYGUARD);
+        verify(mTargetView).setVisibility(View.VISIBLE);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 5901080..ae009bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -417,6 +417,21 @@
     }
 
     @Test
+    public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
+        // GIVEN overlay was showing and the udfps bouncer is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+        when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+
+        // WHEN the overlay is hidden
+        mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+        mFgExecutor.runAllReady();
+
+        // THEN the udfps bouncer is reset
+        verify(mStatusBarKeyguardViewManager).resetAlternateAuth(eq(true));
+    }
+
+    @Test
     public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
         mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                 IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 17730d9..decec63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -23,6 +23,8 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -33,7 +35,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -43,6 +44,7 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -55,6 +57,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Optional;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -80,6 +83,8 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
     private KeyguardViewMediator mKeyguardViewMediator;
     @Mock
     private ConfigurationController mConfigurationController;
@@ -94,14 +99,15 @@
     private StatusBarStateController.StateListener mStatusBarStateListener;
 
     @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
-    private StatusBar.ExpansionChangedListener mExpansionListener;
+    private List<StatusBar.ExpansionChangedListener> mExpansionListeners;
 
     @Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
             mAltAuthInterceptorCaptor;
     private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
 
-    @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mUpdateMonitorCallbackCaptor;
-    private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
+    @Captor private ArgumentCaptor<KeyguardStateController.Callback>
+            mKeyguardStateControllerCallbackCaptor;
+    private KeyguardStateController.Callback mKeyguardStateControllerCallback;
 
     @Before
     public void setUp() {
@@ -121,13 +127,14 @@
                 mLockscreenShadeTransitionController,
                 mConfigurationController,
                 mSystemClock,
+                mKeyguardStateController,
                 mUdfpsController);
     }
 
     @Test
     public void testRegistersExpansionChangedListenerOnAttached() {
         mController.onViewAttached();
-        captureExpansionListener();
+        captureExpansionListeners();
     }
 
     @Test
@@ -156,11 +163,15 @@
     public void testListenersUnregisteredOnDetached() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
+        captureExpansionListeners();
+        captureKeyguardStateControllerCallback();
         mController.onViewDetached();
 
         verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
-        verify(mStatusBar).removeExpansionChangedListener(mExpansionListener);
+        for (StatusBar.ExpansionChangedListener listener : mExpansionListeners) {
+            verify(mStatusBar).removeExpansionChangedListener(listener);
+        }
+        verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback);
     }
 
     @Test
@@ -179,7 +190,6 @@
     public void testShouldPauseAuthBouncerShowing() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         sendStatusBarStateChanged(StatusBarState.KEYGUARD);
 
@@ -190,7 +200,6 @@
     public void testShouldNotPauseAuthOnKeyguard() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         sendStatusBarStateChanged(StatusBarState.KEYGUARD);
 
@@ -198,10 +207,25 @@
     }
 
     @Test
+    public void testShouldPauseAuthIsLaunchTransitionFadingAway() {
+        // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+
+        // WHEN isLaunchTransitionFadingAway=true
+        captureKeyguardStateControllerCallback();
+        when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true);
+        mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged();
+
+        // THEN pause auth
+        assertTrue(mController.shouldPauseAuth());
+    }
+
+    @Test
     public void testShouldPauseAuthOnShadeLocked() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
 
@@ -212,7 +236,6 @@
     public void testShouldPauseAuthOnShade() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         // WHEN not on keyguard yet (shade = home)
         sendStatusBarStateChanged(StatusBarState.SHADE);
@@ -225,7 +248,6 @@
     public void testShouldPauseAuthAnimatingScreenOffFromShade() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         // WHEN transitioning from home/shade => keyguard + animating screen off
         mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD);
@@ -239,7 +261,6 @@
     public void testDoNotPauseAuthAnimatingScreenOffFromLS() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         // WHEN animating screen off transition from LS => AOD
         sendStatusBarStateChanged(StatusBarState.KEYGUARD);
@@ -333,6 +354,21 @@
         verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
     }
 
+    @Test
+    public void testFadeInWithStatusBarExpansion() {
+        // GIVEN view is attached
+        mController.onViewAttached();
+        captureExpansionListeners();
+        captureKeyguardStateControllerCallback();
+        reset(mView);
+
+        // WHEN status bar expansion is 0
+        updateStatusBarExpansion(0, true);
+
+        // THEN alpha is 0
+        verify(mView).setUnpausedAlpha(0);
+    }
+
     private void sendStatusBarStateChanged(int statusBarState) {
         mStatusBarStateListener.onStateChanged(statusBarState);
     }
@@ -342,9 +378,18 @@
         mStatusBarStateListener = mStateListenerCaptor.getValue();
     }
 
-    private void captureExpansionListener() {
-        verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture());
-        mExpansionListener = mExpansionListenerCaptor.getValue();
+    private void captureExpansionListeners() {
+        verify(mStatusBar, times(2))
+                .addExpansionChangedListener(mExpansionListenerCaptor.capture());
+        // first (index=0) is from super class, UdfpsAnimationViewController.
+        // second (index=1) is from UdfpsKeyguardViewController
+        mExpansionListeners = mExpansionListenerCaptor.getAllValues();
+    }
+
+    private void updateStatusBarExpansion(float expansion, boolean expanded) {
+        for (StatusBar.ExpansionChangedListener listener : mExpansionListeners) {
+            listener.onExpansionChanged(expansion, expanded);
+        }
     }
 
     private void captureAltAuthInterceptor() {
@@ -353,8 +398,11 @@
         mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
     }
 
-    private void captureKeyguardUpdateMonitorCallback() {
-        verify(mKeyguardUpdateMonitor).registerCallback(mUpdateMonitorCallbackCaptor.capture());
-        mKeyguardUpdateMonitorCallback = mUpdateMonitorCallbackCaptor.getValue();
+
+
+    private void captureKeyguardStateControllerCallback() {
+        verify(mKeyguardStateController).addCallback(
+                mKeyguardStateControllerCallbackCaptor.capture());
+        mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
index 9906379..9c6c044 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
@@ -29,6 +29,8 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -61,6 +63,12 @@
     @Mock
     private CommunalHostView mCommunalView;
 
+    @Mock
+    private DozeParameters mDozeParameters;
+
+    @Mock
+    private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+
     private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
 
     private CommunalHostViewController mController;
@@ -76,8 +84,8 @@
         when(mCommunalView.isAttachedToWindow()).thenReturn(true);
 
         mController = new CommunalHostViewController(mFakeExecutor, mCommunalStateController,
-                mKeyguardUpdateMonitor, mKeyguardStateController, mStatusBarStateController,
-                mCommunalView);
+                mKeyguardUpdateMonitor, mKeyguardStateController, mDozeParameters,
+                mUnlockedScreenOffAnimationController, mStatusBarStateController, mCommunalView);
         mController.init();
         mFakeExecutor.runAllReady();
         Mockito.clearInvocations(mCommunalView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 0c94f09..5c4c27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -88,7 +88,7 @@
     private FakeSettings mFakeSettings = new FakeSettings();
     private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
     private TestableLooper mTestableLooper;
-    private DozeSensors mDozeSensors;
+    private TestableDozeSensors mDozeSensors;
     private TriggerSensor mSensorTap;
 
     @Before
@@ -170,6 +170,94 @@
         assertTrue(mSensorTap.mRequested);
     }
 
+    @Test
+    public void testDozeSensorSetListening() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled */ true,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // WHEN we want to listen for the trigger sensor
+        triggerSensor.setListening(true);
+
+        // THEN the sensor is registered
+        assertTrue(triggerSensor.mRegistered);
+    }
+
+    @Test
+    public void testDozeSensorSettingDisabled() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled*/ false,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // WHEN setListening is called
+        triggerSensor.setListening(true);
+
+        // THEN the sensor is not registered
+        assertFalse(triggerSensor.mRegistered);
+    }
+
+    @Test
+    public void testDozeSensorIgnoreSetting() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor that's
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled*/ false,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // GIVEN sensor is listening
+        triggerSensor.setListening(true);
+
+        // WHEN ignoreSetting is called
+        triggerSensor.ignoreSetting(true);
+
+        // THEN the sensor is registered
+        assertTrue(triggerSensor.mRegistered);
+    }
+
+    @Test
+    public void testUpdateListeningAfterAlreadyRegistered() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled*/ true,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // WHEN setListening is called AND updateListening is called
+        triggerSensor.setListening(true);
+        triggerSensor.updateListening();
+
+        // THEN the sensor is still registered
+        assertTrue(triggerSensor.mRegistered);
+    }
+
     private class TestableDozeSensors extends DozeSensors {
 
         TestableDozeSensors() {
@@ -187,5 +275,17 @@
             }
             mSensors = new TriggerSensor[] {mTriggerSensor, mSensorTap};
         }
+
+        public TriggerSensor createDozeSensor(Sensor sensor, boolean settingEnabled,
+                boolean requiresTouchScreen) {
+            return new TriggerSensor(/* sensor */ sensor,
+                    /* setting name */ "test_setting",
+                    /* settingDefault */ settingEnabled,
+                    /* configured */ true,
+                    /* pulseReason*/ 0,
+                    /* reportsTouchCoordinate*/ false,
+                    requiresTouchScreen,
+                    mDozeLog);
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
new file mode 100644
index 0000000..664072f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.idle;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Choreographer;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Instant;
+
+import javax.inject.Provider;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class IdleHostViewControllerTest extends SysuiTestCase {
+    @Mock private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock private PowerManager mPowerManager;
+    @Mock private AsyncSensorManager mSensorManager;
+    @Mock private IdleHostView mIdleHostView;
+    @Mock private InputMonitorFactory mInputMonitorFactory;
+    @Mock private DelayableExecutor mDelayableExecutor;
+    @Mock private Resources mResources;
+    @Mock private Looper mLooper;
+    @Mock private Provider<View> mViewProvider;
+    @Mock private Choreographer mChoreographer;
+    @Mock private KeyguardStateController mKeyguardStateController;
+    @Mock private StatusBarStateController mStatusBarStateController;
+    @Mock private Sensor mSensor;
+    @Mock private DreamHelper mDreamHelper;
+    @Mock private InputMonitorCompat mInputMonitor;
+
+    private final long mTimestamp = Instant.now().toEpochMilli();
+    private KeyguardStateController.Callback mKeyguardStateCallback;
+    private StatusBarStateController.StateListener mStatusBarStateListener;
+    private IdleHostViewController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true);
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mSensor);
+        when(mInputMonitorFactory.getInputMonitor("IdleHostViewController"))
+                .thenReturn(mInputMonitor);
+
+        mController = new IdleHostViewController(mContext,
+                mBroadcastDispatcher, mPowerManager, mSensorManager, mIdleHostView,
+                mInputMonitorFactory, mDelayableExecutor, mResources, mLooper, mViewProvider,
+                mChoreographer, mKeyguardStateController, mStatusBarStateController, mDreamHelper);
+        mController.init();
+        mController.onViewAttached();
+
+        // Captures keyguard state controller callback.
+        ArgumentCaptor<KeyguardStateController.Callback> keyguardStateCallbackCaptor =
+                ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+        verify(mKeyguardStateController).addCallback(keyguardStateCallbackCaptor.capture());
+        mKeyguardStateCallback = keyguardStateCallbackCaptor.getValue();
+
+        // Captures status bar state listener.
+        ArgumentCaptor<StatusBarStateController.StateListener> statusBarStateListenerCaptor =
+                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+        verify(mStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture());
+        mStatusBarStateListener = statusBarStateListenerCaptor.getValue();
+    }
+
+    @Test
+    public void testTimeoutToIdleMode() {
+        // Keyguard showing.
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // Regular ambient lighting.
+        final SensorEvent sensorEvent = new SensorEvent(mSensor, 3, mTimestamp,
+                new float[]{90});
+        mController.onSensorChanged(sensorEvent);
+
+        // Times out.
+        ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
+        verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong());
+        callbackCapture.getValue().run();
+
+        // Verifies start dreaming (idle mode).
+        verify(mDreamHelper).startDreaming(any());
+    }
+
+    @Test
+    public void testTimeoutToLowLightMode() {
+        // Keyguard showing.
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // Captures dream broadcast receiver;
+        ArgumentCaptor<BroadcastReceiver> dreamBroadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mBroadcastDispatcher)
+                .registerReceiver(dreamBroadcastReceiverCaptor.capture(), any());
+        final BroadcastReceiver dreamBroadcastReceiver = dreamBroadcastReceiverCaptor.getValue();
+
+        // Low ambient lighting.
+        final SensorEvent sensorEvent = new SensorEvent(mSensor, 3, mTimestamp,
+                new float[]{5});
+        mController.onSensorChanged(sensorEvent);
+
+        // Verifies it goes to sleep because of low light.
+        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
+
+        mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
+        dreamBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STARTED));
+
+        // User wakes up the device.
+        mStatusBarStateListener.onDozingChanged(false /*isDozing*/);
+        dreamBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
+
+        // Clears power manager invocations to make sure the below dozing was triggered by the
+        // timeout.
+        clearInvocations(mPowerManager);
+
+        // Times out.
+        ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
+        verify(mDelayableExecutor, atLeastOnce()).executeDelayed(callbackCapture.capture(),
+                anyLong());
+        callbackCapture.getValue().run();
+
+        // Verifies go to sleep (low light mode).
+        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
+    }
+
+    @Test
+    public void testTransitionBetweenIdleAndLowLightMode() {
+        // Regular ambient lighting.
+        final SensorEvent sensorEventRegularLight = new SensorEvent(mSensor, 3, mTimestamp,
+                new float[]{90});
+        mController.onSensorChanged(sensorEventRegularLight);
+
+        // Keyguard showing.
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // Times out.
+        ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
+        verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong());
+        callbackCapture.getValue().run();
+
+        // Verifies in idle mode (dreaming).
+        verify(mDreamHelper).startDreaming(any());
+        clearInvocations(mDreamHelper);
+
+        // Ambient lighting becomes dim.
+        final SensorEvent sensorEventLowLight = new SensorEvent(mSensor, 3, mTimestamp,
+                new float[]{5});
+        mController.onSensorChanged(sensorEventLowLight);
+
+        // Verifies in low light mode (dozing).
+        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
+
+        // Ambient lighting becomes bright again.
+        mController.onSensorChanged(sensorEventRegularLight);
+
+        // Verifies in idle mode (dreaming).
+        verify(mDreamHelper).startDreaming(any());
+    }
+
+    @Test
+    public void testStartDozingWhenLowLight() {
+        // Regular ambient lighting.
+        final SensorEvent sensorEventRegularLight = new SensorEvent(mSensor, 3, mTimestamp,
+                new float[]{90});
+        mController.onSensorChanged(sensorEventRegularLight);
+
+        // Keyguard showing.
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // Verifies it doesn't go to sleep yet.
+        verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
+
+        // Ambient lighting becomes dim.
+        final SensorEvent sensorEventLowLight = new SensorEvent(mSensor, 3, mTimestamp,
+                new float[]{5});
+        mController.onSensorChanged(sensorEventLowLight);
+
+        // Verifies it goes to sleep.
+        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 756e984..a2bb0af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -389,6 +389,7 @@
         verify(userTracker).removeCallback(userListener)
         verify(contentResolver).unregisterContentObserver(settingsObserver)
         verify(configurationController).removeCallback(configChangeListener)
+        verify(statusBarStateController).removeCallback(statusBarStateListener)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 2ce22a6..3378003 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -802,13 +802,13 @@
                 .onBeforeTransformGroups(anyList());
         inOrder.verify(promoter, atLeastOnce())
                 .shouldPromoteToTopLevel(any(NotificationEntry.class));
+        inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
+        inOrder.verify(preRenderFilter, atLeastOnce())
+                .shouldFilterOut(any(NotificationEntry.class), anyLong());
         inOrder.verify(mOnBeforeSortListener).onBeforeSort(anyList());
         inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class));
         inOrder.verify(comparator, atLeastOnce())
                 .compare(any(ListEntry.class), any(ListEntry.class));
-        inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
-        inOrder.verify(preRenderFilter, atLeastOnce())
-                .shouldFilterOut(any(NotificationEntry.class), anyLong());
         inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
         inOrder.verify(mOnRenderListListener).onRenderList(anyList());
     }
@@ -1045,12 +1045,32 @@
         // because group changes aren't allowed by the stability manager
         verifyBuiltList(
                 notif(0),
+                notif(2),
                 group(
                         summary(3),
                         child(4),
                         child(5)
-                ),
-                notif(2)
+                )
+        );
+    }
+
+    @Test
+    public void testBrokenGroupNotificationOrdering() {
+        // GIVEN two group children with different sections & without a summary yet
+
+        addGroupChild(0, PACKAGE_2, GROUP_1);
+        addNotif(1, PACKAGE_1);
+        addGroupChild(2, PACKAGE_2, GROUP_1);
+        addGroupChild(3, PACKAGE_2, GROUP_1);
+
+        dispatchBuild();
+
+        // THEN all notifications are not grouped and posted in order by index
+        verifyBuiltList(
+                notif(0),
+                notif(1),
+                notif(2),
+                notif(3)
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 2e76bd7..690b841 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -38,27 +38,35 @@
     private static final float ZERO_DRAG = 0.f;
     private static final float OPAQUE = 1.f;
     private static final float TRANSPARENT = 0.f;
+    private static final boolean HAS_CUSTOM_CLOCK = false;
+    private static final boolean HAS_VISIBLE_NOTIFS = false;
 
     private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
     private KeyguardClockPositionAlgorithm.Result mClockPosition;
+    private int mNotificationStackHeight;
     private float mPanelExpansion;
     private int mKeyguardStatusHeight;
     private float mDark;
+    private boolean mHasCustomClock;
+    private boolean mHasVisibleNotifs;
     private float mQsExpansion;
-    private int mCutoutTopInsetPx = 0;
-    private int mSplitShadeSmartspaceHeightPx = 0;
+    private int mCutoutTopInset = 0; // in pixels
     private boolean mIsSplitShade = false;
 
     @Before
     public void setUp() {
         mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
         mClockPosition = new KeyguardClockPositionAlgorithm.Result();
+
+        mHasCustomClock = HAS_CUSTOM_CLOCK;
+        mHasVisibleNotifs = HAS_VISIBLE_NOTIFS;
     }
 
     @Test
     public void clockPositionTopOfScreenOnAOD() {
-        // GIVEN on AOD and clock has 0 height
+        // GIVEN on AOD and both stack scroll and clock have 0 height
         givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -71,10 +79,11 @@
 
     @Test
     public void clockPositionBelowCutout() {
-        // GIVEN on AOD and clock has 0 height
+        // GIVEN on AOD and both stack scroll and clock have 0 height
         givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
-        mCutoutTopInsetPx = 300;
+        mCutoutTopInset = 300;
         // WHEN the clock position algorithm is run
         positionClock();
         // THEN the clock Y position is below the cutout
@@ -88,6 +97,7 @@
     public void clockPositionAdjustsForKeyguardStatusOnAOD() {
         // GIVEN on AOD with a clock of height 100
         givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = 100;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -102,6 +112,7 @@
     public void clockPositionLargeClockOnAOD() {
         // GIVEN on AOD with a full screen clock
         givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -114,8 +125,9 @@
 
     @Test
     public void clockPositionTopOfScreenOnLockScreen() {
-        // GIVEN on lock screen with clock of 0 height
+        // GIVEN on lock screen with stack scroll and clock of 0 height
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -126,9 +138,24 @@
     }
 
     @Test
+    public void clockPositionWithStackScrollExpandOnLockScreen() {
+        // GIVEN on lock screen with stack scroll of height 500
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock Y position stays to the top
+        assertThat(mClockPosition.clockY).isEqualTo(0);
+        // AND the clock is positioned on the left.
+        assertThat(mClockPosition.clockX).isEqualTo(0);
+    }
+
+    @Test
     public void clockPositionWithPartialDragOnLockScreen() {
         // GIVEN dragging up on lock screen
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         mPanelExpansion = 0.5f;
         // WHEN the clock position algorithm is run
@@ -144,6 +171,7 @@
     public void clockPositionWithFullDragOnLockScreen() {
         // GIVEN the lock screen is dragged up
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         mPanelExpansion = 0.f;
         // WHEN the clock position algorithm is run
@@ -156,6 +184,7 @@
     public void largeClockOnLockScreenIsTransparent() {
         // GIVEN on lock screen with a full screen clock
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -165,8 +194,9 @@
 
     @Test
     public void notifPositionTopOfScreenOnAOD() {
-        // GIVEN on AOD and clock has 0 height
+        // GIVEN on AOD and both stack scroll and clock have 0 height
         givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -178,6 +208,7 @@
     public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
         // GIVEN on AOD and clock has a nonzero height
         givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = 100;
         // WHEN the position algorithm is run
         positionClock();
@@ -189,6 +220,7 @@
     public void notifPositionWithLargeClockOnAOD() {
         // GIVEN on AOD and clock has a nonzero height
         givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -198,8 +230,9 @@
 
     @Test
     public void notifPositionMiddleOfScreenOnLockScreen() {
-        // GIVEN on lock screen and clock has 0 height
+        // GIVEN on lock screen and both stack scroll and clock have 0 height
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -208,21 +241,47 @@
     }
 
     @Test
+    public void notifPositionAdjustsForStackHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for keyguard status height
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+    }
+
+    @Test
     public void notifPositionAdjustsForClockHeightOnLockScreen() {
         // GIVEN on lock screen and stack scroller has a nonzero height
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = 200;
         // WHEN the position algorithm is run
         positionClock();
+        // THEN the notif padding adjusts for both clock and notif stack.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
+    }
 
+    @Test
+    public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = 200;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notifs are placed below the statusview
         assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
     }
 
     @Test
     public void notifPositionAlignedWithClockInSplitShadeMode() {
+        // GIVEN on lock screen and split shade mode
         givenLockScreen();
         mIsSplitShade = true;
-        mKeyguardStatusHeight = 200;
+        mHasCustomClock = true;
         // WHEN the position algorithm is run
         positionClock();
         // THEN the notif padding DOESN'T adjust for keyguard status height.
@@ -230,20 +289,10 @@
     }
 
     @Test
-    public void notifPositionAdjustedBySmartspaceHeightInSplitShadeMode() {
-        givenLockScreen();
-        mSplitShadeSmartspaceHeightPx = 200;
-        mIsSplitShade = true;
-        // WHEN the position algorithm is run
-        positionClock();
-
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
-    }
-
-    @Test
     public void notifPositionWithLargeClockOnLockScreen() {
         // GIVEN on lock screen and clock has a nonzero height
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -255,6 +304,7 @@
     public void notifPositionWithFullDragOnLockScreen() {
         // GIVEN the lock screen is dragged up
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         mPanelExpansion = 0.f;
         // WHEN the clock position algorithm is run
@@ -267,18 +317,19 @@
     public void notifPositionWithLargeClockFullDragOnLockScreen() {
         // GIVEN the lock screen is dragged up and a full screen clock
         givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         mPanelExpansion = 0.f;
         // WHEN the clock position algorithm is run
         positionClock();
-
+        // THEN the notif padding is zero.
         assertThat(mClockPosition.stackScrollerPadding).isEqualTo(
                 (int) (mKeyguardStatusHeight * .667f));
     }
 
     @Test
     public void clockHiddenWhenQsIsExpanded() {
-        // GIVEN on the lock screen with visible notifications
+        // GIVEN on the lock screen with a custom clock and visible notifications
         givenLockScreen();
         mQsExpansion = 1;
         // WHEN the clock position algorithm is run
@@ -298,12 +349,12 @@
     }
 
     private void positionClock() {
-        mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT,
-                mPanelExpansion, mKeyguardStatusHeight,
+        mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
+                mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight,
                 0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */,
-                mDark, ZERO_DRAG, false /* bypassEnabled */,
+                mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
                 0 /* unlockedStackScrollerPadding */, mQsExpansion,
-                mCutoutTopInsetPx, mSplitShadeSmartspaceHeightPx, mIsSplitShade);
+                mCutoutTopInset, mIsSplitShade);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 85e17ba..d92816e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -25,6 +25,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.LayoutInflater;
+import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -162,4 +163,18 @@
 
         assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(0);
     }
+
+    @Test
+    public void updateViewState_alphaAndVisibilityGiven_viewUpdated() {
+        // Verify the initial values so we know the method triggers changes.
+        assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(1f);
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+
+        float newAlpha = 0.5f;
+        int newVisibility = View.INVISIBLE;
+        mController.updateViewState(newAlpha, newVisibility);
+
+        assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(newAlpha);
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(newVisibility);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index e9d698f..7338fad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -63,7 +63,6 @@
 import android.view.ViewStub;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.constraintlayout.widget.ConstraintSet;
@@ -121,7 +120,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -344,10 +342,6 @@
     @Mock
     private ControlsComponent mControlsComponent;
     @Mock
-    private LockscreenSmartspaceController mLockscreenSmartspaceController;
-    @Mock
-    private FrameLayout mSplitShadeSmartspaceContainer;
-    @Mock
     private LockscreenGestureLogger mLockscreenGestureLogger;
 
     private SysuiStatusBarStateController mStatusBarStateController;
@@ -399,8 +393,6 @@
         when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
         when(mView.findViewById(R.id.keyguard_status_view))
                 .thenReturn(mock(KeyguardStatusView.class));
-        when(mView.findViewById(R.id.split_shade_smartspace_container))
-                .thenReturn(mSplitShadeSmartspaceContainer);
         mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
         mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
         mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
@@ -514,7 +506,6 @@
                 new FakeExecutor(new FakeSystemClock()),
                 mSecureSettings,
                 mSplitShadeHeaderController,
-                mLockscreenSmartspaceController,
                 mUnlockedScreenOffAnimationController,
                 mLockscreenGestureLogger,
                 mNotificationRemoteInputManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 1953903..9b8e259 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.ScrimController.KEYGUARD_SCRIM_ALPHA;
 import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
 import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -741,9 +744,9 @@
     }
 
     @Test
-    public void transitionToUnlockedFromAod() {
-        // Simulate unlock with fingerprint
-        mScrimController.transitionTo(ScrimState.AOD);
+    public void transitionToUnlockedFromOff() {
+        // Simulate unlock with fingerprint without AOD
+        mScrimController.transitionTo(ScrimState.OFF);
         mScrimController.setPanelExpansion(0f);
         finishAnimationsImmediately();
         mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -769,6 +772,28 @@
     }
 
     @Test
+    public void transitionToUnlockedFromAod() {
+        // Simulate unlock with fingerprint
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.setPanelExpansion(0f);
+        finishAnimationsImmediately();
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+
+        finishAnimationsImmediately();
+
+        // All scrims should be transparent at the end of fade transition.
+        assertScrimAlpha(Map.of(
+                mScrimInFront, TRANSPARENT,
+                mScrimBehind, TRANSPARENT));
+
+        // Make sure at the very end of the animation, we're reset to transparent
+        assertScrimTinted(Map.of(
+                mScrimInFront, false,
+                mScrimBehind, true
+        ));
+    }
+
+    @Test
     public void scrimBlanksBeforeLeavingAod() {
         // Simulate unlock with fingerprint
         mScrimController.transitionTo(ScrimState.AOD);
@@ -1082,6 +1107,26 @@
     }
 
     @Test
+    public void testDoesntAnimate_whenUnlocking() {
+        // LightRevealScrim will animate the transition, we should only hide the keyguard scrims.
+        ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+        ScrimState.UNLOCKED.prepare(ScrimState.PULSING);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isFalse();
+
+        ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+        ScrimState.UNLOCKED.prepare(ScrimState.AOD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isFalse();
+
+        // LightRevealScrim doesn't animate when AOD is disabled. We need to use the legacy anim.
+        ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+        ScrimState.UNLOCKED.prepare(ScrimState.OFF);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+    }
+
+    @Test
     public void testScrimsVisible_whenShadeVisible_clippingQs() {
         mScrimController.setClipsQsScrim(true);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -1143,8 +1188,10 @@
         mScrimController.transitionTo(ScrimState.KEYGUARD);
         mScrimController.setUnocclusionAnimationRunning(true);
 
-        assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.0f, /* expansion */ 0.0f);
-        assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.0f, /* expansion */ 1.0f);
+        assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ KEYGUARD_SCRIM_ALPHA,
+                /* expansion */ 0.0f);
+        assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ KEYGUARD_SCRIM_ALPHA,
+                /* expansion */ 1.0f);
 
         // Verify normal behavior after
         mScrimController.setUnocclusionAnimationRunning(false);
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index e390ae28..6529b11 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -479,6 +479,35 @@
             @AppIdInt int recipientAppId, int visibleUid,
             boolean direct);
 
+    /**
+     * Grants implicit access based on an interaction between two apps. This grants access to the
+     * from one application to the other's package metadata.
+     * <p>
+     * When an application explicitly tries to interact with another application [via an
+     * activity, service or provider that is either declared in the caller's
+     * manifest via the {@code <queries>} tag or has been exposed via the target apps manifest using
+     * the {@code visibleToInstantApp} attribute], the target application must be able to see
+     * metadata about the calling app. If the calling application uses an implicit intent [ie
+     * action VIEW, category BROWSABLE], it remains hidden from the launched app.
+     * <p>
+     * If an interaction is not explicit, the {@code direct} argument should be set to false as
+     * visibility should not be granted in some cases. This method handles that logic.
+     * <p>
+     * @param userId the user
+     * @param intent the intent that triggered the grant
+     * @param recipientAppId The app ID of the application that is being given access to {@code
+     *                       visibleUid}
+     * @param visibleUid The uid of the application that is becoming accessible to {@code
+     *                   recipientAppId}
+     * @param direct true if the access is being made due to direct interaction between visibleUid
+     *               and recipientAppId.
+     * @param retainOnUpdate true if the implicit access is retained across package update.
+     */
+    public abstract void grantImplicitAccess(
+            @UserIdInt int userId, Intent intent,
+            @AppIdInt int recipientAppId, int visibleUid,
+            boolean direct, boolean retainOnUpdate);
+
     public abstract boolean isInstantAppInstallerComponent(ComponentName component);
     /**
      * Prunes instant apps and state associated with uninstalled
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b202c23..18cc154 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -194,6 +194,9 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetManagerInternal;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
@@ -544,6 +547,16 @@
     static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
 
     /**
+     * It is now required for apps to explicitly set either
+     * {@link android.content.Context#RECEIVER_EXPORTED} or
+     * {@link android.content.Context#RECEIVER_NOT_EXPORTED} when registering a receiver for an
+     * unprotected broadcast in code.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L;
+
+    /**
      * The maximum number of bytes that {@link #setProcessStateSummary} accepts.
      *
      * @see {@link android.app.ActivityManager#setProcessStateSummary(byte[])}
@@ -12416,6 +12429,11 @@
         ProcessRecord callerApp = null;
         final boolean visibleToInstantApps
                 = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
+        // Dynamic receivers are exported by default for versions prior to T
+        final boolean exported =
+                ((flags & Context.RECEIVER_EXPORTED) != 0
+                        || (!Compatibility.isChangeEnabled(161145287)));
+
         int callingUid;
         int callingPid;
         boolean instantApp;
@@ -12452,8 +12470,10 @@
                 noAction.add(null);
                 actions = noAction.iterator();
             }
+            boolean onlyProtectedBroadcasts = actions.hasNext();
 
-            // Collect stickies of users
+            // Collect stickies of users and check if broadcast is only registered for protected
+            // broadcasts
             int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
             while (actions.hasNext()) {
                 String action = actions.next();
@@ -12469,6 +12489,31 @@
                         }
                     }
                 }
+                if (onlyProtectedBroadcasts) {
+                    try {
+                        onlyProtectedBroadcasts &=
+                                AppGlobals.getPackageManager().isProtectedBroadcast(action);
+                    } catch (RemoteException e) {
+                        onlyProtectedBroadcasts = false;
+                        Slog.w(TAG, "Remote exception", e);
+                    }
+                }
+            }
+
+            // If the change is enabled, but neither exported or not exported is set, we need to log
+            // an error so the consumer can know to explicitly set the value for their flag
+            if (!onlyProtectedBroadcasts && (Compatibility.isChangeEnabled(161145287)
+                    && (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED))
+                    == 0)) {
+                Slog.e(TAG,
+                        callerPackage + ": Targeting T+ (version " + Build.VERSION_CODES.TIRAMISU
+                                + " and above) requires that one of RECEIVER_EXPORTED or "
+                                + "RECEIVER_NOT_EXPORTED be specified when registering a receiver");
+            } else if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
+                    (flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
+                throw new IllegalArgumentException(
+                        "Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+                                + "flag");
             }
         }
 
@@ -12557,7 +12602,8 @@
                         + " callerPackage is " + callerPackage);
             }
             BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
-                    receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps);
+                    receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
+                    exported);
             if (rl.containsFilter(filter)) {
                 Slog.w(TAG, "Receiver with filter " + filter
                         + " already registered for pid " + rl.pid
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 50e06c3..8e38f0a 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -34,10 +34,12 @@
     final int owningUserId;
     final boolean instantApp;
     final boolean visibleToInstantApp;
+    final boolean exported;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
             String _packageName, String _featureId, String _receiverId, String _requiredPermission,
-            int _owningUid, int _userId, boolean _instantApp, boolean _visibleToInstantApp) {
+            int _owningUid, int _userId, boolean _instantApp, boolean _visibleToInstantApp,
+            boolean _exported) {
         super(_filter);
         receiverList = _receiverList;
         packageName = _packageName;
@@ -48,6 +50,7 @@
         owningUserId = _userId;
         instantApp = _instantApp;
         visibleToInstantApp = _visibleToInstantApp;
+        exported = _exported;
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 503b3a9..32d93ab 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -775,6 +775,22 @@
             skip = true;
         }
 
+        // Ensure that broadcasts are only sent to other apps if they are explicitly marked as
+        // exported, or are System level broadcasts
+        if (!skip && !filter.exported && Process.SYSTEM_UID != r.callingUid
+                && filter.receiverList.uid != r.callingUid) {
+
+            Slog.w(TAG, "Exported Denial: sending "
+                    + r.intent.toString()
+                    + ", action: " + r.intent.getAction()
+                    + " from " + r.callerPackage
+                    + " (uid=" + r.callingUid + ")"
+                    + " due to receiver " + filter.receiverList.app
+                    + " (uid " + filter.receiverList.uid + ")"
+                    + " not specifying RECEIVER_EXPORTED");
+            // skip = true;
+        }
+
         if (skip) {
             r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
             return;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 43fd7ba..bd7baab 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2825,8 +2825,9 @@
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
         }
-        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
-                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
+        // validate calling package and app op
+        if (!checkNoteAppOp(
+                STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage, attributionTag)) {
             return;
         }
 
@@ -3536,8 +3537,8 @@
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
         }
-        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
-                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
+        if (!checkNoteAppOp(
+                STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage, attributionTag)) {
             return;
         }
 
@@ -3956,8 +3957,8 @@
             uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
         }
         // If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
-        if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid,
-                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
+        if (!mute && !checkNoteAppOp(
+                AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage, attributionTag)) {
             return;
         }
         if (userId != UserHandle.getCallingUserId() &&
@@ -4090,8 +4091,8 @@
                         ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE);
 
         // If OP_MUTE_MICROPHONE is set, disallow unmuting.
-        if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid,
-                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
+        if (!on && !checkNoteAppOp(
+                AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage, attributionTag)) {
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "disallow unmuting").record();
             return;
         }
@@ -10574,4 +10575,32 @@
         }
         mFullVolumeDevices.remove(audioSystemDeviceOut);
     }
+
+    //====================
+    // Helper functions for app ops
+    //====================
+    /**
+     * Validates, and notes an app op for a given uid and package name.
+     * Validation comes from exception catching: a security exception indicates the package
+     * doesn't exist, an IAE indicates the uid and package don't match. The code only checks
+     * if exception was thrown for robustness to code changes in op validation
+     * @param op the app op to check
+     * @param uid the uid of the caller
+     * @param packageName the package to check
+     * @return true if the origin of the call is valid (no uid / package mismatch) and the caller
+     *      is allowed to perform the operation
+     */
+    private boolean checkNoteAppOp(int op, int uid, String packageName, String attributionTag) {
+        try {
+            if (mAppOps.noteOp(op, uid, packageName, attributionTag, null)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error noting op:" + op + " on uid:" + uid + " for package:"
+                    + packageName, e);
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 86c9ca9..48eea89 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -230,8 +230,6 @@
                 info.rotation = mOverrideDisplayInfo.rotation;
                 info.displayCutout = mOverrideDisplayInfo.displayCutout;
                 info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
-                info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
-                info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
                 info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
                 info.shouldConstrainMetricsForLauncher =
                         mOverrideDisplayInfo.shouldConstrainMetricsForLauncher;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 56b77b5..15e671c 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -97,6 +97,13 @@
     private final SparseSetArray<Integer> mImplicitlyQueryable = new SparseSetArray<>();
 
     /**
+     * This contains a list of app UIDs that are implicitly queryable because another app explicitly
+     * interacted with it, but could keep across package updates. For example, if application A
+     * grants persistable uri permission to application B; regardless of any manifest entries.
+     */
+    private final SparseSetArray<Integer> mRetainedImplicitlyQueryable = new SparseSetArray<>();
+
+    /**
      * A mapping from the set of App IDs that query other App IDs via package name to the
      * list of packages that they can see.
      */
@@ -256,6 +263,7 @@
      */
     private AppsFilter(AppsFilter orig) {
         Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable);
+        Snapshots.copy(mRetainedImplicitlyQueryable, orig.mRetainedImplicitlyQueryable);
         Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage);
         Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent);
         Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary);
@@ -633,15 +641,19 @@
      *
      * @param recipientUid the uid gaining visibility of the {@code visibleUid}.
      * @param visibleUid   the uid becoming visible to the {@recipientUid}
+     * @param retainOnUpdate  if the implicit access retained across package updates.
      * @return {@code true} if implicit access was not already granted.
      */
-    public boolean grantImplicitAccess(int recipientUid, int visibleUid) {
+    public boolean grantImplicitAccess(int recipientUid, int visibleUid, boolean retainOnUpdate) {
         if (recipientUid == visibleUid) {
             return false;
         }
-        final boolean changed = mImplicitlyQueryable.add(recipientUid, visibleUid);
+        final boolean changed = retainOnUpdate
+                ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid)
+                : mImplicitlyQueryable.add(recipientUid, visibleUid);
         if (changed && DEBUG_LOGGING) {
-            Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
+            Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: "
+                    + recipientUid + " -> " + visibleUid);
         }
         synchronized (mCacheLock) {
             if (mShouldFilterCache != null) {
@@ -677,7 +689,7 @@
         try {
             if (isReplace) {
                 // let's first remove any prior rules for this package
-                removePackage(newPkgSetting);
+                removePackage(newPkgSetting, true /*isReplace*/);
             }
             mStateProvider.runWithState((settings, users) -> {
                 ArraySet<String> additionalChangedPackages =
@@ -1078,8 +1090,9 @@
      * Removes a package for consideration when filtering visibility between apps.
      *
      * @param setting the setting of the package being removed.
+     * @param isReplace if the package is being replaced.
      */
-    public void removePackage(PackageSetting setting) {
+    public void removePackage(PackageSetting setting, boolean isReplace) {
         mStateProvider.runWithState((settings, users) -> {
             final int userCount = users.length;
             for (int u = 0; u < userCount; u++) {
@@ -1089,6 +1102,16 @@
                 for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
                     mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid);
                 }
+
+                if (isReplace) {
+                    continue;
+                }
+
+                mRetainedImplicitlyQueryable.remove(removingUid);
+                for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) {
+                    mRetainedImplicitlyQueryable.remove(
+                            mRetainedImplicitlyQueryable.keyAt(i), removingUid);
+                }
             }
 
             if (!mQueriesViaComponentRequireRecompute) {
@@ -1414,6 +1437,24 @@
 
             try {
                 if (DEBUG_TRACING) {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable");
+                }
+                final int targetUid = UserHandle.getUid(targetUserId, targetAppId);
+                if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting,
+                                "retained implicitly queryable for user");
+                    }
+                    return false;
+                }
+            } finally {
+                if (DEBUG_TRACING) {
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+            }
+
+            try {
+                if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
                 }
                 final String targetName = targetPkg.getPackageName();
@@ -1550,6 +1591,9 @@
             dumpQueriesMap(pw,
                     filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
                     mImplicitlyQueryable, "      ", expandPackages);
+            dumpQueriesMap(pw,
+                    filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
+                    mRetainedImplicitlyQueryable, "      ", expandPackages);
         }
         pw.println("  queryable via uses-library:");
         dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, "    ", expandPackages);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 114dec6..3cd41d3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18051,7 +18051,8 @@
                 synchronized (mLock) {
                     mDomainVerificationManager.clearPackage(deletedPs.name);
                     mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName);
-                    mAppsFilter.removePackage(getPackageSetting(packageName));
+                    mAppsFilter.removePackage(getPackageSetting(packageName),
+                            false /* isReplace */);
                     removedAppId = mSettings.removePackageLPw(packageName);
                     if (outInfo != null) {
                         outInfo.mRemovedAppId = removedAppId;
@@ -23787,6 +23788,13 @@
         @Override
         public void grantImplicitAccess(int userId, Intent intent,
                 int recipientAppId, int visibleUid, boolean direct) {
+            grantImplicitAccess(userId, intent, recipientAppId, visibleUid, direct,
+                    false /* retainOnUpdate */);
+        }
+
+        @Override
+        public void grantImplicitAccess(int userId, Intent intent,
+                int recipientAppId, int visibleUid, boolean direct, boolean retainOnUpdate) {
             synchronized (mLock) {
                 final AndroidPackage visiblePackage = getPackage(visibleUid);
                 final int recipientUid = UserHandle.getUid(userId, recipientAppId);
@@ -23807,7 +23815,8 @@
                     accessGranted = mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
                             recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
                 } else {
-                    accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
+                    accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
+                            retainOnUpdate);
                 }
                 if (accessGranted) {
                     ApplicationPackageManager.invalidateGetPackagesForUidCache();
@@ -24747,7 +24756,7 @@
         }
         int visibleUid = providerInfo.applicationInfo.uid;
         mPmInternal.grantImplicitAccess(userId, null /*Intent*/, UserHandle.getAppId(recipientUid),
-                visibleUid, false);
+                visibleUid, false /*direct*/);
     }
 
     boolean canHaveOatDir(String packageName) {
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index b1b46af..11aed8d 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -497,13 +497,10 @@
                 if (permissionTree.getUid() == UserHandle.getAppId(callingUid)) {
                     return permissionTree;
                 }
-                throw new SecurityException("Calling uid " + callingUid
-                        + " is not allowed to add to permission tree "
-                        + permissionTree.getName() + " owned by uid "
-                        + permissionTree.getUid());
             }
         }
-        throw new SecurityException("No permission tree found for " + permissionName);
+        throw new SecurityException("Calling uid " + callingUid
+            + " is not allowed to add to or remove from the permission tree");
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index fd1995d..b17257a 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -706,10 +706,10 @@
                                         sourcePkg, targetPkg, targetUid, grantUri);
                                 perm.initPersistedModes(modeFlags, createdTime);
                                 mPmInternal.grantImplicitAccess(
-                                        targetUserId, null,
+                                        targetUserId, null /* intent */,
                                         UserHandle.getAppId(targetUid),
                                         pi.applicationInfo.uid,
-                                        false /* direct */);
+                                        false /* direct */, true /* retainOnUpdate */);
                             }
                         } else {
                             Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -773,8 +773,9 @@
             perm = findOrCreateUriPermissionLocked(pi.packageName, targetPkg, targetUid, grantUri);
         }
         perm.grantModes(modeFlags, owner);
-        mPmInternal.grantImplicitAccess(UserHandle.getUserId(targetUid), null,
-                UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/);
+        mPmInternal.grantImplicitAccess(UserHandle.getUserId(targetUid), null /*intent*/,
+                UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/,
+                (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0);
     }
 
     /** Like grantUriPermissionUnchecked, but takes an Intent. */
@@ -1187,7 +1188,7 @@
             // provider supports granting permissions
             mPmInternal.grantImplicitAccess(
                     UserHandle.getUserId(targetUid), null,
-                    UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false);
+                    UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/);
             return -1;
         }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 94e6ca2..2b57179 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -670,6 +670,7 @@
             if (magnifying) {
                 switch (transition) {
                     case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
+                    case WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN:
                     case WindowManager.TRANSIT_OLD_TASK_OPEN:
                     case WindowManager.TRANSIT_OLD_TASK_TO_FRONT:
                     case WindowManager.TRANSIT_OLD_WALLPAPER_OPEN:
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index ce1592d..c715c39 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -59,6 +59,8 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
@@ -89,6 +91,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -157,6 +160,9 @@
     private final ArrayList<TransitionInfo> mTransitionInfoList = new ArrayList<>();
     /** Map : Last launched activity => {@link TransitionInfo} */
     private final ArrayMap<ActivityRecord, TransitionInfo> mLastTransitionInfo = new ArrayMap<>();
+    /** SparseArray : Package UID => {@link PackageCompatStateInfo} */
+    private final SparseArray<PackageCompatStateInfo> mPackageUidToCompatStateInfo =
+            new SparseArray<>(0);
 
     private ArtManagerInternal mArtManagerInternal;
     private final StringBuilder mStringBuilder = new StringBuilder();
@@ -452,6 +458,15 @@
         }
     }
 
+    /** Information about the App Compat state logging associated with a package UID . */
+    private static final class PackageCompatStateInfo {
+        /** All activities that have a visible state. */
+        final ArrayList<ActivityRecord> mVisibleActivities = new ArrayList<>();
+        /** The last logged state. */
+        int mLastLoggedState = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        @Nullable ActivityRecord mLastLoggedActivity;
+    }
+
     ActivityMetricsLogger(ActivityTaskSupervisor supervisor, Looper looper) {
         mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
         mSupervisor = supervisor;
@@ -703,6 +718,7 @@
         // Always calculate the delay because the caller may need to know the individual drawn time.
         info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
         info.removePendingDrawActivity(r);
+        info.updatePendingDraw(false /* keepInitializing */);
         final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
         if (info.mLoggedTransitionStarting && info.allDrawn()) {
             done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
@@ -774,6 +790,21 @@
     /** Makes sure that the reference to the removed activity is cleared. */
     void notifyActivityRemoved(@NonNull ActivityRecord r) {
         mLastTransitionInfo.remove(r);
+
+        final int packageUid = r.info.applicationInfo.uid;
+        final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
+        if (compatStateInfo == null) {
+            return;
+        }
+
+        compatStateInfo.mVisibleActivities.remove(r);
+        if (compatStateInfo.mLastLoggedActivity == r) {
+            compatStateInfo.mLastLoggedActivity = null;
+        }
+        if (compatStateInfo.mVisibleActivities.isEmpty()) {
+            // No need to keep the entry if there are no visible activities.
+            mPackageUidToCompatStateInfo.remove(packageUid);
+        }
     }
 
     /**
@@ -1270,6 +1301,115 @@
                 memoryStat.swapInBytes);
     }
 
+    /**
+     * Logs the current App Compat state of the given {@link ActivityRecord} with its package
+     * UID, if all of the following hold:
+     * <ul>
+     *   <li>The current state is different than the last logged state for the package UID of the
+     *   activity.
+     *   <li>If the current state is NOT_VISIBLE, there is a previously logged state for the
+     *   package UID and there are no other visible activities with the same package UID.
+     *   <li>The last logged activity with the same package UID is either {@code activity} or the
+     *   last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
+     * </ul>
+     *
+     * <p>If the current state is NOT_VISIBLE and the previous state which was logged by {@code
+     * activity} wasn't, looks for the first visible activity with the same package UID that has
+     * a letterboxed state, or a non-letterboxed state if there isn't one, and logs that state.
+     *
+     * <p>This method assumes that the caller is wrapping the call with a synchronized block so
+     * that there won't be a race condition between two activities with the same package.
+     */
+    void logAppCompatState(@NonNull ActivityRecord activity) {
+        final int packageUid = activity.info.applicationInfo.uid;
+        final int state = activity.getAppCompatState();
+
+        if (!mPackageUidToCompatStateInfo.contains(packageUid)) {
+            mPackageUidToCompatStateInfo.put(packageUid, new PackageCompatStateInfo());
+        }
+        final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
+        final int lastLoggedState = compatStateInfo.mLastLoggedState;
+        final ActivityRecord lastLoggedActivity = compatStateInfo.mLastLoggedActivity;
+
+        final boolean isVisible = state != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+        if (isVisible && !visibleActivities.contains(activity)) {
+            visibleActivities.add(activity);
+        } else if (!isVisible) {
+            visibleActivities.remove(activity);
+            if (visibleActivities.isEmpty()) {
+                // No need to keep the entry if there are no visible activities.
+                mPackageUidToCompatStateInfo.remove(packageUid);
+            }
+        }
+
+        if (state == lastLoggedState) {
+            // We don’t want to log the same state twice or log DEFAULT_NOT_VISIBLE before any
+            // visible state was logged.
+            return;
+        }
+
+        if (!isVisible && !visibleActivities.isEmpty()) {
+            // There is another visible activity for this package UID.
+            if (activity == lastLoggedActivity) {
+                // Make sure a new visible state is logged if needed.
+                findAppCompatStateToLog(compatStateInfo, packageUid);
+            }
+            return;
+        }
+
+        if (activity != lastLoggedActivity
+                && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE
+                && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED) {
+            // Another visible activity for this package UID has logged a letterboxed state.
+            return;
+        }
+
+        logAppCompatStateInternal(activity, state, packageUid, compatStateInfo);
+    }
+
+    /**
+     * Looks for the first visible activity in {@code compatStateInfo} that has a letterboxed
+     * state, or a non-letterboxed state if there isn't one, and logs that state for the given
+     * {@code packageUid}.
+     */
+    private void findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid) {
+        final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+
+        ActivityRecord activityToLog = null;
+        int stateToLog = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        for (int i = 0; i < visibleActivities.size(); i++) {
+            ActivityRecord activity = visibleActivities.get(i);
+            int state = activity.getAppCompatState();
+            if (state == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
+                // This shouldn't happen.
+                Slog.w(TAG, "Visible activity with NOT_VISIBLE App Compat state for package UID: "
+                        + packageUid);
+                continue;
+            }
+            if (stateToLog == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE || (
+                    stateToLog == APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED
+                            && state != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED)) {
+                activityToLog = activity;
+                stateToLog = state;
+            }
+        }
+        if (activityToLog != null && stateToLog != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
+            logAppCompatStateInternal(activityToLog, stateToLog, packageUid, compatStateInfo);
+        }
+    }
+
+    private void logAppCompatStateInternal(@NonNull ActivityRecord activity, int state,
+            int packageUid, PackageCompatStateInfo compatStateInfo) {
+        compatStateInfo.mLastLoggedState = state;
+        compatStateInfo.mLastLoggedActivity = activity;
+        FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPAT_STATE_CHANGED, packageUid, state);
+
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("APP_COMPAT_STATE_CHANGED(%s, %s)", packageUid, state));
+        }
+    }
+
     private ArtManagerInternal getArtManagerInternal() {
         if (mArtManagerInternal == null) {
             // Note that this may be null.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3625c7a..cf6a38f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -127,6 +127,11 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityRecord.State.DESTROYED;
@@ -3935,7 +3940,9 @@
         if (tStartingWindow != null && fromActivity.mStartingSurface != null) {
             // In this case, the starting icon has already been displayed, so start
             // letting windows get shown immediately without any more transitions.
-            getDisplayContent().mSkipAppTransitionAnimation = true;
+            if (fromActivity.mVisible) {
+                mDisplayContent.mSkipAppTransitionAnimation = true;
+            }
 
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Moving existing starting %s"
                     + " from %s to %s", tStartingWindow, fromActivity, this);
@@ -4690,6 +4697,7 @@
                 ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
         mTaskSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
         mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
+        logAppCompatState();
     }
 
     @VisibleForTesting
@@ -7438,6 +7446,8 @@
             }
             resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
         }
+
+        logAppCompatState();
     }
 
     /**
@@ -7447,18 +7457,55 @@
      * LetterboxUiController#shouldShowLetterboxUi} for more context.
      */
     boolean areBoundsLetterboxed() {
+        return getAppCompatState(/* ignoreVisibility= */ true)
+                != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+    }
+
+    /**
+     * Logs the current App Compat state via {@link ActivityMetricsLogger#logAppCompatState}.
+     */
+    private void logAppCompatState() {
+        mTaskSupervisor.getActivityMetricsLogger().logAppCompatState(this);
+    }
+
+    /**
+     * Returns the current App Compat state of this activity.
+     *
+     * <p>The App Compat state indicates whether the activity is visible and letterboxed, and if so
+     * what is the reason for letterboxing. The state is used for logging the time spent in
+     * letterbox (sliced by the reason) vs non-letterbox per app.
+     */
+    int getAppCompatState() {
+        return getAppCompatState(/* ignoreVisibility= */ false);
+    }
+
+    /**
+     * Same as {@link #getAppCompatState()} except when {@code ignoreVisibility} the visibility
+     * of the activity is ignored.
+     *
+     * @param ignoreVisibility whether to ignore the visibility of the activity and not return
+     *                         NOT_VISIBLE if {@code mVisibleRequested} is false.
+     */
+    private int getAppCompatState(boolean ignoreVisibility) {
+        if (!ignoreVisibility && !mVisibleRequested) {
+            return APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        }
         if (mInSizeCompatModeForBounds) {
-            return true;
+            return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
         }
         // Letterbox for fixed orientation. This check returns true only when an activity is
         // letterboxed for fixed orientation. Aspect ratio restrictions are also applied if
         // present. But this doesn't return true when the activity is letterboxed only because
         // of aspect ratio restrictions.
         if (isLetterboxedForFixedOrientationAndAspectRatio()) {
-            return true;
+            return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
         }
         // Letterbox for limited aspect ratio.
-        return mIsAspectRatioApplied;
+        if (mIsAspectRatioApplied) {
+            return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
+        }
+
+        return APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
     }
 
     /**
@@ -9220,6 +9267,11 @@
         super.finishSync(outMergedTransaction, cancel);
     }
 
+    @Override
+    boolean canBeAnimationTarget() {
+        return true;
+    }
+
     static class Builder {
         private final ActivityTaskManagerService mAtmService;
         private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f6757f5..af538272 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1798,7 +1798,7 @@
         mRootWindowContainer.startPowerModeLaunchIfNeeded(
                 false /* forceSend */, mStartActivity);
 
-        final boolean isTaskSwitch = startedTask != prevTopTask;
+        final boolean isTaskSwitch = startedTask != prevTopTask && !startedTask.isEmbedded();
         mTargetRootTask.startActivityLocked(mStartActivity,
                 topRootTask != null ? topRootTask.getTopNonFinishingActivity() : null, newTask,
                 isTaskSwitch, mOptions, sourceRecord);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 174b396..6aea848 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -40,6 +40,8 @@
 import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
@@ -1005,6 +1007,21 @@
                     animAttr = enter
                             ? WindowAnimation_launchTaskBehindSourceAnimation
                             : WindowAnimation_launchTaskBehindTargetAnimation;
+                    break;
+                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+                //  need new TaskFragment transition.
+                case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+                    animAttr = enter
+                            ? WindowAnimation_activityOpenEnterAnimation
+                            : WindowAnimation_activityOpenExitAnimation;
+                    break;
+                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+                //  need new TaskFragment transition.
+                case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+                    animAttr = enter
+                            ? WindowAnimation_activityCloseEnterAnimation
+                            : WindowAnimation_activityCloseExitAnimation;
+                    break;
             }
             a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1315,6 +1332,12 @@
             case TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE: {
                 return "TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE";
             }
+            case TRANSIT_OLD_TASK_FRAGMENT_OPEN: {
+                return "TRANSIT_OLD_TASK_FRAGMENT_OPEN";
+            }
+            case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: {
+                return "TRANSIT_OLD_TASK_FRAGMENT_CLOSE";
+            }
             default: {
                 return "<UNKNOWN: " + transition + ">";
             }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d6b0086..6745c69 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -39,6 +39,8 @@
 import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
@@ -68,6 +70,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.os.Trace;
 import android.util.ArrayMap;
@@ -86,6 +89,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.function.Predicate;
@@ -102,6 +107,20 @@
     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
     private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
 
+    private static final int TYPE_NONE = 0;
+    private static final int TYPE_ACTIVITY = 1;
+    private static final int TYPE_TASK_FRAGMENT = 2;
+    private static final int TYPE_TASK = 3;
+
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_NONE,
+            TYPE_ACTIVITY,
+            TYPE_TASK_FRAGMENT,
+            TYPE_TASK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface TransitContainerType {}
+
     private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
 
     AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
@@ -387,33 +406,38 @@
                 openingApps, closingApps, true /* visible */);
         final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
                 openingApps, closingApps, false /* visible */);
-        final boolean isActivityOpening = !openingWcs.isEmpty()
-                && openingWcs.valueAt(0).asActivityRecord() != null;
-        final boolean isActivityClosing = !closingWcs.isEmpty()
-                && closingWcs.valueAt(0).asActivityRecord() != null;
-        final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening;
-        final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing;
-
-        if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) {
+        final WindowContainer<?> openingContainer = !openingWcs.isEmpty()
+                ? openingWcs.valueAt(0) : null;
+        final WindowContainer<?> closingContainer = !closingWcs.isEmpty()
+                ? closingWcs.valueAt(0) : null;
+        @TransitContainerType int openingType = getTransitContainerType(openingContainer);
+        @TransitContainerType int closingType = getTransitContainerType(closingContainer);
+        if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && openingType == TYPE_TASK) {
             return TRANSIT_OLD_TASK_TO_FRONT;
         }
-        if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) {
+        if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && closingType == TYPE_TASK) {
             return TRANSIT_OLD_TASK_TO_BACK;
         }
         if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
-            if (isTaskOpening) {
+            if (openingType == TYPE_TASK) {
                 return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0
                         ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN;
             }
-            if (isActivityOpening) {
+            if (openingType == TYPE_ACTIVITY) {
                 return TRANSIT_OLD_ACTIVITY_OPEN;
             }
+            if (openingType == TYPE_TASK_FRAGMENT) {
+                return TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+            }
         }
         if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
-            if (isTaskClosing) {
+            if (closingType == TYPE_TASK) {
                 return TRANSIT_OLD_TASK_CLOSE;
             }
-            if (isActivityClosing) {
+            if (closingType == TYPE_TASK_FRAGMENT) {
+                return TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+            }
+            if (closingType == TYPE_ACTIVITY) {
                 for (int i = closingApps.size() - 1; i >= 0; i--) {
                     if (closingApps.valueAt(i).visibleIgnoringKeyguard) {
                         return TRANSIT_OLD_ACTIVITY_CLOSE;
@@ -430,6 +454,23 @@
         return TRANSIT_OLD_NONE;
     }
 
+    @TransitContainerType
+    private static int getTransitContainerType(@Nullable WindowContainer<?> container) {
+        if (container == null) {
+            return TYPE_NONE;
+        }
+        if (container.asTask() != null) {
+            return TYPE_TASK;
+        }
+        if (container.asTaskFragment() != null) {
+            return TYPE_TASK_FRAGMENT;
+        }
+        if (container.asActivityRecord() != null) {
+            return TYPE_ACTIVITY;
+        }
+        return TYPE_NONE;
+    }
+
     private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
         final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
         return mainWindow != null ? mainWindow.mAttrs : null;
@@ -633,6 +674,7 @@
             boolean canPromote = true;
 
             if (parent == null || !parent.canCreateRemoteAnimationTarget()
+                    || !parent.canBeAnimationTarget()
                     // We cannot promote the animation on Task's parent when the task is in
                     // clearing task in case the animating get stuck when performing the opening
                     // task that behind it.
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 1a429f8..6b57ede 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -396,6 +396,7 @@
         RemoteAnimationTarget mTarget;
         final WindowContainer mWindowContainer;
         final Rect mStartBounds;
+        private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
 
         RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
                 Rect endBounds, Rect startBounds) {
@@ -428,18 +429,12 @@
             return mTarget;
         }
 
+        void setMode(@RemoteAnimationTarget.Mode int mode) {
+            mMode = mode;
+        }
+
         int getMode() {
-            final DisplayContent dc = mWindowContainer.getDisplayContent();
-            final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
-            // Note that opening/closing transitions are per-activity while changing transitions
-            // are per-task.
-            if (dc.mOpeningApps.contains(topActivity)) {
-                return RemoteAnimationTarget.MODE_OPENING;
-            } else if (dc.mChangingContainers.contains(mWindowContainer)) {
-                return RemoteAnimationTarget.MODE_CHANGING;
-            } else {
-                return RemoteAnimationTarget.MODE_CLOSING;
-            }
+            return mMode;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 6e4792bd..5363c9b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -324,6 +324,7 @@
         // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
         if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
             mAdjacentTaskFragment.mAdjacentTaskFragment = null;
+            mAdjacentTaskFragment.mDelayLastActivityRemoval = false;
         }
         mAdjacentTaskFragment = null;
         mDelayLastActivityRemoval = false;
@@ -1616,7 +1617,12 @@
     @Override
     RemoteAnimationTarget createRemoteAnimationTarget(
             RemoteAnimationController.RemoteAnimationRecord record) {
-        final ActivityRecord activity = getTopMostActivity();
+        final ActivityRecord activity = record.getMode() == RemoteAnimationTarget.MODE_OPENING
+                // There may be a trampoline activity without window on top of the existing task
+                // which is moving to front. Exclude the finishing activity so the window of next
+                // activity can be chosen to create the animation target.
+                ? getTopNonFinishingActivity()
+                : getTopMostActivity();
         return activity != null ? activity.createRemoteAnimationTarget(record) : null;
     }
 
@@ -2149,6 +2155,11 @@
         sendTaskFragmentVanished();
     }
 
+    @Override
+    boolean canBeAnimationTarget() {
+        return true;
+    }
+
     boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
             boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
         boolean printed = false;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index c48e9d1..b6c8e13 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2689,6 +2689,11 @@
             final RemoteAnimationController.RemoteAnimationRecord adapters =
                     controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
                             screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
+            if (!isChanging) {
+                adapters.setMode(enter
+                        ? RemoteAnimationTarget.MODE_OPENING
+                        : RemoteAnimationTarget.MODE_CLOSING);
+            }
             resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
         } else if (isChanging) {
             final float durationScale = mWmService.getTransitionAnimationScaleLocked();
@@ -2842,6 +2847,14 @@
         return false;
     }
 
+    /**
+     * {@code true} to indicate that this container can be a candidate of
+     * {@link AppTransitionController#getAnimationTargets(ArraySet, ArraySet, boolean) animation
+     * target}. */
+    boolean canBeAnimationTarget() {
+        return false;
+    }
+
     boolean okToDisplay() {
         final DisplayContent dc = getDisplayContent();
         return dc != null && dc.okToDisplay();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b98debc..a9ddd35 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10110,7 +10110,7 @@
     }
 
     @Override
-    public boolean setPermittedAccessibilityServices(ComponentName who, List packageList) {
+    public boolean setPermittedAccessibilityServices(ComponentName who, List<String> packageList) {
         if (!mHasFeature) {
             return false;
         }
@@ -10162,7 +10162,7 @@
     }
 
     @Override
-    public List getPermittedAccessibilityServices(ComponentName who) {
+    public List<String> getPermittedAccessibilityServices(ComponentName who) {
         if (!mHasFeature) {
             return null;
         }
@@ -10177,7 +10177,7 @@
     }
 
     @Override
-    public List getPermittedAccessibilityServicesForUser(int userId) {
+    public List<String> getPermittedAccessibilityServicesForUser(int userId) {
         if (!mHasFeature) {
             return null;
         }
@@ -10263,7 +10263,7 @@
     }
 
     @Override
-    public boolean setPermittedInputMethods(ComponentName who, List packageList,
+    public boolean setPermittedInputMethods(ComponentName who, List<String> packageList,
             boolean calledOnParentInstance) {
         if (!mHasFeature) {
             return false;
@@ -10327,7 +10327,8 @@
     }
 
     @Override
-    public List getPermittedInputMethods(ComponentName who, boolean calledOnParentInstance) {
+    public List<String> getPermittedInputMethods(ComponentName who,
+            boolean calledOnParentInstance) {
         if (!mHasFeature) {
             return null;
         }
@@ -10348,7 +10349,7 @@
     }
 
     @Override
-    public List getPermittedInputMethodsForCurrentUser() {
+    public List<String> getPermittedInputMethodsForCurrentUser() {
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(canManageUsers(caller));
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index be942d6..9bc20a7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -829,7 +829,7 @@
         assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting,
                 actorSetting, SYSTEM_USER));
 
-        appsFilter.removePackage(targetSetting);
+        appsFilter.removePackage(targetSetting, false /* isReplace */);
 
         // Actor loses visibility to the overlay via removal of the target
         assertTrue(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
@@ -1074,7 +1074,8 @@
         watcher.verifyNoChangeReported("getVisibility");
 
         // provider read
-        appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId);
+        appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId,
+                false /* retainOnUpdate */);
         watcher.verifyChangeReported("grantImplicitAccess");
 
         // ensure implicit access is included in the filter
@@ -1143,7 +1144,8 @@
         watcher.verifyNoChangeReported("get");
 
         // provider read
-        appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId);
+        appsFilter.grantImplicitAccess(
+                hasProviderAppId, queriesProviderAppId, false /* retainOnUpdate */);
         watcher.verifyChangeReported("grantImplicitAccess");
 
         // ensure implicit access is included in the filter
@@ -1154,7 +1156,7 @@
         watcher.verifyNoChangeReported("get");
 
         // remove a package
-        appsFilter.removePackage(seesNothing);
+        appsFilter.removePackage(seesNothing, false /* isReplace */);
         watcher.verifyChangeReported("removePackage");
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 25b51da..e418d2f 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -73,8 +73,8 @@
 
     // we expect the following only during grant if a grant is expected
     private void verifyNoVisibilityGrant() {
-        verify(mContext.mPmInternal, never())
-                .grantImplicitAccess(anyInt(), any(), anyInt(), anyInt(), anyBoolean());
+        verify(mContext.mPmInternal, never()).grantImplicitAccess(
+                anyInt(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     @Before
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 733d3f0..9dca235 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertNull;
@@ -36,13 +38,10 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -81,10 +80,12 @@
         ZenModeConfig config = getMutedAllConfig();
         config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
 
+        // Explicitly allow conversations from priority senders to make sure that goes through
         ZenPolicy zenPolicy = new ZenPolicy.Builder()
                 .allowAlarms(true)
                 .allowReminders(true)
                 .allowEvents(true)
+                .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
                 .showLights(false)
                 .showInAmbientDisplay(false)
                 .build();
@@ -93,11 +94,12 @@
         int priorityCategories = originalPolicy.priorityCategories;
         int priorityCallSenders = originalPolicy.priorityCallSenders;
         int priorityMessageSenders = originalPolicy.priorityMessageSenders;
-        int priorityConversationsSenders = originalPolicy.priorityConversationSenders;
+        int priorityConversationsSenders = CONVERSATION_SENDERS_IMPORTANT;
         int suppressedVisualEffects = originalPolicy.suppressedVisualEffects;
         priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+        priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
         suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
         suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
 
@@ -344,7 +346,7 @@
         config.allowCallsFrom = ZenModeConfig.SOURCE_ANYONE;
         config.allowMessagesFrom = ZenModeConfig.SOURCE_ANYONE;
         config.allowConversations = true;
-        config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+        config.allowConversationsFrom = CONVERSATION_SENDERS_IMPORTANT;
 
         config.suppressedVisualEffects = 0;
         return config;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 89a126b..0b91802 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -476,6 +476,16 @@
     }
 
     @Test
+    public void testConsecutiveLaunch() {
+        mTrampolineActivity.setState(ActivityRecord.State.INITIALIZING, "test");
+        onActivityLaunched(mTrampolineActivity);
+        mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
+                mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
+        notifyActivityLaunched(START_SUCCESS, mTopActivity);
+        transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
+    }
+
+    @Test
     public void testConsecutiveLaunchNewTask() {
         final IBinder launchCookie = mock(IBinder.class);
         final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e717c34..9ad479a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -219,7 +219,7 @@
     public void testNoCleanupMovingActivityInSameStack() {
         final ActivityRecord activity = createActivityWith2LevelTask();
         final Task rootTask = activity.getRootTask();
-        final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask).build();
+        final Task newTask = createTaskInRootTask(rootTask, 0 /* userId */);
         activity.reparent(newTask, 0, null /*reason*/);
         verify(rootTask, times(0)).cleanUpActivityReferences(any());
     }
@@ -2510,8 +2510,10 @@
     @Test
     public void testTransferStartingWindow() {
         registerTestStartingWindowOrganizer();
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true)
+                .setVisible(false).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true)
+                .setVisible(false).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
@@ -2520,6 +2522,7 @@
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
                 true, true, false, true, false, false);
         waitUntilHandlersIdle();
+        assertFalse(mDisplayContent.mSkipAppTransitionAnimation);
         assertNoStartingWindow(activity1);
         assertHasStartingWindow(activity2);
     }
@@ -2627,6 +2630,7 @@
                 false /* newTask */, false /* isTaskSwitch */, null /* options */,
                 null /* sourceRecord */);
 
+        assertTrue(mDisplayContent.mSkipAppTransitionAnimation);
         assertNull(middle.mStartingWindow);
         assertHasStartingWindow(top);
         assertTrue(top.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 1b4d0a4..3e8a2e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -478,7 +478,7 @@
         final ActivityRecord splitSecondActivity =
                 new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor)
-                .setParentTask(splitOrg.mPrimary)
+                .setParentTaskFragment(splitOrg.mPrimary)
                 .setCreateActivity(true)
                 .build()
                 .getTopMostActivity();
@@ -856,7 +856,7 @@
         // Create another activity on top of the secondary display.
         final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build();
+        final Task topTask = new TaskBuilder(mSupervisor).setParentTaskFragment(topStack).build();
         new ActivityBuilder(mAtm).setTask(topTask).build();
 
         doReturn(mActivityMetricsLogger).when(mSupervisor).getActivityMetricsLogger();
@@ -920,7 +920,7 @@
                 DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
         final Task task = new TaskBuilder(mSupervisor)
                 .setComponent(componentName)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         return new ActivityBuilder(mAtm)
                 .setComponent(componentName)
@@ -1056,8 +1056,8 @@
         final ActivityStarter starter = prepareStarter(0 /* flags */);
         starter.mStartActivity = new ActivityBuilder(mAtm).build();
         final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+                .setParentTaskFragment(createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
+                        ACTIVITY_TYPE_STANDARD))
                 .setUserId(10)
                 .build();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 3a6aac9..11e3fd4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -375,7 +375,7 @@
         final ArraySet<ActivityRecord> closing = new ArraySet<>();
         closing.add(activity3);
 
-        // Promote animation targets to TaskStack level. Invisible ActivityRecords don't affect
+        // Promote animation targets to root Task level. Invisible ActivityRecords don't affect
         // promotion decision.
         assertEquals(
                 new ArraySet<>(new WindowContainer[]{activity1.getRootTask()}),
@@ -520,6 +520,128 @@
                         opening, closing, false /* visible */));
     }
 
+    @Test
+    public void testGetAnimationTargets_openingClosingTaskFragment() {
+        // [DefaultTDA] - [Task] -+- [TaskFragment1] - [ActivityRecord1] (opening, invisible)
+        //                        +- [TaskFragment2] - [ActivityRecord2] (closing, visible)
+        final Task parentTask = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(parentTask,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+
+        final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity2 = taskFragment2.getTopMostActivity();
+        activity2.setVisible(true);
+        activity2.mVisibleRequested = false;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity2);
+
+        // Promote animation targets up to TaskFragment level, not beyond.
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment2}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
+    @Test
+    public void testGetAnimationTargets_openingClosingTaskFragmentWithEmbeddedTask() {
+        // [DefaultTDA] - [Task] -+- [TaskFragment1] - [ActivityRecord1] (opening, invisible)
+        //                        +- [TaskFragment2] - [ActivityRecord2] (closing, visible)
+        final Task parentTask = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(parentTask,
+                true /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+
+        final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask,
+                true /* createEmbeddedTask */);
+        final ActivityRecord activity2 = taskFragment2.getTopMostActivity();
+        activity2.setVisible(true);
+        activity2.mVisibleRequested = false;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity2);
+
+        // Promote animation targets up to TaskFragment level, not beyond.
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment2}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
+    @Test
+    public void testGetAnimationTargets_openingTheOnlyTaskFragmentInTask() {
+        // [DefaultTDA] -+- [Task1] - [TaskFragment1] - [ActivityRecord1] (opening, invisible)
+        //               +- [Task2] - [ActivityRecord2] (closing, visible)
+        final Task task1 = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(task1,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+        activity2.setVisible(true);
+        activity2.mVisibleRequested = false;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity2);
+
+        // Promote animation targets up to leaf Task level because there's only one TaskFragment in
+        // the Task.
+        assertEquals(new ArraySet<>(new WindowContainer[]{task1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{activity2.getTask()}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
+    @Test
+    public void testGetAnimationTargets_closingTheOnlyTaskFragmentInTask() {
+        // [DefaultTDA] -+- [Task1] - [TaskFragment1] - [ActivityRecord1] (closing, visible)
+        //               +- [Task2] - [ActivityRecord2] (opening, invisible)
+        final Task task1 = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(task1,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(true);
+        activity1.mVisibleRequested = false;
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+        activity2.setVisible(false);
+        activity2.mVisibleRequested = true;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity2);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity1);
+
+        // Promote animation targets up to leaf Task level because there's only one TaskFragment in
+        // the Task.
+        assertEquals(new ArraySet<>(new WindowContainer[]{activity2.getTask()}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{task1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
     static class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         @Override
         public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index c3279bf..67aac13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -23,6 +23,8 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_UNSET;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
@@ -40,6 +42,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
 import android.view.Display;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
@@ -147,6 +150,91 @@
     }
 
     @Test
+    public void testTaskFragmentOpeningTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                false /* createEmbeddedTask */);
+        activity.setVisible(false);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
+        mDisplayContent.mOpeningApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /* skipAppTransitionAnimation */));
+    }
+
+    @Test
+    public void testEmbeddedTaskOpeningTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                true /* createEmbeddedTask */);
+        activity.setVisible(false);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
+        mDisplayContent.mOpeningApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /* skipAppTransitionAnimation */));
+    }
+
+    @Test
+    public void testTaskFragmentClosingTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                false /* createEmbeddedTask */);
+        activity.setVisible(true);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+        mDisplayContent.mClosingApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /* skipAppTransitionAnimation */));
+    }
+
+    @Test
+    public void testEmbeddedTaskClosingTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                true /* createEmbeddedTask */);
+        activity.setVisible(true);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+        mDisplayContent.mClosingApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /* skipAppTransitionAnimation */));
+    }
+
+    /**
+     * Creates a {@link Task} with two {@link TaskFragment TaskFragments}.
+     * The bottom TaskFragment is to prevent
+     * {@link AppTransitionController#getAnimationTargets(ArraySet, ArraySet, boolean) the animation
+     * target} to promote to Task or above.
+     *
+     * @param createEmbeddedTask {@code true} to create embedded Task for verified TaskFragment
+     * @return The Activity to be put in either opening or closing Activity
+     */
+    private ActivityRecord createHierarchyForTaskFragmentTest(boolean createEmbeddedTask) {
+        final Task parentTask = createTask(mDisplayContent);
+        final TaskFragment bottomTaskFragment = createTaskFragmentWithParentTask(parentTask,
+                false /* createEmbeddedTask */);
+        final ActivityRecord bottomActivity = bottomTaskFragment.getTopMostActivity();
+        bottomActivity.setOccludesParent(true);
+        bottomActivity.setVisible(true);
+
+        final TaskFragment verifiedTaskFragment = createTaskFragmentWithParentTask(parentTask,
+                createEmbeddedTask);
+        final ActivityRecord activity = verifiedTaskFragment.getTopMostActivity();
+        activity.setOccludesParent(true);
+
+        return activity;
+    }
+
+    @Test
     public void testAppTransitionStateForMultiDisplay() {
         // Create 2 displays & presume both display the state is ON for ready to display & animate.
         final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 473d303..14dc33e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2352,10 +2352,10 @@
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask1).build();
-        final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask2).build();
-        final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask3).build();
-        final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask4).build();
+        final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask1).build();
+        final Task task2 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask2).build();
+        final Task task3 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask3).build();
+        final Task task4 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask4).build();
 
         // Reordering root tasks while removing root tasks.
         doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 7cb7c79d..8a6db2c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -117,7 +117,7 @@
         Task rootTask = mTestDisplay.getDefaultTaskDisplayArea()
                 .createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
         mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT)
-                .setParentTask(rootTask).build();
+                .setParentTaskFragment(rootTask).build();
         mTestTask.mUserId = TEST_USER_ID;
         mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS;
         mTestTask.setHasBeenVisible(true);
@@ -353,7 +353,7 @@
         final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor)
                 .setComponent(ALTERNATIVE_COMPONENT)
                 .setUserId(TEST_USER_ID)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500);
@@ -365,7 +365,7 @@
         final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor)
                 .setComponent(TEST_COMPONENT)
                 .setUserId(ALTERNATIVE_USER_ID)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600);
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 1b078b7..22e687a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -331,7 +331,7 @@
         // other task
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .setParentTask(mTaskContainer.getRootHomeTask()).build();
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask()).build();
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
                 .build();
@@ -471,8 +471,8 @@
         final Task root = createTaskBuilder(".CreatedByOrganizerRoot").build();
         root.mCreatedByOrganizer = true;
         // Add organized and non-organized child.
-        final Task child1 = createTaskBuilder(".Task1").setParentTask(root).build();
-        final Task child2 = createTaskBuilder(".Task2").setParentTask(root).build();
+        final Task child1 = createTaskBuilder(".Task1").setParentTaskFragment(root).build();
+        final Task child2 = createTaskBuilder(".Task2").setParentTaskFragment(root).build();
         doReturn(true).when(child1).isOrganized();
         doReturn(false).when(child2).isOrganized();
         mRecentTasks.add(root);
@@ -508,7 +508,8 @@
         // tasks because their intents are identical.
         mRecentTasks.add(task1);
         // Go home to trigger the removal of untracked tasks.
-        mRecentTasks.add(createTaskBuilder(".Home").setParentTask(mTaskContainer.getRootHomeTask())
+        mRecentTasks.add(createTaskBuilder(".Home")
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask())
                 .build());
         triggerIdleToTrim();
 
@@ -675,7 +676,7 @@
     public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() {
         // Create some set of tasks, some of which are visible and some are not
         Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
-                .setParentTask(mTaskContainer.getRootHomeTask())
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask())
                 .build();
         homeTask.mUserSetupComplete = true;
         mRecentTasks.add(homeTask);
@@ -696,7 +697,7 @@
         t1.mUserSetupComplete = true;
         mRecentTasks.add(t1);
         Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
-                .setParentTask(mTaskContainer.getRootHomeTask())
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask())
                 .build();
         homeTask.mUserSetupComplete = true;
         mRecentTasks.add(homeTask);
@@ -949,10 +950,10 @@
 
         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
         // the tasks belong in stacks above the home stack
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task3").setParentTask(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setParentTaskFragment(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task3").setParentTaskFragment(aboveHomeStack).build());
 
         triggerTrimAndAssertNoTasksTrimmed();
     }
@@ -970,11 +971,11 @@
         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
         // the home stack is trimmed once a new task is added
         final Task behindHomeTask = createTaskBuilder(".Task1")
-                .setParentTask(behindHomeStack)
+                .setParentTaskFragment(behindHomeStack)
                 .build();
         mRecentTasks.add(behindHomeTask);
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(aboveHomeStack).build());
 
         triggerTrimAndAssertTrimmed(behindHomeTask);
     }
@@ -990,10 +991,12 @@
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeTask).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayRootTask).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayRootTask).build());
-        mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeTask).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeTask).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setParentTaskFragment(otherDisplayRootTask)
+                .build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(otherDisplayRootTask)
+                .build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTaskFragment(homeTask).build());
 
         triggerTrimAndAssertNoTasksTrimmed();
     }
@@ -1023,7 +1026,7 @@
         Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
         mRecentTasks.add(t1);
         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".HomeTask")
-                .setParentTask(mTaskContainer.getRootHomeTask()).build());
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask()).build());
         Task t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
         mRecentTasks.add(t2);
         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".PipTask")
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index b6cfa8e..89ae5ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -51,6 +51,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
@@ -274,6 +275,32 @@
     }
 
     @Test
+    public void testOpeningTaskWithTopFinishingActivity() {
+        final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "win");
+        final Task task = win.getTask();
+        final ActivityRecord topFinishing = new ActivityBuilder(mAtm).setTask(task).build();
+        // Now the task contains:
+        //     - Activity[1] (top, finishing, no window)
+        //     - Activity[0] (has window)
+        topFinishing.finishing = true;
+        spyOn(mDisplayContent.mAppTransition);
+        doReturn(mController).when(mDisplayContent.mAppTransition).getRemoteAnimationController();
+        task.applyAnimationUnchecked(null /* lp */, true /* enter */, TRANSIT_OLD_TASK_OPEN,
+                false /* isVoiceInteraction */, null /* sources */);
+        mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
+        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+        try {
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
+                    appsCaptor.capture(), any(), any(), any());
+        } catch (RemoteException ignored) {
+        }
+        assertEquals(1, appsCaptor.getValue().length);
+        assertEquals(RemoteAnimationTarget.MODE_OPENING, appsCaptor.getValue()[0].mode);
+    }
+
+    @Test
     public void testChangeToSmallerSize() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mDisplayContent.mChangingContainers.add(win.mActivityRecord);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 56d01cd..030733b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -329,7 +329,7 @@
 
         // Create primary splitscreen root task.
         final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(organizer.mPrimary)
+                .setParentTaskFragment(organizer.mPrimary)
                 .setOnTop(true)
                 .build();
 
@@ -505,8 +505,8 @@
                 targetActivity);
         final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME,
                 aliasActivity);
-        final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
-        final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build();
+        final Task parentTask = new TaskBuilder(mSupervisor).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(parentTask).build();
         task.origActivity = alias;
         task.realActivity = target;
         new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity(
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 8d4acbb..f2eb709 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -168,7 +168,7 @@
     @Test
     public void testTaskLayerRank() {
         final Task rootTask = new TaskBuilder(mSupervisor).build();
-        final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true;
         mWm.mRoot.rankTaskLayers();
 
@@ -523,7 +523,8 @@
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build();
+        final Task targetTask = new TaskBuilder(mSupervisor).setParentTaskFragment(targetRootTask)
+                .build();
 
         // Create Recents on secondary display.
         final TestDisplayContent secondDisplay = addNewDisplayContentAt(
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 c44c22f..cb85884 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -166,7 +166,7 @@
         final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
                 .setComponent(new ComponentName(mContext.getPackageName(), className))
                 .setTaskId(taskId)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         task.lastActiveTime = lastActiveTime;
         final ActivityRecord activity = new ActivityBuilder(mAtm)
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 3bebf6b..6407c92 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,6 +40,11 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
 import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
@@ -55,7 +60,9 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doCallRealMethod;
 
@@ -101,10 +108,14 @@
 
     private Task mTask;
     private ActivityRecord mActivity;
+    private ActivityMetricsLogger mActivityMetricsLogger;
     private Properties mInitialConstrainDisplayApisFlags;
 
     @Before
     public void setUp() throws Exception {
+        mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
+        clearInvocations(mActivityMetricsLogger);
+        doReturn(mActivityMetricsLogger).when(mAtm.mTaskSupervisor).getActivityMetricsLogger();
         mInitialConstrainDisplayApisFlags = DeviceConfig.getProperties(
                 NAMESPACE_CONSTRAIN_DISPLAY_APIS);
         DeviceConfig.setProperties(
@@ -1939,13 +1950,19 @@
         setUpDisplaySizeWithApp(1000, 2500);
 
         assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
 
         prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
 
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
+
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
         rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
 
         // After returning to the original rotation, bounds are computed in
@@ -1955,6 +1972,18 @@
         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
+
+        // After setting the visibility of the activity to false, areBoundsLetterboxed() still
+        // returns true but the NOT_VISIBLE App Compat state is logged.
+        mActivity.setVisibility(false);
+        assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE);
+        mActivity.setVisibility(true);
+        assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
     }
 
     @Test
@@ -1963,12 +1992,15 @@
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
 
         assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
 
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION);
     }
 
     @Test
@@ -1978,12 +2010,15 @@
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
         assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
 
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertTrue(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
     }
 
     /**
@@ -2197,6 +2232,11 @@
                 .isEqualTo(activity.getWindowConfiguration().getBounds());
     }
 
+    private void verifyLogAppCompatState(ActivityRecord activity, int state) {
+        verify(mActivityMetricsLogger, atLeastOnce()).logAppCompatState(
+                argThat(r -> activity == r && r.getAppCompatState() == state));
+    }
+
     static Configuration rotateDisplay(DisplayContent display, int rotation) {
         final Configuration c = new Configuration();
         display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index c45c18d..d68edba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -620,7 +620,7 @@
         final Task pinnedRootTask = mRootWindowContainer.getDefaultTaskDisplayArea()
                 .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(pinnedRootTask).build();
+                .setParentTaskFragment(pinnedRootTask).build();
         new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
                 .setTask(pinnedTask).build();
         pinnedRootTask.moveToFront("movePinnedRootTaskToFront");
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 5e4c67c..e528a4a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1716,7 +1716,7 @@
         final Task rootTask = display.getDefaultTaskDisplayArea()
                 .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
         rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         // Just work around the unnecessary adjustments for bounds.
         task.getWindowConfiguration().setBounds(bounds);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 8074944..67e8c87 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -427,7 +427,7 @@
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
         Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         final Configuration parentConfig = rootTask.getConfiguration();
         parentConfig.windowConfiguration.setBounds(parentBounds);
         parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
@@ -757,7 +757,7 @@
         DisplayInfo displayInfo = new DisplayInfo();
         mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
         final int displayHeight = displayInfo.logicalHeight;
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         final Configuration inOutConfig = new Configuration();
         final Configuration parentConfig = new Configuration();
         final int longSide = 1200;
@@ -1375,7 +1375,7 @@
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
         Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
 
         final Configuration parentConfig = rootTask.getConfiguration();
         parentConfig.windowConfiguration.setAppBounds(parentBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 050fd80..e7ef6ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -56,6 +56,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
 import static org.junit.Assert.assertEquals;
@@ -589,7 +590,7 @@
     Task createTaskInRootTask(Task rootTask, int userId) {
         final Task task = new TaskBuilder(rootTask.mTaskSupervisor)
                 .setUserId(userId)
-                .setParentTask(rootTask)
+                .setParentTaskFragment(rootTask)
                 .build();
         return task;
     }
@@ -675,6 +676,26 @@
         activity.mVisibleRequested = true;
     }
 
+    /**
+     * Creates a {@link TaskFragment} and attach it to the {@code parentTask}.
+     *
+     * @param parentTask the {@link Task} this TaskFragment is going to be attached
+     * @param createEmbeddedTask Sets to {@code true} to create an embedded Task for this
+     *                           TaskFragment. Otherwise, create a {@link ActivityRecord}.
+     * @return the created TaskFragment
+     */
+    static TaskFragment createTaskFragmentWithParentTask(@NonNull Task parentTask,
+            boolean createEmbeddedTask) {
+        final TaskFragmentBuilder builder = new TaskFragmentBuilder(parentTask.mAtmService)
+                .setParentTask(parentTask);
+        if (createEmbeddedTask) {
+            builder.createEmbeddedTask();
+        } else {
+            builder.createActivityCount(1);
+        }
+        return builder.build();
+    }
+
     /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
     DisplayContent createNewDisplay() {
         return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL);
@@ -1042,7 +1063,7 @@
                         // Apply the root activity info and intent
                         .setActivityInfo(aInfo)
                         .setIntent(intent)
-                        .setParentTask(mParentTask).build();
+                        .setParentTaskFragment(mParentTask).build();
             } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask(
                     mParentTask.getWindowingMode(), mParentTask.getActivityType())) {
                 // The parent task can be the task root.
@@ -1101,6 +1122,64 @@
         }
     }
 
+    static class TaskFragmentBuilder {
+        private final ActivityTaskManagerService mAtm;
+        private Task mParentTask;
+        private boolean mCreateParentTask;
+        private boolean mCreateEmbeddedTask;
+        private int mCreateActivityCount = 0;
+
+        TaskFragmentBuilder(ActivityTaskManagerService service) {
+            mAtm = service;
+        }
+
+        TaskFragmentBuilder setCreateParentTask() {
+            mCreateParentTask = true;
+            return this;
+        }
+
+        TaskFragmentBuilder setParentTask(Task task) {
+            mParentTask = task;
+            return this;
+        }
+
+        /** Creates a child embedded Task and its Activity */
+        TaskFragmentBuilder createEmbeddedTask() {
+            mCreateEmbeddedTask = true;
+            return this;
+        }
+
+        TaskFragmentBuilder createActivityCount(int count) {
+            mCreateActivityCount = count;
+            return this;
+        }
+
+        TaskFragment build() {
+            SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock);
+
+            final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */,
+                    false /* createdByOrganizer */);
+            if (mParentTask == null && mCreateParentTask) {
+                mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
+            }
+            if (mParentTask != null) {
+                mParentTask.addChild(taskFragment, POSITION_TOP);
+            }
+            if (mCreateEmbeddedTask) {
+                new TaskBuilder(mAtm.mTaskSupervisor)
+                        .setParentTaskFragment(taskFragment)
+                        .setCreateActivity(true)
+                        .build();
+            }
+            while (mCreateActivityCount > 0) {
+                final ActivityRecord activity = new ActivityBuilder(mAtm).build();
+                taskFragment.addChild(activity);
+                mCreateActivityCount--;
+            }
+            return taskFragment;
+        }
+    }
+
     /**
      * Builder for creating new tasks.
      */
@@ -1121,7 +1200,7 @@
         private IVoiceInteractionSession mVoiceSession;
 
         private boolean mCreateParentTask = false;
-        private Task mParentTask;
+        private TaskFragment mParentTaskFragment;
 
         private boolean mCreateActivity = false;
 
@@ -1205,8 +1284,8 @@
             return this;
         }
 
-        TaskBuilder setParentTask(Task parentTask) {
-            mParentTask = parentTask;
+        TaskBuilder setParentTaskFragment(TaskFragment parentTaskFragment) {
+            mParentTaskFragment = parentTaskFragment;
             return this;
         }
 
@@ -1219,12 +1298,13 @@
             SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock);
 
             // Create parent task.
-            if (mParentTask == null && mCreateParentTask) {
-                mParentTask = mTaskDisplayArea.createRootTask(
+            if (mParentTaskFragment == null && mCreateParentTask) {
+                mParentTaskFragment = mTaskDisplayArea.createRootTask(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
             }
-            if (mParentTask != null && !Mockito.mockingDetails(mParentTask).isSpy()) {
-                spyOn(mParentTask);
+            if (mParentTaskFragment != null
+                    && !Mockito.mockingDetails(mParentTaskFragment).isSpy()) {
+                spyOn(mParentTaskFragment);
             }
 
             // Create task.
@@ -1252,13 +1332,15 @@
                     .setOnTop(mOnTop)
                     .setVoiceSession(mVoiceSession);
             final Task task;
-            if (mParentTask == null) {
+            if (mParentTaskFragment == null) {
                 task = builder.setActivityType(mActivityType)
                         .setParent(mTaskDisplayArea)
                         .build();
             } else {
-                task = builder.setParent(mParentTask).build();
-                mParentTask.moveToFront("build-task");
+                task = builder.setParent(mParentTaskFragment).build();
+                if (mParentTaskFragment.asTask() != null) {
+                    mParentTaskFragment.asTask().moveToFront("build-task");
+                }
             }
             spyOn(task);
             task.mUserId = mUserId;