Merge "Add logic to clean up resources more frequently" 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/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/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/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/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/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/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/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/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 4a7534d..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) {
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/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/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/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);