Merge "Make Markup fade in from screenshots" into sc-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5fa75dd..0bb12c8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1113,6 +1113,10 @@
     method @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER) public void onCameraOpened(@NonNull String, @NonNull String);
   }
 
+  public abstract class CameraMetadata<TKey> {
+    field public static final int SENSOR_TEST_PATTERN_MODE_BLACK = 5; // 0x5
+  }
+
 }
 
 package android.hardware.devicestate {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 854c9f2..db5dcc5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3866,9 +3866,26 @@
     }
 
     /**
-     * Called when the activity has detected the user's press of the back
-     * key.  The default implementation simply finishes the current activity,
-     * but you can override this to do whatever you want.
+     * Called when the activity has detected the user's press of the back key. The default
+     * implementation depends on the platform version:
+     *
+     * <ul>
+     *     <li>On platform versions prior to {@link android.os.Build.VERSION_CODES#S}, it
+     *         finishes the current activity, but you can override this to do whatever you want.
+     *
+     *     <li><p>Starting with platform version {@link android.os.Build.VERSION_CODES#S}, for
+     *         activities that are the root activity of the task and also declare an
+     *         {@link android.content.IntentFilter} with {@link Intent#ACTION_MAIN} and
+     *         {@link Intent#CATEGORY_LAUNCHER} in the manifest, the current activity and its
+     *         task will be moved to the back of the activity stack instead of being finished.
+     *         Other activities will simply be finished.
+     *
+     *         <p>If you target version {@link android.os.Build.VERSION_CODES#S} or later and
+     *         override this method, it is strongly recommended to call through to the superclass
+     *         implementation after you finish handling navigation within the app.
+     * </ul>
+     *
+     * @see #moveTaskToBack(boolean)
      */
     public void onBackPressed() {
         if (mActionBar != null && mActionBar.collapseActionView()) {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index d4da3b9..9501994 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.PublicKey;
@@ -2922,10 +2923,10 @@
      * respective color channel provided in
      * {@link CaptureRequest#SENSOR_TEST_PATTERN_DATA android.sensor.testPatternData}.</p>
      * <p>For example:</p>
-     * <pre><code>android.control.testPatternData = [0, 0xFFFFFFFF, 0xFFFFFFFF, 0]
+     * <pre><code>{@link CaptureRequest#SENSOR_TEST_PATTERN_DATA android.sensor.testPatternData} = [0, 0xFFFFFFFF, 0xFFFFFFFF, 0]
      * </code></pre>
      * <p>All green pixels are 100% green. All red/blue pixels are black.</p>
-     * <pre><code>android.control.testPatternData = [0xFFFFFFFF, 0, 0xFFFFFFFF, 0]
+     * <pre><code>{@link CaptureRequest#SENSOR_TEST_PATTERN_DATA android.sensor.testPatternData} = [0xFFFFFFFF, 0, 0xFFFFFFFF, 0]
      * </code></pre>
      * <p>All red pixels are 100% red. Only the odd green pixels
      * are 100% green. All blue pixels are 100% black.</p>
@@ -3002,6 +3003,20 @@
     public static final int SENSOR_TEST_PATTERN_MODE_PN9 = 4;
 
     /**
+     * <p>All pixel data is replaced by 0% intensity (black) values.</p>
+     * <p>This test pattern is identical to SOLID_COLOR with a value of <code>[0, 0, 0, 0]</code> for
+     * {@link CaptureRequest#SENSOR_TEST_PATTERN_DATA android.sensor.testPatternData}.  It is recommended that devices implement full
+     * SOLID_COLOR support instead, but BLACK can be used to provide minimal support for a
+     * test pattern suitable for privacy use cases.</p>
+     *
+     * @see CaptureRequest#SENSOR_TEST_PATTERN_DATA
+     * @see CaptureRequest#SENSOR_TEST_PATTERN_MODE
+     * @hide
+     */
+    @TestApi
+    public static final int SENSOR_TEST_PATTERN_MODE_BLACK = 5;
+
+    /**
      * <p>The first custom test pattern. All custom patterns that are
      * available only on this camera device are at least this numeric
      * value.</p>
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 8912997..a1f7aa1 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -53,7 +53,8 @@
                 || pickupGestureEnabled(user)
                 || tapGestureEnabled(user)
                 || doubleTapGestureEnabled(user)
-                || quickPickupSensorEnabled(user);
+                || quickPickupSensorEnabled(user)
+                || screenOffUdfpsEnabled(user);
     }
 
     /** {@hide} */
@@ -106,6 +107,12 @@
     }
 
     /** {@hide} */
+    public boolean screenOffUdfpsEnabled(int user) {
+        return !TextUtils.isEmpty(udfpsLongPressSensorType())
+            && boolSettingDefaultOff("screen_off_udfps_enabled", user);
+    }
+
+    /** {@hide} */
     public boolean wakeScreenGestureAvailable() {
         return mContext.getResources()
                 .getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable);
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 97e03e9..55b1f940 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -40,7 +40,12 @@
       ]
     },
     {
-      "file_patterns": ["BatteryStats.java"],
+      "file_patterns": [
+        "BatteryStats[^/]*\\.java",
+        "BatteryUsageStats[^/]*\\.java",
+        "PowerComponents\\.java",
+        "[^/]*BatteryConsumer[^/]*\\.java"
+      ],
       "name": "FrameworksCoreTests",
       "options": [
         { "include-filter": "com.android.internal.os.BatteryStatsTests" },
@@ -48,13 +53,26 @@
       ]
     },
     {
-      "file_patterns": ["BatteryStats.java"],
+      "file_patterns": [
+        "BatteryStats[^/]*\\.java",
+        "BatteryUsageStats[^/]*\\.java",
+        "PowerComponents\\.java",
+        "[^/]*BatteryConsumer[^/]*\\.java"
+      ],
       "name": "FrameworksServicesTests",
       "options": [
         { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
         { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" },
         { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" }
       ]
+    },
+    {
+      "file_patterns": [
+        "BatteryUsageStats[^/]*\\.java",
+        "PowerComponents\\.java",
+        "[^/]*BatteryConsumer[^/]*\\.java"
+      ],
+      "name": "BatteryUsageStatsProtoTests"
     }
   ],
   "postsubmit": [
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 055e71f..8b4c0d9 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -229,6 +229,7 @@
     protected abstract void onStopListening(Callback listener);
 
     @Override
+    @SuppressLint("MissingNullability")
     public Context createContext(@NonNull ContextParams contextParams) {
         if (contextParams.getNextAttributionSource() != null) {
             if (mHandler.getLooper().equals(Looper.myLooper())) {
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index ac45541..1b1dc4a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -25,8 +25,12 @@
 import android.graphics.Insets;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
 import android.util.Log;
 import android.view.autofill.AutofillId;
+import android.view.inputmethod.BaseInputConnection;
 
 import com.android.internal.util.Preconditions;
 
@@ -132,6 +136,9 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
 
+    /** @hide */
+    public static final int MAX_INVALID_VALUE = -1;
+
     private final int mSessionId;
     private final int mType;
     private final long mEventTime;
@@ -143,6 +150,11 @@
     private @Nullable ContentCaptureContext mClientContext;
     private @Nullable Insets mInsets;
 
+    private int mComposingStart = MAX_INVALID_VALUE;
+    private int mComposingEnd = MAX_INVALID_VALUE;
+    private int mSelectionStartIndex = MAX_INVALID_VALUE;
+    private int mSelectionEndIndex = MAX_INVALID_VALUE;
+
     /** Only used in the main Content Capture session, no need to parcel */
     private boolean mTextHasComposingSpan;
 
@@ -246,19 +258,75 @@
 
     /** @hide */
     @NonNull
-    public ContentCaptureEvent setText(@Nullable CharSequence text, boolean hasComposingSpan) {
+    public ContentCaptureEvent setText(@Nullable CharSequence text) {
         mText = text;
-        mTextHasComposingSpan = hasComposingSpan;
         return this;
     }
 
-    /**
-     * The value is not parcelled, become false after parcelled.
-     * @hide
-     */
+    /** @hide */
     @NonNull
-    public boolean getTextHasComposingSpan() {
-        return mTextHasComposingSpan;
+    public ContentCaptureEvent setComposingIndex(int start, int end) {
+        mComposingStart = start;
+        mComposingEnd = end;
+        return this;
+    }
+
+    /** @hide */
+    @NonNull
+    public boolean hasComposingSpan() {
+        return mComposingStart > MAX_INVALID_VALUE;
+    }
+
+    /** @hide */
+    @NonNull
+    public ContentCaptureEvent setSelectionIndex(int start, int end) {
+        mSelectionStartIndex = start;
+        mSelectionEndIndex = end;
+        return this;
+    }
+
+    private int getComposingStart() {
+        return mComposingStart;
+    }
+
+    private int getComposingEnd() {
+        return mComposingEnd;
+    }
+
+    private int getSelectionStart() {
+        return mSelectionStartIndex;
+    }
+
+    private int getSelectionEnd() {
+        return mSelectionEndIndex;
+    }
+
+    private void restoreComposingSpan() {
+        if (mComposingStart <= MAX_INVALID_VALUE
+                || mComposingEnd <= MAX_INVALID_VALUE) {
+            return;
+        }
+        if (mText instanceof Spannable) {
+            BaseInputConnection.setComposingSpans((Spannable) mText, mComposingStart,
+                    mComposingEnd);
+        } else {
+            Log.w(TAG, "Text is not a Spannable.");
+        }
+    }
+
+    private void restoreSelectionSpans() {
+        if (mSelectionStartIndex <= MAX_INVALID_VALUE
+                || mSelectionEndIndex <= MAX_INVALID_VALUE) {
+            return;
+        }
+
+        if (mText instanceof SpannableString) {
+            SpannableString ss = (SpannableString) mText;
+            ss.setSpan(Selection.SELECTION_START, mSelectionStartIndex, mSelectionStartIndex, 0);
+            ss.setSpan(Selection.SELECTION_END, mSelectionEndIndex, mSelectionEndIndex, 0);
+        } else {
+            Log.w(TAG, "Text is not a SpannableString.");
+        }
     }
 
     /** @hide */
@@ -374,7 +442,9 @@
             throw new IllegalArgumentException("mergeEvent(): got "
                     + "TYPE_VIEW_DISAPPEARED event with neither id or ids: " + event);
         } else if (eventType == TYPE_VIEW_TEXT_CHANGED) {
-            setText(event.getText(), event.getTextHasComposingSpan());
+            setText(event.getText());
+            setComposingIndex(event.getComposingStart(), event.getComposingEnd());
+            setSelectionIndex(event.getSelectionStart(), event.getSelectionEnd());
         } else {
             Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType)
                     + ") does not support this event type.");
@@ -409,6 +479,14 @@
         if (mInsets != null) {
             pw.print(", insets="); pw.println(mInsets);
         }
+        if (mComposingStart > MAX_INVALID_VALUE) {
+            pw.print(", composing("); pw.print(mComposingStart);
+            pw.print(", "); pw.print(mComposingEnd); pw.print(")");
+        }
+        if (mSelectionStartIndex > MAX_INVALID_VALUE) {
+            pw.print(", selection("); pw.print(mSelectionStartIndex);
+            pw.print(", "); pw.print(mSelectionEndIndex); pw.print(")");
+        }
     }
 
     @NonNull
@@ -443,6 +521,12 @@
         if (mInsets != null) {
             string.append(", insets=").append(mInsets);
         }
+        if (mComposingStart > MAX_INVALID_VALUE) {
+            string.append(", hasComposing");
+        }
+        if (mSelectionStartIndex > MAX_INVALID_VALUE) {
+            string.append(", hasSelection");
+        }
         return string.append(']').toString();
     }
 
@@ -469,6 +553,12 @@
         if (mType == TYPE_VIEW_INSETS_CHANGED) {
             parcel.writeParcelable(mInsets, flags);
         }
+        if (mType == TYPE_VIEW_TEXT_CHANGED) {
+            parcel.writeInt(mComposingStart);
+            parcel.writeInt(mComposingEnd);
+            parcel.writeInt(mSelectionStartIndex);
+            parcel.writeInt(mSelectionEndIndex);
+        }
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -493,7 +583,7 @@
             if (node != null) {
                 event.setViewNode(node);
             }
-            event.setText(parcel.readCharSequence(), false);
+            event.setText(parcel.readCharSequence());
             if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
                 event.setParentSessionId(parcel.readInt());
             }
@@ -503,6 +593,12 @@
             if (type == TYPE_VIEW_INSETS_CHANGED) {
                 event.setInsets(parcel.readParcelable(null));
             }
+            if (type == TYPE_VIEW_TEXT_CHANGED) {
+                event.setComposingIndex(parcel.readInt(), parcel.readInt());
+                event.restoreComposingSpan();
+                event.setSelectionIndex(parcel.readInt(), parcel.readInt());
+                event.restoreSelectionSpans();
+            }
             return event;
         }
 
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index aee540f..d8ac779 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -43,6 +43,7 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.text.Selection;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.Spanned;
@@ -347,8 +348,8 @@
             //    2.2 last event doesn't have composing span: add.
             // Otherwise, merge.
             final CharSequence text = event.getText();
-            final boolean textHasComposingSpan = event.getTextHasComposingSpan();
-            if (textHasComposingSpan) {
+            final boolean hasComposingSpan = event.hasComposingSpan();
+            if (hasComposingSpan) {
                 ContentCaptureEvent lastEvent = null;
                 for (int index = mEvents.size() - 1; index >= 0; index--) {
                     final ContentCaptureEvent tmpEvent = mEvents.get(index);
@@ -357,7 +358,7 @@
                         break;
                     }
                 }
-                if (lastEvent != null && lastEvent.getTextHasComposingSpan()) {
+                if (lastEvent != null && lastEvent.hasComposingSpan()) {
                     final CharSequence lastText = lastEvent.getText();
                     final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
                             && !TextUtils.isEmpty(text);
@@ -705,12 +706,24 @@
         // a copy of its content so that its value will not be changed by subsequent updates
         // in the TextView.
         final CharSequence eventText = stringOrSpannedStringWithoutNoCopySpans(text);
-        final boolean textHasComposingSpan =
-                text instanceof Spannable && BaseInputConnection.getComposingSpanStart(
-                        (Spannable) text) >= 0;
+
+        final int composingStart;
+        final int composingEnd;
+        if (text instanceof Spannable) {
+            composingStart = BaseInputConnection.getComposingSpanStart((Spannable) text);
+            composingEnd = BaseInputConnection.getComposingSpanEnd((Spannable) text);
+        } else {
+            composingStart = ContentCaptureEvent.MAX_INVALID_VALUE;
+            composingEnd = ContentCaptureEvent.MAX_INVALID_VALUE;
+        }
+
+        final int startIndex = Selection.getSelectionStart(text);
+        final int endIndex = Selection.getSelectionEnd(text);
         mHandler.post(() -> sendEvent(
                 new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
-                        .setAutofillId(id).setText(eventText, textHasComposingSpan)));
+                        .setAutofillId(id).setText(eventText)
+                        .setComposingIndex(composingStart, composingEnd)
+                        .setSelectionIndex(startIndex, endIndex)));
     }
 
     private CharSequence stringOrSpannedStringWithoutNoCopySpans(CharSequence source) {
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 2b22f08..5a5165d 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -14,6 +14,14 @@
     },
     {
       "file_patterns": [
+        "Battery[^/]*\\.java",
+        "Kernel[^/]*\\.java",
+        "[^/]*Power[^/]*\\.java"
+      ],
+      "name": "BatteryUsageStatsProtoTests"
+    },
+    {
+      "file_patterns": [
         "BinderDeathDispatcher\\.java"
       ],
       "name": "FrameworksCoreTests",
@@ -23,7 +31,11 @@
       ]
     },
     {
-      "file_patterns": ["Battery[^/]*\\.java"],
+      "file_patterns": [
+        "Battery[^/]*\\.java",
+        "Kernel[^/]*\\.java",
+        "[^/]*Power[^/]*\\.java"
+      ],
       "name": "FrameworksServicesTests",
       "options": [
         { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
index c419e46..e4c2a34 100644
--- a/core/res/res/layout/floating_popup_menu_button.xml
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -51,7 +51,7 @@
         android:gravity="center"
         android:singleLine="true"
         android:ellipsize="end"
-        android:fontFamily="sans-serif-medium"
+        android:fontFamily="@*android:string/config_bodyFontFamily"
         android:textSize="@dimen/floating_toolbar_text_size"
         android:textColor="?attr/floatingToolbarForegroundColor"
         android:background="@null"
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index e6a25d0..f28ee46 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -236,13 +236,13 @@
     @Test
     public void testMergeEvent_typeViewTextChanged() {
         final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_TEXT_CHANGED)
-                .setText("test", false);
+                .setText("test");
         final ContentCaptureEvent event2 = new ContentCaptureEvent(43, TYPE_VIEW_TEXT_CHANGED)
-                .setText("empty", true);
+                .setText("composing").setComposingIndex(0, 1);
 
         event.mergeEvent(event2);
         assertThat(event.getText()).isEqualTo(event2.getText());
-        assertThat(event.getTextHasComposingSpan()).isEqualTo(event2.getTextHasComposingSpan());
+        assertThat(event.hasComposingSpan()).isEqualTo(event2.hasComposingSpan());
     }
 
     @Test
@@ -283,18 +283,18 @@
     @Test
     public void testMergeEvent_differentEventTypes() {
         final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_DISAPPEARED)
-                .setText("test", false).setAutofillId(new AutofillId(1));
+                .setText("test").setAutofillId(new AutofillId(1));
         final ContentCaptureEvent event2 = new ContentCaptureEvent(17, TYPE_VIEW_TEXT_CHANGED)
-                .setText("empty", true).setAutofillId(new AutofillId(2));
+                .setText("composing").setAutofillId(new AutofillId(2)).setComposingIndex(0, 1);
 
         event.mergeEvent(event2);
         assertThat(event.getText()).isEqualTo("test");
-        assertThat(event.getTextHasComposingSpan()).isFalse();
+        assertThat(event.hasComposingSpan()).isFalse();
         assertThat(event.getId()).isEqualTo(new AutofillId(1));
 
         event2.mergeEvent(event);
-        assertThat(event2.getText()).isEqualTo("empty");
-        assertThat(event2.getTextHasComposingSpan()).isTrue();
+        assertThat(event2.getText()).isEqualTo("composing");
+        assertThat(event2.hasComposingSpan()).isTrue();
         assertThat(event2.getId()).isEqualTo(new AutofillId(2));
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 8c8a56a..dfd878f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -368,6 +368,13 @@
                         return;
                     }
                 }
+                for (Bubble b : mBubbleData.getOverflowBubbles()) {
+                    if (task.taskId == b.getTaskId()) {
+                        promoteBubbleFromOverflow(b);
+                        mBubbleData.setExpanded(true);
+                        return;
+                    }
+                }
             }
         });
 
@@ -815,7 +822,35 @@
         setIsBubble(bubble, true /* isBubble */);
     }
 
-    @VisibleForTesting
+    /**
+     * Expands and selects the provided bubble as long as it already exists in the stack or the
+     * overflow.
+     *
+     * This is currently only used when opening a bubble via clicking on a conversation widget.
+     */
+    public void expandStackAndSelectBubble(Bubble b) {
+        if (b == null) {
+            return;
+        }
+        if (mBubbleData.hasBubbleInStackWithKey(b.getKey())) {
+            // already in the stack
+            mBubbleData.setSelectedBubble(b);
+            mBubbleData.setExpanded(true);
+        } else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) {
+            // promote it out of the overflow
+            promoteBubbleFromOverflow(b);
+        }
+    }
+
+    /**
+     * Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble
+     * exists for this entry, and it is able to bubble, a new bubble will be created.
+     *
+     * This is the method to use when opening a bubble via a notification or in a state where
+     * the device might not be unlocked.
+     *
+     * @param entry the entry to use for the bubble.
+     */
     public void expandStackAndSelectBubble(BubbleEntry entry) {
         if (mIsStatusBarShade) {
             mNotifEntryToExpandOnShadeUnlock = null;
@@ -1383,6 +1418,21 @@
         }
 
         @Override
+        public void expandStackAndSelectBubble(Bubble bubble) {
+            mMainExecutor.execute(() -> {
+                BubbleController.this.expandStackAndSelectBubble(bubble);
+            });
+        }
+
+        @Override
+        @Nullable
+        public Bubble getBubbleWithShortcutId(String shortcutId) {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return BubbleController.this.mBubbleData.getAnyBubbleWithShortcutId(shortcutId);
+            }, Bubble.class);
+        }
+
+        @Override
         public void onTaskbarChanged(Bundle b) {
             mMainExecutor.execute(() -> {
                 BubbleController.this.onTaskbarChanged(b);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 69a741c..6f5cfd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.LocusId;
 import android.content.pm.ShortcutInfo;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -874,6 +875,34 @@
         return b;
     }
 
+    /** @return any bubble (in the stack or the overflow) that matches the provided shortcutId. */
+    @Nullable
+    Bubble getAnyBubbleWithShortcutId(String shortcutId) {
+        if (TextUtils.isEmpty(shortcutId)) {
+            return null;
+        }
+        for (int i = 0; i < mBubbles.size(); i++) {
+            Bubble bubble = mBubbles.get(i);
+            String bubbleShortcutId = bubble.getShortcutInfo() != null
+                    ? bubble.getShortcutInfo().getId()
+                    : bubble.getMetadataShortcutId();
+            if (shortcutId.equals(bubbleShortcutId)) {
+                return bubble;
+            }
+        }
+
+        for (int i = 0; i < mOverflowBubbles.size(); i++) {
+            Bubble bubble = mOverflowBubbles.get(i);
+            String bubbleShortcutId = bubble.getShortcutInfo() != null
+                    ? bubble.getShortcutInfo().getId()
+                    : bubble.getMetadataShortcutId();
+            if (shortcutId.equals(bubbleShortcutId)) {
+                return bubble;
+            }
+        }
+        return null;
+    }
+
     @VisibleForTesting(visibility = PRIVATE)
     @Nullable
     public Bubble getBubbleInStackWithKey(String key) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 16b8150..c71f123 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -50,7 +50,6 @@
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.widget.FrameLayout;
@@ -1018,7 +1017,10 @@
             removeView(mDismissView);
         }
         mDismissView = new DismissView(getContext());
+        int elevation = getResources().getDimensionPixelSize(R.dimen.bubble_elevation);
+
         addView(mDismissView);
+        mDismissView.setElevation(elevation);
 
         final ContentResolver contentResolver = getContext().getContentResolver();
         final int dismissRadius = Settings.Secure.getInt(
@@ -1028,6 +1030,8 @@
         // MagnetizedObjects.
         mMagneticTarget = new MagnetizedObject.MagneticTarget(
                 mDismissView.getCircle(), dismissRadius);
+
+        mBubbleContainer.bringToFront();
     }
 
     // TODO: Create ManageMenuView and move setup / animations there
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index a93ce01..c73b5ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -118,6 +118,19 @@
      */
     void expandStackAndSelectBubble(BubbleEntry entry);
 
+    /**
+     * Request the stack expand if needed, then select the specified Bubble as current.
+     *
+     * @param bubble the bubble to be selected
+     */
+    void expandStackAndSelectBubble(Bubble bubble);
+
+    /**
+     * @return a bubble that matches the provided shortcutId, if one exists.
+     */
+    @Nullable
+    Bubble getBubbleWithShortcutId(String shortcutId);
+
     /** Called for any taskbar changes. */
     void onTaskbarChanged(Bundle b);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 32553f9..841edef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -536,8 +536,9 @@
                 mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
                         PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback);
             }
+            final float magnetRadiusPercent = (float) mLastResizeBounds.width() / mMinSize.x / 2.f;
             mPipDismissTargetHandler
-                    .setMagneticFieldRadiusPercent((float) mLastResizeBounds.width() / mMinSize.x);
+                    .setMagneticFieldRadiusPercent(magnetRadiusPercent);
             mPipUiEventLogger.log(
                     PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
         } else {
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 46e8060..ded2b06 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -210,6 +210,14 @@
     }
 }
 
+void CacheManager::performDeferredCleanup(nsecs_t cleanupOlderThanMillis) {
+    if (mGrContext) {
+        mGrContext->performDeferredCleanup(
+            std::chrono::milliseconds(cleanupOlderThanMillis),
+            /* scratchResourcesOnly */true);
+    }
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 713ea99..af82672 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -23,6 +23,7 @@
 #include <SkSurface.h>
 #include <utils/String8.h>
 #include <vector>
+#include "utils/TimeUtils.h"
 
 namespace android {
 
@@ -53,6 +54,8 @@
     size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
     void onFrameCompleted();
 
+    void performDeferredCleanup(nsecs_t cleanupOlderThanMillis);
+
 private:
     friend class RenderThread;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a0d93e9..8bfc2c1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -461,6 +461,7 @@
 }
 
 void CanvasContext::stopDrawing() {
+    cleanupResources();
     mRenderThread.removeFrameCallback(this);
     mAnimationContext->pauseAnimators();
     mGenerationID++;
@@ -619,10 +620,25 @@
         }
     }
 
+    cleanupResources();
     mRenderThread.cacheManager().onFrameCompleted();
     return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
 }
 
+void CanvasContext::cleanupResources() {
+    auto& tracker = mJankTracker.frames();
+    auto size = tracker.size();
+    auto capacity = tracker.capacity();
+    if (size == capacity) {
+        nsecs_t nowNanos = systemTime(SYSTEM_TIME_MONOTONIC);
+        nsecs_t frameCompleteNanos =
+            tracker[0].get(FrameInfoIndex::FrameCompleted);
+        nsecs_t frameDiffNanos = nowNanos - frameCompleteNanos;
+        nsecs_t cleanupMillis = ns2ms(std::max(frameDiffNanos, 10_s));
+        mRenderThread.cacheManager().performDeferredCleanup(cleanupMillis);
+    }
+}
+
 void CanvasContext::reportMetricsWithPresentTime() {
     if (mFrameMetricsReporter == nullptr) {
         return;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6f90e81..4bdc251 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -312,6 +312,7 @@
     bool mExpectSurfaceStats = false;
 
     std::function<void(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
+    void cleanupResources();
 };
 
 } /* namespace renderthread */
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
index eada22c..88a2479 100644
--- a/location/java/android/location/provider/LocationProviderBase.java
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -62,6 +62,10 @@
  * <p>The service should have an intent filter in place for the location provider it wishes to
  * implements. Defaults for some providers are specified as constants in this class.
  *
+ * <p>Location providers are identified by their UID / package name / attribution tag. Based on this
+ * identity, location providers may be given some special privileges (such as making special
+ * requests to other location providers).
+ *
  * @hide
  */
 @SystemApi
@@ -95,14 +99,14 @@
     public static final String ACTION_FUSED_PROVIDER =
             "com.android.location.service.FusedLocationProvider";
 
-    private final String mTag;
-    private final @Nullable String mAttributionTag;
-    private final IBinder mBinder;
+    final String mTag;
+    final @Nullable String mAttributionTag;
+    final IBinder mBinder;
 
     // write locked on mBinder, read lock is optional depending on atomicity requirements
-    private @Nullable volatile ILocationProviderManager mManager;
-    private volatile ProviderProperties mProperties;
-    private volatile boolean mAllowed;
+    volatile @Nullable ILocationProviderManager mManager;
+    volatile ProviderProperties mProperties;
+    volatile boolean mAllowed;
 
     public LocationProviderBase(@NonNull Context context, @NonNull String tag,
             @NonNull ProviderProperties properties) {
diff --git a/packages/SystemUI/res/drawable/people_space_activity_card.xml b/packages/SystemUI/res/drawable/people_space_activity_card.xml
deleted file mode 100644
index 7e2db63..0000000
--- a/packages/SystemUI/res/drawable/people_space_activity_card.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@color/people_tile_background" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml b/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml
index aa940bd..29a014a 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
-    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <solid android:color="?androidprv:attr/colorAccentPrimary" />
     <corners android:radius="40dp" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
index 4f97ca4..21b177b 100644
--- a/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
@@ -32,7 +32,7 @@
         android:gravity="center_vertical|right"
         android:height="@dimen/rounded_slider_icon_size"
         android:width="@dimen/rounded_slider_icon_size"
-        android:right="@dimen/rounded_slider_icon_inset">
+        android:right="@dimen/volume_slider_icon_inset">
         <rotate
             android:fromDegrees="-270"
             android:toDegrees="-270">
diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml
index fa19943..7102375 100644
--- a/packages/SystemUI/res/layout/people_space_activity.xml
+++ b/packages/SystemUI/res/layout/people_space_activity.xml
@@ -15,6 +15,7 @@
   -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/top_level"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -41,7 +42,8 @@
         android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
         android:textColor="?android:attr/textColorPrimary"
         android:textSize="16sp"
-        android:padding="24dp" />
+        android:paddingVertical="24dp"
+        android:paddingHorizontal="48dp"/>
 
     <androidx.core.widget.NestedScrollView
         android:id="@+id/scroll_view"
@@ -65,8 +67,8 @@
                     android:id="@+id/priority_header"
                     android:text="@string/priority_conversations"
                     android:layout_width="wrap_content"
-                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-                    android:textColor="?android:attr/colorAccent"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+                    android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
                     android:textSize="14sp"
                     android:paddingStart="16dp"
                     android:layout_height="wrap_content"/>
@@ -92,8 +94,8 @@
                     android:gravity="start"
                     android:text="@string/recent_conversations"
                     android:layout_width="wrap_content"
-                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-                    android:textColor="?android:attr/colorAccent"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+                    android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
                     android:textSize="14sp"
                     android:paddingStart="16dp"
                     android:layout_height="wrap_content"/>
diff --git a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
index 232cd72..2a4a21f 100644
--- a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
+++ b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
@@ -55,6 +55,7 @@
         android:background="@drawable/rounded_bg_full_large_radius"
         android:onClick="dismissActivity"
         android:text="@string/okay"
+        android:textColor="?android:attr/textColorPrimary"
         android:layout_marginBottom="60dp"
         android:layout_alignParentBottom="true" />
 
diff --git a/packages/SystemUI/res/layout/people_space_tile_view.xml b/packages/SystemUI/res/layout/people_space_tile_view.xml
index 3e90180..2a2c35d 100644
--- a/packages/SystemUI/res/layout/people_space_tile_view.xml
+++ b/packages/SystemUI/res/layout/people_space_tile_view.xml
@@ -13,7 +13,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/tile_view"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -21,7 +23,7 @@
 
     <LinearLayout
         android:orientation="vertical"
-        android:background="@drawable/people_space_activity_card"
+        android:background="?androidprv:attr/colorSurface"
         android:padding="12dp"
         android:elevation="4dp"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 76a4c5f..0ccde60 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -486,6 +486,9 @@
 
     <dimen name="volume_dialog_slider_height">116dp</dimen>
 
+    <!-- (volume_dialog_panel_width - rounded_slider_icon_size) / 2 -->
+    <dimen name="volume_slider_icon_inset">11dp</dimen>
+
     <dimen name="volume_dialog_track_width">4dp</dimen>
 
     <dimen name="volume_dialog_track_corner_radius">2dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3d51f23..f3a6d63 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -66,10 +66,8 @@
 import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -243,21 +241,14 @@
     private final boolean mIsPrimaryUser;
     private final boolean mIsAutomotive;
     private final AuthController mAuthController;
-    private final PowerManager mPowerManager;
     private final StatusBarStateController mStatusBarStateController;
     private int mStatusBarState;
-    private boolean mDozing;
     private final StatusBarStateController.StateListener mStatusBarStateControllerListener =
             new StatusBarStateController.StateListener() {
         @Override
         public void onStateChanged(int newState) {
             mStatusBarState = newState;
         }
-
-        @Override
-        public void onDozingChanged(boolean dozing) {
-            mDozing = dozing;
-        }
     };
 
     HashMap<Integer, SimData> mSimDatas = new HashMap<>();
@@ -1330,19 +1321,16 @@
 
     private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
             = new AuthenticationCallback() {
-        private boolean mIsUdfpsRunningWhileDozing;
 
         @Override
         public void onAuthenticationFailed() {
             handleFingerprintAuthFailed();
-            cancelAodInterrupt();
         }
 
         @Override
         public void onAuthenticationSucceeded(AuthenticationResult result) {
             Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
             handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric());
-            cancelAodInterrupt();
             Trace.endSection();
         }
 
@@ -1354,7 +1342,6 @@
         @Override
         public void onAuthenticationError(int errMsgId, CharSequence errString) {
             handleFingerprintError(errMsgId, errString.toString());
-            cancelAodInterrupt();
         }
 
         @Override
@@ -1365,25 +1352,12 @@
         @Override
         public void onUdfpsPointerDown(int sensorId) {
             Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId);
-
-            if (mDozing) {
-                mIsUdfpsRunningWhileDozing = true;
-            }
         }
 
         @Override
         public void onUdfpsPointerUp(int sensorId) {
             Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId);
         }
-
-        private void cancelAodInterrupt() {
-            if (mIsUdfpsRunningWhileDozing) {
-                mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
-                        "com.android.systemui:AOD_INTERRUPT_END");
-            }
-            mAuthController.onCancelUdfps();
-            mIsUdfpsRunningWhileDozing = false;
-        }
     };
 
     private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
@@ -1676,7 +1650,6 @@
             LockPatternUtils lockPatternUtils,
             AuthController authController,
             TelephonyListenerManager telephonyListenerManager,
-            PowerManager powerManager,
             FeatureFlags featureFlags) {
         mContext = context;
         mSubscriptionManager = SubscriptionManager.from(context);
@@ -1689,10 +1662,8 @@
         mStatusBarStateController = statusBarStateController;
         mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
         mStatusBarState = mStatusBarStateController.getState();
-        mDozing = mStatusBarStateController.isDozing();
         mLockPatternUtils = lockPatternUtils;
         mAuthController = authController;
-        mPowerManager = powerManager;
         dumpManager.registerDumpable(getClass().getName(), this);
 
         mHandler = new Handler(mainLooper) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 5e6b904..7947241 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -43,6 +43,7 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.IUdfpsHbmListener;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -100,6 +101,8 @@
     @Nullable
     private UdfpsController mUdfpsController;
     @Nullable
+    private IUdfpsHbmListener mUdfpsHbmListener;
+    @Nullable
     private SidefpsController mSidefpsController;
     @VisibleForTesting
     TaskStackListener mTaskStackListener;
@@ -470,6 +473,24 @@
         mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
     }
 
+    /**
+     * Stores the listener received from {@link com.android.server.display.DisplayModeDirector}.
+     *
+     * DisplayModeDirector implements {@link IUdfpsHbmListener} and registers it with this class by
+     * calling {@link CommandQueue#setUdfpsHbmListener(IUdfpsHbmListener)}.
+     */
+    @Override
+    public void setUdfpsHbmListener(IUdfpsHbmListener listener) {
+        mUdfpsHbmListener = listener;
+    }
+
+    /**
+     * @return IUdfpsHbmListener that can be set by DisplayModeDirector.
+     */
+    @Nullable public IUdfpsHbmListener getUdfpsHbmListener() {
+        return mUdfpsHbmListener;
+    }
+
     @Override
     public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
             int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 39adabb..23c4413 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -70,6 +70,7 @@
     private final Consumer<Boolean> mProxCallback;
     private final SecureSettings mSecureSettings;
     private final Callback mCallback;
+    private final boolean mScreenOffUdfpsEnabled;
     @VisibleForTesting
     protected TriggerSensor[] mSensors;
 
@@ -116,6 +117,8 @@
         mProximitySensor = proximitySensor;
         mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx();
         mListeningProxSensors = !mSelectivelyRegisterProxSensors;
+        mScreenOffUdfpsEnabled =
+                config.screenOffUdfpsEnabled(KeyguardUpdateMonitor.getCurrentUser());
 
         boolean udfpsEnrolled =
                 authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
@@ -171,7 +174,7 @@
                         findSensorWithType(config.udfpsLongPressSensorType()),
                         "doze_pulse_on_auth",
                         true /* settingDef */,
-                        udfpsEnrolled,
+                        udfpsEnrolled && (alwaysOn || mScreenOffUdfpsEnabled),
                         DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
                         true /* reports touch coordinates */,
                         true /* touchscreen */,
@@ -369,6 +372,7 @@
         pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors);
         pw.println("mSelectivelyRegisterProxSensors=" + mSelectivelyRegisterProxSensors);
         pw.println("mListeningProxSensors=" + mListeningProxSensors);
+        pw.println("mScreenOffUdfpsEnabled=" + mScreenOffUdfpsEnabled);
         IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
         idpw.increaseIndent();
         for (TriggerSensor s : mSensors) {
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 6a025a7..d9e2648 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -91,8 +91,8 @@
             // than the activity's background.
             LinearLayout item = findViewById(R.id.item);
             GradientDrawable shape = (GradientDrawable) item.getBackground();
-            final TypedArray ta = mContext.obtainStyledAttributes(
-                    new int[]{android.R.attr.colorBackgroundFloating});
+            final TypedArray ta = mContext.getTheme().obtainStyledAttributes(
+                    new int[]{com.android.internal.R.attr.colorSurface});
             shape.setColor(ta.getColor(0, Color.WHITE));
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index c416b5e..b031637 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -35,9 +35,11 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.people.PeopleSpaceUtils;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.Bubble;
 
 import java.util.Optional;
 
@@ -53,14 +55,35 @@
     private final UserManager mUserManager;
     private boolean mIsForTesting;
     private IStatusBarService mIStatusBarService;
+    private CommandQueue mCommandQueue;
+    private Bubble mBubble;
+    private NotificationEntry mEntryToBubble;
 
     @Inject
     public LaunchConversationActivity(NotificationEntryManager notificationEntryManager,
-            Optional<BubblesManager> bubblesManagerOptional, UserManager userManager) {
+            Optional<BubblesManager> bubblesManagerOptional, UserManager userManager,
+            CommandQueue commandQueue) {
         super();
         mNotificationEntryManager = notificationEntryManager;
         mBubblesManagerOptional = bubblesManagerOptional;
         mUserManager = userManager;
+        mCommandQueue = commandQueue;
+        mCommandQueue.addCallback(new CommandQueue.Callbacks() {
+            // (b/190833924) Wait for the app transition to finish before showing the bubble,
+            // opening the bubble while the transition is happening can mess with the placement
+            // of the  bubble's surface.
+            @Override
+            public void appTransitionFinished(int displayId) {
+                if (mBubblesManagerOptional.isPresent()) {
+                    if (mBubble != null) {
+                        mBubblesManagerOptional.get().expandStackAndSelectBubble(mBubble);
+                    } else if (mEntryToBubble != null) {
+                        mBubblesManagerOptional.get().expandStackAndSelectBubble(mEntryToBubble);
+                    }
+                }
+                mCommandQueue.removeCallback(this);
+            }
+        });
     }
 
     @Override
@@ -95,14 +118,28 @@
                     return;
                 }
 
-                NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
-                        notificationKey);
-                if (entry != null && entry.canBubble() && mBubblesManagerOptional.isPresent()) {
-                    if (DEBUG) Log.d(TAG, "Open bubble for conversation");
-                    mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
-                    // Just opt-out and don't cancel the notification for bubbles.
-                    finish();
-                    return;
+                // We can potentially bubble without a notification, so rather than rely on
+                // notificationKey here (which could be null if there's no notification or if the
+                // bubble is suppressing the notification), so we'll use the shortcutId for lookups.
+                // This misses one specific case: a bubble that was never opened & still has a
+                // visible notification, but the bubble was dismissed & aged out of the overflow.
+                // So it wouldn't exist in the stack or overflow to be looked up BUT the notif entry
+                // would still exist & be bubbleable. So if we don't get a bubble from the
+                // shortcutId, fallback to notificationKey if it exists.
+                if (mBubblesManagerOptional.isPresent()) {
+                    mBubble = mBubblesManagerOptional.get().getBubbleWithShortcutId(tileId);
+                    NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+                            notificationKey);
+                    if (mBubble != null || (entry != null && entry.canBubble())) {
+                        mEntryToBubble = entry;
+                        if (DEBUG) {
+                            Log.d(TAG,
+                                    "Opening bubble: " + mBubble  + ", entry: " + mEntryToBubble);
+                        }
+                        // Just opt-out and don't cancel the notification for bubbles.
+                        finish();
+                        return;
+                    }
                 }
 
                 if (mIStatusBarService == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 08a68bc..7cc6ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -266,10 +266,9 @@
 
     private void updateAlphaAnimator() {
         TouchAnimator.Builder builder = new TouchAnimator.Builder()
-                // The following two views have to be hidden manually, so as not to hide the
-                // Privacy chip in QQS
-                .addFloat(mDateView, "alpha", 0, 1)
                 .addFloat(mSecurityHeaderView, "alpha", 0, 1)
+                // These views appear on expanding down
+                .addFloat(mClockView, "alpha", 0, 1)
                 .addFloat(mQSCarriers, "alpha", 0, 1)
                 .setListener(new TouchAnimator.ListenerAdapter() {
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 32a6c6c..24b9208 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -103,6 +103,8 @@
         state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.label = mContext.getString(R.string.quick_settings_screen_record_label);
         state.icon = ResourceIcon.get(R.drawable.ic_screenrecord);
+        // Show expand icon when clicking will open a dialog
+        state.forceExpandIcon = state.state == Tile.STATE_INACTIVE;
 
         if (isRecording) {
             state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_stop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 085a076..baac254 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -259,11 +259,9 @@
             final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast,
                     expandingAnimated, isLastChild);
 
-            final float stackEnd = mAmbientState.getStackY()
-                    + mAmbientState.getStackHeight();
             // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
             if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
-                notificationClipEnd = stackEnd;
+                notificationClipEnd = shelfStart + getIntrinsicHeight();
             } else {
                 notificationClipEnd = shelfStart - mPaddingBetweenElements;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 298d4f0..8e24890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -115,13 +115,18 @@
     }
 
     public class FooterViewState extends ExpandableViewState {
+        /**
+         * used to hide the content of the footer to animate.
+         * #hide is applied without animation, but #hideContent has animation.
+         */
+        public boolean hideContent;
+
         @Override
         public void applyToView(View view) {
             super.applyToView(view);
             if (view instanceof FooterView) {
                 FooterView footerView = (FooterView) view;
-                boolean visible = this.clipTopAmount < mClearAllTopPadding;
-                footerView.setContentVisible(visible && footerView.isVisible());
+                footerView.setContentVisible(!hideContent);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 8277fae..f90b4c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4550,6 +4550,12 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setQsExpansionFraction(float qsExpansionFraction) {
         mQsExpansionFraction = qsExpansionFraction;
+
+        // If notifications are scrolled,
+        // clear out scrollY by the time we push notifications offscreen
+        if (mOwnScrollY > 0) {
+            setOwnScrollY((int) MathUtils.lerp(mOwnScrollY, 0, mQsExpansionFraction));
+        }
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index a02ebbf..e5fd103 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -411,8 +411,8 @@
             final float footerEnd = algorithmState.mCurrentExpandedYPosition
                     + view.getIntrinsicHeight();
             final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
-
-            viewState.hidden = shadeClosed || isShelfShowing || noSpaceForFooter;
+            ((FooterView.FooterViewState) viewState).hideContent =
+                    shadeClosed || isShelfShowing || noSpaceForFooter;
 
         } else if (view != ambientState.getTrackedHeadsUpRow()) {
             if (ambientState.isExpansionChanging()) {
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 b3569d0..528827f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -101,6 +101,7 @@
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
 import com.android.systemui.plugins.qs.DetailAdapter;
@@ -108,6 +109,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qs.QSDetailDisplayer;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
@@ -581,6 +583,11 @@
      * The alpha of the views which only show on the keyguard but not in shade / shade locked
      */
     private float mKeyguardOnlyContentAlpha = 1.0f;
+
+    /**
+     * Are we currently in gesture navigation
+     */
+    private boolean mIsGestureNavigation;
     private int mOldLayoutDirection;
     private NotificationShelfController mNotificationShelfController;
     private int mScrimCornerRadius;
@@ -669,6 +676,7 @@
             KeyguardMediaController keyguardMediaController,
             PrivacyDotViewController privacyDotViewController,
             TapAgainViewController tapAgainViewController,
+            NavigationModeController navigationModeController,
             FragmentService fragmentService,
             QuickAccessWalletController quickAccessWalletController,
             @Main Executor uiExecutor,
@@ -769,6 +777,9 @@
         mAuthController = authController;
         mLockIconViewController = lockIconViewController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+        int currentMode = navigationModeController.addListener(
+                mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
+        mIsGestureNavigation = QuickStepContract.isGesturalMode(currentMode);
 
         mView.setBackgroundColor(Color.TRANSPARENT);
         OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
@@ -1808,9 +1819,15 @@
     }
 
     private boolean isInQsArea(float x, float y) {
-        return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && (
-                y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
-                        || y <= mQs.getView().getY() + mQs.getView().getHeight());
+        if (x < mQsFrame.getX() || x > mQsFrame.getX() + mQsFrame.getWidth()) {
+            return false;
+        }
+        // Let's reject anything at the very bottom around the home handle in gesture nav
+        if (mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight) {
+            return false;
+        }
+        return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
+                        || y <= mQs.getView().getY() + mQs.getView().getHeight();
     }
 
     private boolean isOpenQsEvent(MotionEvent event) {
@@ -2200,8 +2217,9 @@
     }
 
     private void updateQSExpansionEnabledAmbient() {
+        final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
         mQsExpansionEnabledAmbient =
-                mAmbientState.getScrollY() <= 0 && !mAmbientState.isShadeOpening();
+                mAmbientState.getScrollY() <= scrollRangeToTop && !mAmbientState.isShadeOpening();
         setQsExpansionEnabled();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 10c4a55..5441bd4 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -84,6 +84,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleEntry;
 import com.android.wm.shell.bubbles.Bubbles;
 
@@ -657,6 +658,22 @@
         mBubbles.expandStackAndSelectBubble(notifToBubbleEntry(entry));
     }
 
+    /**
+     * Request the stack expand if needed, then select the specified Bubble as current.
+     *
+     * @param bubble the bubble to be selected
+     */
+    public void expandStackAndSelectBubble(Bubble bubble) {
+        mBubbles.expandStackAndSelectBubble(bubble);
+    }
+
+    /**
+     * @return a bubble that matches the provided shortcutId, if one exists.
+     */
+    public Bubble getBubbleWithShortcutId(String shortcutId) {
+        return mBubbles.getBubbleWithShortcutId(shortcutId);
+    }
+
     /** See {@link NotifCallback}. */
     public void addNotifCallback(NotifCallback callback) {
         mCallbacks.add(callback);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 0342796..3d4da27 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -62,7 +62,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IRemoteCallback;
-import android.os.PowerManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telephony.ServiceState;
@@ -166,8 +165,6 @@
     @Mock
     private AuthController mAuthController;
     @Mock
-    private PowerManager mPowerManager;
-    @Mock
     private TelephonyListenerManager mTelephonyListenerManager;
     @Mock
     private FeatureFlags mFeatureFlags;
@@ -526,46 +523,6 @@
     }
 
     @Test
-    public void testFingerprintCancelAodInterrupt_onAuthenticationFailed() {
-        // GIVEN on keyguard and listening for fingerprint authentication
-        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
-        mTestableLooper.processAllMessages();
-
-        ArgumentCaptor<FingerprintManager.AuthenticationCallback> fingerprintCallbackCaptor =
-                ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback.class);
-        verify(mFingerprintManager).authenticate(any(), any(), fingerprintCallbackCaptor.capture(),
-                any(), anyInt(), anyInt());
-        FingerprintManager.AuthenticationCallback authCallback =
-                fingerprintCallbackCaptor.getValue();
-
-        // WHEN authentication fails
-        authCallback.onAuthenticationFailed();
-
-        // THEN aod interrupt is cancelled
-        verify(mAuthController).onCancelUdfps();
-    }
-
-    @Test
-    public void testFingerprintCancelAodInterrupt_onAuthenticationError() {
-        // GIVEN on keyguard and listening for fingerprint authentication
-        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
-        mTestableLooper.processAllMessages();
-
-        ArgumentCaptor<FingerprintManager.AuthenticationCallback> fingerprintCallbackCaptor =
-                ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback.class);
-        verify(mFingerprintManager).authenticate(any(), any(), fingerprintCallbackCaptor.capture(),
-                any(), anyInt(), anyInt());
-        FingerprintManager.AuthenticationCallback authCallback =
-                fingerprintCallbackCaptor.getValue();
-
-        // WHEN authentication errors
-        authCallback.onAuthenticationError(0, "");
-
-        // THEN aod interrupt is cancelled
-        verify(mAuthController).onCancelUdfps();
-    }
-
-    @Test
     public void skipsAuthentication_whenStatusBarShadeLocked() {
         mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -1022,7 +979,7 @@
                     mBroadcastDispatcher, mDumpManager,
                     mRingerModeTracker, mBackgroundExecutor,
                     mStatusBarStateController, mLockPatternUtils,
-                    mAuthController, mTelephonyListenerManager, mPowerManager, mFeatureFlags);
+                    mAuthController, mTelephonyListenerManager, mFeatureFlags);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 724f8a3..d6226aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -73,6 +73,7 @@
         when(config.dozePickupSensorAvailable()).thenReturn(false);
         when(config.wakeScreenGestureAvailable()).thenReturn(false);
         when(config.quickPickupSensorEnabled(anyInt())).thenReturn(false);
+        when(config.screenOffUdfpsEnabled(anyInt())).thenReturn(false);
 
         doneHolder[0] = true;
         return config;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index ccb40e1..5f4d90b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -16,11 +16,14 @@
 
 package com.android.systemui.people.widget;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -39,9 +42,11 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.Bubble;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -87,6 +92,8 @@
     @Mock
     private UserManager mUserManager;
 
+    private CommandQueue mCommandQueue;
+
     @Captor
     private ArgumentCaptor<NotificationVisibility> mNotificationVisibilityCaptor;
 
@@ -95,8 +102,9 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mCommandQueue = new CommandQueue(mContext);
         mActivity = new LaunchConversationActivity(mNotificationEntryManager,
-                Optional.of(mBubblesManager), mUserManager);
+                Optional.of(mBubblesManager), mUserManager, mCommandQueue);
         mActivity.setIsForTesting(true, mIStatusBarService);
         mIntent = new Intent();
         mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, "tile ID");
@@ -159,9 +167,13 @@
         mActivity.setIntent(mIntent);
         mActivity.onCreate(new Bundle());
 
+        assertThat(mActivity.isFinishing()).isTrue();
+        mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
+
         verify(mIStatusBarService, times(1)).onNotificationClear(any(),
                 anyInt(), any(), anyInt(), anyInt(), mNotificationVisibilityCaptor.capture());
-        verify(mBubblesManager, never()).expandStackAndSelectBubble(any());
+        verify(mBubblesManager, never()).expandStackAndSelectBubble(any(Bubble.class));
+        verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class));
 
         NotificationVisibility nv = mNotificationVisibilityCaptor.getValue();
         assertThat(nv.count).isEqualTo(NOTIF_COUNT);
@@ -175,6 +187,9 @@
         mActivity.setIntent(mIntent);
         mActivity.onCreate(new Bundle());
 
+        assertThat(mActivity.isFinishing()).isTrue();
+        mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
+
         // Don't clear the notification for bubbles.
         verify(mIStatusBarService, never()).onNotificationClear(any(),
                 anyInt(), any(), anyInt(), anyInt(), any());
@@ -190,8 +205,27 @@
         mActivity.onCreate(new Bundle());
 
         assertThat(mActivity.isFinishing()).isTrue();
+        mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
+
         verify(mIStatusBarService, never()).onNotificationClear(any(),
                 anyInt(), any(), anyInt(), anyInt(), any());
-        verify(mBubblesManager, never()).expandStackAndSelectBubble(any());
+        verify(mBubblesManager, never()).expandStackAndSelectBubble(any(Bubble.class));
+        verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class));
+    }
+
+    @Test
+    public void testBubbleWithNoNotifOpensBubble() throws Exception {
+        Bubble bubble = mock(Bubble.class);
+        when(mBubblesManager.getBubbleWithShortcutId(any())).thenReturn(bubble);
+
+        mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY,
+                EMPTY_STRING);
+        mActivity.setIntent(mIntent);
+        mActivity.onCreate(new Bundle());
+
+        assertThat(mActivity.isFinishing()).isTrue();
+        mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
+
+        verify(mBubblesManager, times(1)).expandStackAndSelectBubble(eq(bubble));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index e4af21a..3b4e863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -19,6 +19,7 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
+import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -167,4 +168,37 @@
 
         assertTrue(mTile.getState().contentDescription.toString().contains(mTile.getState().label));
     }
+
+    @Test
+    public void testForceExpandIcon_notRecordingNotStarting() {
+        when(mController.isStarting()).thenReturn(false);
+        when(mController.isRecording()).thenReturn(false);
+
+        mTile.refreshState();
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testForceExpandIcon_recordingNotStarting() {
+        when(mController.isStarting()).thenReturn(false);
+        when(mController.isRecording()).thenReturn(true);
+
+        mTile.refreshState();
+        mTestableLooper.processAllMessages();
+
+        assertFalse(mTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testForceExpandIcon_startingNotRecording() {
+        when(mController.isStarting()).thenReturn(true);
+        when(mController.isRecording()).thenReturn(false);
+
+        mTile.refreshState();
+        mTestableLooper.processAllMessages();
+
+        assertFalse(mTile.getState().forceExpandIcon);
+    }
 }
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 c6e5697..9e939ee 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
@@ -82,6 +82,7 @@
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.QSDetailDisplayer;
@@ -261,6 +262,8 @@
     @Mock
     private PrivacyDotViewController mPrivacyDotViewController;
     @Mock
+    private NavigationModeController mNavigationModeController;
+    @Mock
     private SecureSettings mSecureSettings;
     @Mock
     private TapAgainViewController mTapAgainViewController;
@@ -391,6 +394,7 @@
                 mKeyguardMediaController,
                 mPrivacyDotViewController,
                 mTapAgainViewController,
+                mNavigationModeController,
                 mFragmentService,
                 mQuickAccessWalletController,
                 new FakeExecutor(new FakeSystemClock()),
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8aea4a9..9f831d2 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -122,6 +122,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerExemptionManager;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -1850,7 +1851,6 @@
                     notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
                     r.foregroundNoti = notification;
                     r.foregroundServiceType = foregroundServiceType;
-                    boolean enterForeground = false;
                     if (!r.isForeground) {
                         final ServiceMap smap = getServiceMapLocked(r.userId);
                         if (smap != null) {
@@ -1877,7 +1877,12 @@
                         }
                         r.isForeground = true;
                         r.mLogEntering = true;
-                        enterForeground = true;
+                        // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
+                        // be deferred, make a copy of mAllowStartForeground and
+                        // mAllowWhileInUsePermissionInFgs.
+                        r.mAllowStartForegroundAtEntering = r.mAllowStartForeground;
+                        r.mAllowWhileInUsePermissionInFgsAtEntering =
+                                r.mAllowWhileInUsePermissionInFgs;
                         r.mStartForegroundCount++;
                         r.mFgsEnterTime = SystemClock.uptimeMillis();
                         if (!stopProcStatsOp) {
@@ -6235,12 +6240,22 @@
                 r.packageName, mAm.mConstants.mFgsAtomSampleRate)) {
             return;
         }
+        boolean allowWhileInUsePermissionInFgs;
+        @PowerExemptionManager.ReasonCode int fgsStartReasonCode;
+        if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER
+                || state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT) {
+            allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgsAtEntering;
+            fgsStartReasonCode = r.mAllowStartForegroundAtEntering;
+        } else {
+            allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgs;
+            fgsStartReasonCode = r.mAllowStartForeground;
+        }
         FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                 r.appInfo.uid,
                 r.shortInstanceName,
                 state,
-                r.mAllowWhileInUsePermissionInFgs,
-                r.mAllowStartForeground,
+                allowWhileInUsePermissionInFgs,
+                fgsStartReasonCode,
                 r.appInfo.targetSdkVersion,
                 r.mRecentCallingUid,
                 r.mRecentCallerApplicationInfo != null
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0757e7b..d71919e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -102,7 +102,6 @@
 import com.android.server.compat.PlatformCompat;
 
 import java.io.BufferedReader;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -806,8 +805,7 @@
             return -1;
         }
 
-        File file = new File(filename);
-        file.delete();
+        // Writes an error message to stderr on failure
         ParcelFileDescriptor fd = openFileForSystem(filename, "w");
         if (fd == null) {
             return -1;
@@ -961,16 +959,16 @@
             String logNameTimeString = LOG_NAME_TIME_FORMATTER.format(localDateTime);
             heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof";
         }
-        pw.println("File: " + heapFile);
-        pw.flush();
 
-        File file = new File(heapFile);
-        file.delete();
+        // Writes an error message to stderr on failure
         ParcelFileDescriptor fd = openFileForSystem(heapFile, "w");
         if (fd == null) {
             return -1;
         }
 
+        pw.println("File: " + heapFile);
+        pw.flush();
+
         final CountDownLatch latch = new CountDownLatch(1);
 
         final RemoteCallback finishCallback = new RemoteCallback(new OnResultListener() {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index dd1ddd7..3ba07af 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -18,7 +18,7 @@
 
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
-import static android.os.PowerWhitelistManager.REASON_DENIED;
+import static android.os.PowerExemptionManager.REASON_DENIED;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,7 +37,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
-import android.os.PowerWhitelistManager;
+import android.os.PowerExemptionManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -153,6 +153,8 @@
     // allow while-in-use permissions in foreground service or not.
     // while-in-use permissions in FGS started from background might be restricted.
     boolean mAllowWhileInUsePermissionInFgs;
+    // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
+    boolean mAllowWhileInUsePermissionInFgsAtEntering;
 
     // the most recent package that start/bind this service.
     String mRecentCallingPackage;
@@ -172,7 +174,9 @@
 
     // allow the service becomes foreground service? Service started from background may not be
     // allowed to become a foreground service.
-    @PowerWhitelistManager.ReasonCode int mAllowStartForeground = REASON_DENIED;
+    @PowerExemptionManager.ReasonCode int mAllowStartForeground = REASON_DENIED;
+    // A copy of mAllowStartForeground's value when the service is entering FGS state.
+    @PowerExemptionManager.ReasonCode int mAllowStartForegroundAtEntering = REASON_DENIED;
     // Debug info why mAllowStartForeground is allowed or denied.
     String mInfoAllowStartForeground;
     // Debug info if mAllowStartForeground is allowed because of a temp-allowlist.
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index ce8cbaf..104bc9b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1029,7 +1029,7 @@
 
             if (isStarted && mInProgressEvents == null) {
                 mInProgressEvents = new ArrayMap<>(1);
-            } else if (mPausedInProgressEvents == null) {
+            } else if (!isStarted && mPausedInProgressEvents == null) {
                 mPausedInProgressEvents = new ArrayMap<>(1);
             }
             ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted
@@ -1231,11 +1231,13 @@
          */
         void onClientDeath(@NonNull IBinder clientId) {
             synchronized (AppOpsService.this) {
-                if (mInProgressEvents == null) {
+                if (mInProgressEvents == null && mPausedInProgressEvents == null) {
                     return;
                 }
 
-                InProgressStartOpEvent deadEvent = mInProgressEvents.get(clientId);
+                ArrayMap<IBinder, InProgressStartOpEvent> events = isPaused()
+                        ? mPausedInProgressEvents : mInProgressEvents;
+                InProgressStartOpEvent deadEvent = events.get(clientId);
                 if (deadEvent != null) {
                     deadEvent.numUnfinishedStarts = 1;
                 }
@@ -1250,14 +1252,18 @@
          * @param newState The new state
          */
         public void onUidStateChanged(@AppOpsManager.UidState int newState) {
-            if (mInProgressEvents == null) {
+            if (mInProgressEvents == null && mPausedInProgressEvents == null) {
                 return;
             }
 
-            int numInProgressEvents = mInProgressEvents.size();
-            List<IBinder> binders = new ArrayList<>(mInProgressEvents.keySet());
+            boolean isRunning = isRunning();
+            ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> events =
+                    isRunning ? mInProgressEvents : mPausedInProgressEvents;
+
+            int numInProgressEvents = events.size();
+            List<IBinder> binders = new ArrayList<>(events.keySet());
             for (int i = 0; i < numInProgressEvents; i++) {
-                InProgressStartOpEvent event = mInProgressEvents.get(binders.get(i));
+                InProgressStartOpEvent event = events.get(binders.get(i));
 
                 if (event != null && event.getUidState() != newState) {
                     try {
@@ -1272,16 +1278,17 @@
                         // Call started() to add a new start event object and then add the
                         // previously removed unfinished start counts back
                         if (proxy != null) {
-                            started(event.getClientId(), proxy.getUid(), proxy.getPackageName(),
-                                    proxy.getAttributionTag(), newState, event.getFlags(), false,
+                            startedOrPaused(event.getClientId(), proxy.getUid(),
+                                    proxy.getPackageName(), proxy.getAttributionTag(), newState,
+                                    event.getFlags(), false, isRunning,
                                     event.getAttributionFlags(), event.getAttributionChainId());
                         } else {
-                            started(event.getClientId(), Process.INVALID_UID, null, null, newState,
-                                    OP_FLAG_SELF, false, event.getAttributionFlags(),
-                                    event.getAttributionChainId());
+                            startedOrPaused(event.getClientId(), Process.INVALID_UID, null, null,
+                                    newState, event.getFlags(), false, isRunning,
+                                    event.getAttributionFlags(), event.getAttributionChainId());
                         }
 
-                        InProgressStartOpEvent newEvent = mInProgressEvents.get(binders.get(i));
+                        InProgressStartOpEvent newEvent = events.get(binders.get(i));
                         if (newEvent != null) {
                             newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
                         }
@@ -4551,15 +4558,15 @@
                 }
 
                 try {
-                    if (mPlatformCompat.isChangeEnabledByPackageName(
+                    if (!mPlatformCompat.isChangeEnabledByPackageName(
                             SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
-                            userId) && mPlatformCompat.isChangeEnabledByUid(
+                            userId) || !mPlatformCompat.isChangeEnabledByUid(
                                     SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE,
-                            callingUid) && !isAttributionTagValid) {
-                        Slog.e(TAG, msg);
-                    } else {
-                        Slog.e(TAG, msg);
+                            callingUid)) {
+                        // Do not override tags if overriding is not enabled for this package
+                        isAttributionTagValid = true;
                     }
+                    Slog.e(TAG, msg);
                 } catch (RemoteException neverHappens) {
                 }
             }
@@ -6494,9 +6501,9 @@
             int numAttrTags = op.mAttributions.size();
             for (int attrNum = 0; attrNum < numAttrTags; attrNum++) {
                 AttributedOp attrOp = op.mAttributions.valueAt(attrNum);
-                if (restricted) {
+                if (restricted && attrOp.isRunning()) {
                     attrOp.pause();
-                } else {
+                } else if (attrOp.isPaused()) {
                     attrOp.resume();
                 }
             }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 13b7ebc..fe5ee78 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -512,6 +512,8 @@
                     }
                 }
             }
+        } else if (phase == PHASE_BOOT_COMPLETED) {
+            mDisplayModeDirector.onBootCompleted();
         }
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 997f0e5..07d13c2 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -155,7 +155,6 @@
         mSettingsObserver.observe();
         mDisplayObserver.observe();
         mBrightnessObserver.observe(sensorManager);
-        mUdfpsObserver.observe();
         synchronized (mLock) {
             // We may have a listener already registered before the call to start, so go ahead and
             // notify them to pick up our newly initialized state.
@@ -163,6 +162,16 @@
         }
     }
 
+    /**
+     * Same as {@link #start(SensorManager)}, but for observers that need to be delayed even more,
+     * for example until SystemUI is ready.
+     */
+    public void onBootCompleted() {
+        // UDFPS observer registers a listener with SystemUI which might not be ready until the
+        // system is fully booted.
+        mUdfpsObserver.observe();
+    }
+
     public void setLoggingEnabled(boolean loggingEnabled) {
         if (mLoggingEnabled == loggingEnabled) {
             return;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c14acd7..1b50f03 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -365,7 +365,7 @@
 
     @Nullable
     private final ColorDisplayServiceInternal mCdsi;
-    private final float[] mNitsRange;
+    private float[] mNitsRange;
 
     private final HighBrightnessModeController mHbmController;
 
@@ -447,6 +447,7 @@
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
         mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+        mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
         mHandler = new DisplayControllerHandler(handler.getLooper());
 
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
@@ -462,6 +463,7 @@
         mBlanker = blanker;
         mContext = context;
         mBrightnessTracker = brightnessTracker;
+        // TODO: b/186428377 update brightness setting when display changes
         mBrightnessSetting = brightnessSetting;
         mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
 
@@ -498,13 +500,10 @@
         mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
 
+        mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
+                .getDisplayDeviceConfig();
 
-        mDisplayDeviceConfig = logicalDisplay
-                .getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
-        mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease();
-        mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease();
-        mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease();
-        mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease();
+        loadBrightnessRampRates();
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
 
@@ -513,66 +512,7 @@
         // Seed the cached brightness
         saveBrightnessInfo(getScreenBrightnessSetting());
 
-        if (mUseSoftwareAutoBrightnessConfig) {
-            final float dozeScaleFactor = resources.getFraction(
-                    com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
-                    1, 1);
-
-            int[] ambientBrighteningThresholds = resources.getIntArray(
-                    com.android.internal.R.array.config_ambientBrighteningThresholds);
-            int[] ambientDarkeningThresholds = resources.getIntArray(
-                    com.android.internal.R.array.config_ambientDarkeningThresholds);
-            int[] ambientThresholdLevels = resources.getIntArray(
-                    com.android.internal.R.array.config_ambientThresholdLevels);
-            HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels(
-                    ambientBrighteningThresholds, ambientDarkeningThresholds,
-                    ambientThresholdLevels);
-
-            int[] screenBrighteningThresholds = resources.getIntArray(
-                    com.android.internal.R.array.config_screenBrighteningThresholds);
-            int[] screenDarkeningThresholds = resources.getIntArray(
-                    com.android.internal.R.array.config_screenDarkeningThresholds);
-            int[] screenThresholdLevels = resources.getIntArray(
-                    com.android.internal.R.array.config_screenThresholdLevels);
-            HysteresisLevels screenBrightnessThresholds = new HysteresisLevels(
-                    screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels);
-
-            long brighteningLightDebounce = resources.getInteger(
-                    com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
-            long darkeningLightDebounce = resources.getInteger(
-                    com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
-            boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
-                    com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
-
-            int lightSensorWarmUpTimeConfig = resources.getInteger(
-                    com.android.internal.R.integer.config_lightSensorWarmupTime);
-            int lightSensorRate = resources.getInteger(
-                    com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
-            int initialLightSensorRate = resources.getInteger(
-                    com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
-            if (initialLightSensorRate == -1) {
-                initialLightSensorRate = lightSensorRate;
-            } else if (initialLightSensorRate > lightSensorRate) {
-                Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
-                        + initialLightSensorRate + ") to be less than or equal to "
-                        + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
-            }
-
-            loadAmbientLightSensor();
-
-            mBrightnessMapper = BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig);
-            if (mBrightnessMapper != null) {
-                mAutomaticBrightnessController = new AutomaticBrightnessController(this,
-                        handler.getLooper(), sensorManager, mLightSensor, mBrightnessMapper,
-                        lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
-                        PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
-                        initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
-                        autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                        screenBrightnessThresholds, logicalDisplay, context, mHbmController);
-            } else {
-                mUseSoftwareAutoBrightnessConfig = false;
-            }
-        }
+        setUpAutoBrightness(resources, handler);
 
         mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
         mColorFadeFadesConfig = resources.getBoolean(
@@ -610,13 +550,8 @@
         mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
         mDisplayWhiteBalanceController = displayWhiteBalanceController;
 
-        if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
-            mNitsRange = mDisplayDeviceConfig.getNits();
-        } else {
-            Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
-            mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources()
-                    .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
-        }
+        loadNitsRange(resources);
+
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
             mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
             boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
@@ -802,10 +737,7 @@
             mDisplayDevice = device;
             mUniqueDisplayId = uniqueId;
             mDisplayDeviceConfig = config;
-
-            loadAmbientLightSensor();
-            loadProximitySensor();
-            mHbmController.resetHbmData(token, config.getHighBrightnessModeData());
+            loadFromDisplayDeviceConfig(token);
         });
     }
 
@@ -846,6 +778,18 @@
         }
     }
 
+    private void loadFromDisplayDeviceConfig(IBinder token) {
+        // All properties that depend on the associated DisplayDevice and the DDC must be
+        // updated here.
+        loadAmbientLightSensor();
+        loadBrightnessRampRates();
+        loadProximitySensor();
+        loadNitsRange(mContext.getResources());
+        setUpAutoBrightness(mContext.getResources(), mHandler);
+        reloadReduceBrightColours();
+        mHbmController.resetHbmData(token, mDisplayDeviceConfig.getHighBrightnessModeData());
+    }
+
     private void sendUpdatePowerState() {
         synchronized (mLock) {
             sendUpdatePowerStateLocked();
@@ -903,6 +847,98 @@
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
     }
 
+    private void setUpAutoBrightness(Resources resources, Handler handler) {
+        if (!mUseSoftwareAutoBrightnessConfig) {
+            return;
+        }
+
+        mBrightnessMapper = BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig);
+
+        if (mBrightnessMapper != null) {
+            final float dozeScaleFactor = resources.getFraction(
+                    com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
+                    1, 1);
+
+            int[] ambientBrighteningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_ambientBrighteningThresholds);
+            int[] ambientDarkeningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_ambientDarkeningThresholds);
+            int[] ambientThresholdLevels = resources.getIntArray(
+                    com.android.internal.R.array.config_ambientThresholdLevels);
+            HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels(
+                    ambientBrighteningThresholds, ambientDarkeningThresholds,
+                    ambientThresholdLevels);
+
+            int[] screenBrighteningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_screenBrighteningThresholds);
+            int[] screenDarkeningThresholds = resources.getIntArray(
+                    com.android.internal.R.array.config_screenDarkeningThresholds);
+            int[] screenThresholdLevels = resources.getIntArray(
+                    com.android.internal.R.array.config_screenThresholdLevels);
+            HysteresisLevels screenBrightnessThresholds = new HysteresisLevels(
+                    screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels);
+
+            long brighteningLightDebounce = resources.getInteger(
+                    com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
+            long darkeningLightDebounce = resources.getInteger(
+                    com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
+            boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+                    com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+
+            int lightSensorWarmUpTimeConfig = resources.getInteger(
+                    com.android.internal.R.integer.config_lightSensorWarmupTime);
+            int lightSensorRate = resources.getInteger(
+                    com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
+            int initialLightSensorRate = resources.getInteger(
+                    com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
+            if (initialLightSensorRate == -1) {
+                initialLightSensorRate = lightSensorRate;
+            } else if (initialLightSensorRate > lightSensorRate) {
+                Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
+                        + initialLightSensorRate + ") to be less than or equal to "
+                        + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
+            }
+
+            loadAmbientLightSensor();
+
+            if (mAutomaticBrightnessController != null) {
+                mAutomaticBrightnessController.stop();
+            }
+            mAutomaticBrightnessController = new AutomaticBrightnessController(this,
+                    handler.getLooper(), mSensorManager, mLightSensor, mBrightnessMapper,
+                    lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
+                    PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
+                    initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
+                    autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
+                    screenBrightnessThresholds, mLogicalDisplay, mContext, mHbmController);
+        } else {
+            mUseSoftwareAutoBrightnessConfig = false;
+        }
+    }
+
+    private void loadBrightnessRampRates() {
+        mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease();
+        mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease();
+        mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease();
+        mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease();
+    }
+
+    private void loadNitsRange(Resources resources) {
+        if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
+            mNitsRange = mDisplayDeviceConfig.getNits();
+        } else {
+            Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
+            mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
+                    .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
+        }
+    }
+
+    private void reloadReduceBrightColours() {
+        if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) {
+            applyReduceBrightColorsSplineAdjustment();
+        }
+    }
+
     private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
         @Override
         public void onAnimationStart(Animator animation) {
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 66b23c4..e6d25ec 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -33,7 +33,6 @@
 import android.location.LocationRequest;
 import android.location.provider.ProviderRequest;
 import android.location.util.identity.CallerIdentity;
-import android.os.Build;
 import android.os.PowerManager.LocationPowerSaveMode;
 import android.os.SystemClock;
 import android.util.ArrayMap;
@@ -47,8 +46,8 @@
     public static final LocationEventLog EVENT_LOG = new LocationEventLog();
 
     private static int getLogSize() {
-        if (Build.IS_DEBUGGABLE || D) {
-            return 500;
+        if (D) {
+            return 600;
         } else {
             return 200;
         }
@@ -152,7 +151,7 @@
 
     /** Logs a client for a location provider entering the foreground state. */
     public void logProviderClientForeground(String provider, CallerIdentity identity) {
-        if (Build.IS_DEBUGGABLE || D) {
+        if (D) {
             addLogEvent(EVENT_PROVIDER_CLIENT_FOREGROUND, provider, identity);
         }
         getAggregateStats(provider, identity).markRequestForeground();
@@ -160,7 +159,7 @@
 
     /** Logs a client for a location provider leaving the foreground state. */
     public void logProviderClientBackground(String provider, CallerIdentity identity) {
-        if (Build.IS_DEBUGGABLE || D) {
+        if (D) {
             addLogEvent(EVENT_PROVIDER_CLIENT_BACKGROUND, provider, identity);
         }
         getAggregateStats(provider, identity).markRequestBackground();
@@ -168,14 +167,14 @@
 
     /** Logs a client for a location provider entering the permitted state. */
     public void logProviderClientPermitted(String provider, CallerIdentity identity) {
-        if (Build.IS_DEBUGGABLE || D) {
+        if (D) {
             addLogEvent(EVENT_PROVIDER_CLIENT_PERMITTED, provider, identity);
         }
     }
 
     /** Logs a client for a location provider leaving the permitted state. */
     public void logProviderClientUnpermitted(String provider, CallerIdentity identity) {
-        if (Build.IS_DEBUGGABLE || D) {
+        if (D) {
             addLogEvent(EVENT_PROVIDER_CLIENT_UNPERMITTED, provider, identity);
         }
     }
@@ -187,7 +186,7 @@
 
     /** Logs a new incoming location for a location provider. */
     public void logProviderReceivedLocations(String provider, int numLocations) {
-        if (Build.IS_DEBUGGABLE || D) {
+        if (D) {
             addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
         }
     }
@@ -195,7 +194,7 @@
     /** Logs a location deliver for a client of a location provider. */
     public void logProviderDeliveredLocations(String provider, int numLocations,
             CallerIdentity identity) {
-        if (Build.IS_DEBUGGABLE || D) {
+        if (D) {
             addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
         }
         getAggregateStats(provider, identity).markLocationDelivered();
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 2b6a838..14f6fb3 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -312,6 +312,12 @@
             }
         }
 
+        /** Returns {@code true} if the incoming activity can belong to this transition. */
+        boolean canCoalesce(ActivityRecord r) {
+            return mLastLaunchedActivity.mDisplayContent == r.mDisplayContent
+                    && mLastLaunchedActivity.getWindowingMode() == r.getWindowingMode();
+        }
+
         /** @return {@code true} if the activity matches a launched activity in this transition. */
         boolean contains(ActivityRecord r) {
             return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r));
@@ -604,8 +610,7 @@
             return;
         }
 
-        final DisplayContent targetDisplay = launchedActivity.mDisplayContent;
-        if (info != null && info.mLastLaunchedActivity.mDisplayContent == targetDisplay) {
+        if (info != null && info.canCoalesce(launchedActivity)) {
             // If we are already in an existing transition on the same display, only update the
             // activity name, but not the other attributes.
 
@@ -633,7 +638,7 @@
             // As abort for no process switch.
             launchObserverNotifyIntentFailed();
         }
-        if (targetDisplay.isSleeping()) {
+        if (launchedActivity.mDisplayContent.isSleeping()) {
             // It is unknown whether the activity can be drawn or not, e.g. ut depends on the
             // keyguard states and the attributes or flags set by the activity. If the activity
             // keeps invisible in the grace period, the tracker will be cancelled so it won't get
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index fb2db22..918979c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -854,7 +854,7 @@
                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 110.f}, 0);
         verify(mStatusBarMock, never()).setUdfpsHbmListener(any());
 
-        director.start(createMockSensorManager());
+        director.onBootCompleted();
         verify(mStatusBarMock).setUdfpsHbmListener(eq(director.getUdpfsObserver()));
     }
 
@@ -863,6 +863,7 @@
         DisplayModeDirector director =
                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 110.f}, 0);
         director.start(createMockSensorManager());
+        director.onBootCompleted();
         ArgumentCaptor<IUdfpsHbmListener> captor =
                 ArgumentCaptor.forClass(IUdfpsHbmListener.class);
         verify(mStatusBarMock).setUdfpsHbmListener(captor.capture());
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 82c459c..38466eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -38,6 +38,7 @@
 import android.app.ActivityOptions;
 import android.app.ActivityOptions.SourceInfo;
 import android.app.WaitResult;
+import android.app.WindowConfiguration;
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.SystemClock;
@@ -476,6 +477,18 @@
         transitToDrawnAndVerifyOnLaunchFinished(activityOnNewDisplay);
     }
 
+    @Test
+    public void testConsecutiveLaunchWithDifferentWindowingMode() {
+        mTopActivity.setWindowingMode(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+        onActivityLaunched(mTrampolineActivity);
+        mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
+                mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
+        notifyActivityLaunched(START_SUCCESS, mTopActivity);
+        // Different windowing modes should be independent launch events.
+        transitToDrawnAndVerifyOnLaunchFinished(mTrampolineActivity);
+        transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
+    }
+
     private void transitToDrawnAndVerifyOnLaunchFinished(ActivityRecord activity) {
         notifyTransitionStarting(activity);
         notifyWindowsDrawn(activity);