Merge "Fix issue where PIP jumps after being tapped after being dragged through the dismiss target." into rvc-dev
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 3d4154a2..0b760a6 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -154,10 +154,10 @@
         }
     }
 
-    void removeInvalidCommitters(SparseArray<String> packages) {
+    void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) {
         synchronized (mMetadataLock) {
             mCommitters.removeIf(committer ->
-                    !committer.packageName.equals(packages.get(committer.uid)));
+                    !committer.packageName.equals(knownPackages.get(committer.uid)));
         }
     }
 
@@ -200,16 +200,27 @@
         }
     }
 
-    void removeInvalidLeasees(SparseArray<String> packages) {
+    void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) {
         synchronized (mMetadataLock) {
             mLeasees.removeIf(leasee ->
-                    !leasee.packageName.equals(packages.get(leasee.uid)));
+                    !leasee.packageName.equals(knownPackages.get(leasee.uid)));
         }
     }
 
-    boolean hasLeases() {
+    void removeExpiredLeases() {
         synchronized (mMetadataLock) {
-            return !mLeasees.isEmpty();
+            mLeasees.removeIf(leasee -> !leasee.isStillValid());
+        }
+    }
+
+    boolean hasValidLeases() {
+        synchronized (mMetadataLock) {
+            for (int i = 0, size = mLeasees.size(); i < size; ++i) {
+                if (mLeasees.valueAt(i).isStillValid()) {
+                    return true;
+                }
+            }
+            return false;
         }
     }
 
@@ -226,8 +237,7 @@
             // Check if packageName already holds a lease on the blob.
             for (int i = 0, size = mLeasees.size(); i < size; ++i) {
                 final Leasee leasee = mLeasees.valueAt(i);
-                if (leasee.equals(callingPackage, callingUid)
-                        && leasee.isStillValid()) {
+                if (leasee.isStillValid() && leasee.equals(callingPackage, callingUid)) {
                     return true;
                 }
             }
@@ -259,25 +269,32 @@
 
     boolean isALeasee(@Nullable String packageName, int uid) {
         synchronized (mMetadataLock) {
-            return isAnAccessor(mLeasees, packageName, uid);
+            final Leasee leasee = getAccessor(mLeasees, packageName, uid);
+            return leasee != null && leasee.isStillValid();
         }
     }
 
     private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors,
             @Nullable String packageName, int uid) {
         // Check if the package is an accessor of the data blob.
+        return getAccessor(accessors, packageName, uid) != null;
+    }
+
+    private static <T extends Accessor> T getAccessor(@NonNull ArraySet<T> accessors,
+            @Nullable String packageName, int uid) {
+        // Check if the package is an accessor of the data blob.
         for (int i = 0, size = accessors.size(); i < size; ++i) {
             final Accessor accessor = accessors.valueAt(i);
             if (packageName != null && uid != INVALID_UID
                     && accessor.equals(packageName, uid)) {
-                return true;
+                return (T) accessor;
             } else if (packageName != null && accessor.packageName.equals(packageName)) {
-                return true;
+                return (T) accessor;
             } else if (uid != INVALID_UID && accessor.uid == uid) {
-                return true;
+                return (T) accessor;
             }
         }
-        return false;
+        return null;
     }
 
     boolean isALeasee(@NonNull String packageName) {
@@ -298,11 +315,11 @@
 
     private boolean hasOtherLeasees(@Nullable String packageName, int uid) {
         synchronized (mMetadataLock) {
-            if (mCommitters.size() > 1 || mLeasees.size() > 1) {
-                return true;
-            }
             for (int i = 0, size = mLeasees.size(); i < size; ++i) {
                 final Leasee leasee = mLeasees.valueAt(i);
+                if (!leasee.isStillValid()) {
+                    continue;
+                }
                 // TODO: Also exclude packages which are signed with same cert?
                 if (packageName != null && uid != INVALID_UID
                         && !leasee.equals(packageName, uid)) {
@@ -322,6 +339,9 @@
         synchronized (mMetadataLock) {
             for (int i = 0, size = mLeasees.size(); i < size; ++i) {
                 final Leasee leasee = mLeasees.valueAt(i);
+                if (!leasee.isStillValid()) {
+                    continue;
+                }
                 if (leasee.uid == uid && leasee.packageName.equals(packageName)) {
                     final int descriptionResId = leasee.descriptionResEntryName == null
                             ? Resources.ID_NULL
@@ -426,7 +446,7 @@
 
         // Blobs with no active leases
         if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsedForAll())
-                && !hasLeases()) {
+                && !hasValidLeases()) {
             return true;
         }
 
@@ -715,7 +735,7 @@
         }
 
         boolean isStillValid() {
-            return expiryTimeMillis == 0 || expiryTimeMillis <= System.currentTimeMillis();
+            return expiryTimeMillis == 0 || expiryTimeMillis >= System.currentTimeMillis();
         }
 
         void dump(@NonNull Context context, @NonNull IndentingPrintWriter fout) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index f7468d8..d9c0e47 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -539,7 +539,7 @@
                 Slog.v(TAG, "Released lease on " + blobHandle
                         + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
             }
-            if (!blobMetadata.hasLeases()) {
+            if (!blobMetadata.hasValidLeases()) {
                 mHandler.postDelayed(() -> {
                     synchronized (mBlobsLock) {
                         // Check if blobMetadata object is still valid. If it is not, then
@@ -583,6 +583,9 @@
             getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
                 final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
                 blobMetadata.forEachLeasee(leasee -> {
+                    if (!leasee.isStillValid()) {
+                        return;
+                    }
                     final int descriptionResId = leasee.descriptionResEntryName == null
                             ? Resources.ID_NULL
                             : getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
@@ -922,8 +925,8 @@
                         blobMetadata.getBlobFile().delete();
                     } else {
                         addBlobForUserLocked(blobMetadata, blobMetadata.getUserId());
-                        blobMetadata.removeInvalidCommitters(userPackages);
-                        blobMetadata.removeInvalidLeasees(userPackages);
+                        blobMetadata.removeCommittersFromUnknownPkgs(userPackages);
+                        blobMetadata.removeLeaseesFromUnknownPkgs(userPackages);
                     }
                     mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
                 }
@@ -1110,6 +1113,9 @@
             userBlobs.entrySet().removeIf(entry -> {
                 final BlobMetadata blobMetadata = entry.getValue();
 
+                // Remove expired leases
+                blobMetadata.removeExpiredLeases();
+
                 if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
                     deleteBlobLocked(blobMetadata);
                     deletedBlobIds.add(blobMetadata.getBlobId());
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
index 240222e..6108a32 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -33,7 +33,6 @@
 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
 import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import com.android.internal.os.statsd.StatsConfigUtils;
 import com.android.internal.os.statsd.protos.TestAtoms;
 import com.android.os.AtomsProto.Atom;
 
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
similarity index 98%
rename from apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
rename to apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
index d0d1400..b5afb94 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.internal.os.statsd;
+package com.android.internal.os.statsd.libstats;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ca03343..02c0763 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -9694,6 +9694,7 @@
         UNIFORM = 1;
         RARELY_USED = 2;
         BOOT_TIME_SAMPLING = 3;
+        UNIFORM_OPS = 4;
     }
 
     // sampling strategy used to collect this message
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 9bf2e01..91e75911 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -427,8 +427,7 @@
         if (copySources) {
             for (int i = 0; i < SIZE; i++) {
                 InsetsSource source = other.mSources[i];
-                if (source == null) continue;
-                mSources[i] = new InsetsSource(source);
+                mSources[i] = source != null ? new InsetsSource(source) : null;
             }
         } else {
             for (int i = 0; i < SIZE; i++) {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 5efd46c..de5ab6f 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -561,7 +561,7 @@
                 mChooserTargetScores.put(componentName, new HashMap<>());
             }
             mChooserTargetScores.get(componentName).put(shortcutInfo.getShortLabel().toString(),
-                    shortcutInfo.getRank());
+                    target.getRank());
         }
         mChooserTargetScores.keySet().forEach(key -> rankTargetsWithinComponent(key));
     }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 86c13a0..fba4675a 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -20,9 +20,6 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.PermissionChecker.PID_UNKNOWN;
 
-import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
-import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_WORK;
-
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.UiThread;
@@ -159,6 +156,9 @@
     protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
     protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser";
 
+    /** Tracks if we should ignore future broadcasts telling us the work profile is enabled */
+    private boolean mWorkProfileHasBeenEnabled = false;
+
     @VisibleForTesting
     public static boolean ENABLE_TABBED_VIEW = true;
     private static final String TAB_TAG_PERSONAL = "personal";
@@ -825,12 +825,23 @@
         if (shouldShowTabs()) {
             mWorkProfileStateReceiver = createWorkProfileStateReceiver();
             registerWorkProfileStateReceiver();
+
+            mWorkProfileHasBeenEnabled = isWorkProfileEnabled();
         }
     }
 
+    private boolean isWorkProfileEnabled() {
+        UserHandle workUserHandle = getWorkProfileUserHandle();
+        UserManager userManager = getSystemService(UserManager.class);
+
+        return !userManager.isQuietModeEnabled(workUserHandle)
+                && userManager.isUserUnlocked(workUserHandle);
+    }
+
     private void registerWorkProfileStateReceiver() {
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
         registerReceiverAsUser(mWorkProfileStateReceiver, UserHandle.ALL, filter, null, null);
     }
@@ -1961,17 +1972,29 @@
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 if (!TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
-                        && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
+                        && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+                        && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_AVAILABLE)) {
                     return;
                 }
-                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-                if (TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
-                        && userHandle != getWorkProfileUserHandle().getIdentifier()) {
+
+                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+
+                if (userId != getWorkProfileUserHandle().getIdentifier()) {
                     return;
                 }
-                if (TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)) {
+
+                if (isWorkProfileEnabled()) {
+                    if (mWorkProfileHasBeenEnabled) {
+                        return;
+                    }
+
+                    mWorkProfileHasBeenEnabled = true;
                     mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
+                } else {
+                    // Must be an UNAVAILABLE broadcast, so we watch for the next availability
+                    mWorkProfileHasBeenEnabled = false;
                 }
+
                 if (mMultiProfilePagerAdapter.getCurrentUserHandle()
                         .equals(getWorkProfileUserHandle())) {
                     mMultiProfilePagerAdapter.rebuildActiveTab(true);
diff --git a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
index c72707db..1533377 100644
--- a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
@@ -58,6 +58,8 @@
     private Handler mHandler;
     private Executor mExecutor;
     private BugreportManager mBrm;
+    private File mBugreportFile;
+    private File mScreenshotFile;
     private ParcelFileDescriptor mBugreportFd;
     private ParcelFileDescriptor mScreenshotFd;
 
@@ -73,8 +75,10 @@
         };
 
         mBrm = getBugreportManager();
-        mBugreportFd = parcelFd("bugreport_" + name.getMethodName(), ".zip");
-        mScreenshotFd = parcelFd("screenshot_" + name.getMethodName(), ".png");
+        mBugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+        mScreenshotFile = createTempFile("screenshot_" + name.getMethodName(), ".png");
+        mBugreportFd = parcelFd(mBugreportFile);
+        mScreenshotFd = parcelFd(mScreenshotFile);
 
         getPermissions();
     }
@@ -121,6 +125,21 @@
     }
 
     @Test
+    public void normalFlow_full() throws Exception {
+        BugreportCallbackImpl callback = new BugreportCallbackImpl();
+        mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
+
+        waitTillDoneOrTimeout(callback);
+        assertThat(callback.isDone()).isTrue();
+        assertThat(callback.getErrorCode()).isEqualTo(
+                BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+        // bugreport and screenshot files should be empty when user consent timed out.
+        assertThat(mBugreportFile.length()).isEqualTo(0);
+        assertThat(mScreenshotFile.length()).isEqualTo(0);
+        assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+    }
+
+    @Test
     public void simultaneousBugreportsNotAllowed() throws Exception {
         // Start bugreport #1
         BugreportCallbackImpl callback = new BugreportCallbackImpl();
@@ -129,9 +148,10 @@
         // Before #1 is done, try to start #2.
         assertThat(callback.isDone()).isFalse();
         BugreportCallbackImpl callback2 = new BugreportCallbackImpl();
-        ParcelFileDescriptor bugreportFd2 = parcelFd("bugreport_2_" + name.getMethodName(), ".zip");
-        ParcelFileDescriptor screenshotFd2 =
-                parcelFd("screenshot_2_" + name.getMethodName(), ".png");
+        File bugreportFile2 = createTempFile("bugreport_2_" + name.getMethodName(), ".zip");
+        File screenshotFile2 = createTempFile("screenshot_2_" + name.getMethodName(), ".png");
+        ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2);
+        ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2);
         mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
         Thread.sleep(500 /* .5s */);
 
@@ -271,12 +291,16 @@
         return bm;
     }
 
-    private static ParcelFileDescriptor parcelFd(String prefix, String extension) throws Exception {
-        File f = File.createTempFile(prefix, extension);
+    private static File createTempFile(String prefix, String extension) throws Exception {
+        final File f = File.createTempFile(prefix, extension);
         f.setReadable(true, true);
         f.setWritable(true, true);
+        f.deleteOnExit();
+        return f;
+    }
 
-        return ParcelFileDescriptor.open(f,
+    private static ParcelFileDescriptor parcelFd(File file) throws Exception {
+        return ParcelFileDescriptor.open(file,
                 ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
     }
 
@@ -342,4 +366,13 @@
     private static BugreportParams interactive() {
         return new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE);
     }
+
+    /*
+     * Returns a {@link BugreportParams} for full bugreport that includes a screenshot.
+     *
+     * <p> This can take on the order of minutes to finish
+     */
+    private static BugreportParams full() {
+        return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 7115acf..5260ef8 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -304,6 +304,7 @@
         mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
         mState.getSource(ITYPE_IME).setVisible(true);
         mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(0, 0, 100, 100));
         mState2.set(mState, true);
         assertEquals(mState, mState2);
     }
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 0af74c4..0e45e43 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -22,6 +22,10 @@
     android:layout_height="match_parent"
     android:background="@color/notification_shade_background_color">
 
+    <com.android.car.ui.FocusParkingView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
     <View
         android:id="@+id/glass_pane"
         android:layout_width="match_parent"
@@ -33,16 +37,20 @@
         app:layout_constraintTop_toTopOf="parent"
     />
 
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/notifications"
+    <com.android.car.ui.FocusArea
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:orientation="vertical"
-        android:paddingBottom="@dimen/notification_shade_list_padding_bottom"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"/>
+        app:layout_constraintTop_toTopOf="parent">
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/notifications"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingBottom="@dimen/notification_shade_list_padding_bottom"/>
+    </com.android.car.ui.FocusArea>
 
     <include layout="@layout/notification_handle_bar"/>
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5b61551..a7ef5e6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -329,6 +329,7 @@
 
         <activity android:name=".screenrecord.ScreenRecordDialog"
             android:theme="@style/ScreenRecord"
+            android:showForAllUsers="true"
             android:excludeFromRecents="true" />
         <service android:name=".screenrecord.RecordingService" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index b34312e2..2bfe015 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -506,6 +506,14 @@
         }
     }
 
+    @Nullable ActivityView getActivityView() {
+        return mActivityView;
+    }
+
+    int getTaskId() {
+        return mTaskId;
+    }
+
     /**
      * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
      * This should be done post-move and post-animation.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 09e8799..1f3d981 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -31,6 +31,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
+import android.app.ActivityView;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -293,11 +294,42 @@
     /** Description of current animation controller state. */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Stack view state:");
-        pw.print("  gestureInProgress:    "); pw.println(mIsGestureInProgress);
-        pw.print("  showingDismiss:       "); pw.println(mShowingDismiss);
-        pw.print("  isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
+        pw.print("  gestureInProgress:       "); pw.println(mIsGestureInProgress);
+        pw.print("  showingDismiss:          "); pw.println(mShowingDismiss);
+        pw.print("  isExpansionAnimating:    "); pw.println(mIsExpansionAnimating);
+        pw.print("  expandedContainerVis:    "); pw.println(mExpandedViewContainer.getVisibility());
+        pw.print("  expandedContainerAlpha:  "); pw.println(mExpandedViewContainer.getAlpha());
+        pw.print("  expandedContainerMatrix: ");
+        pw.println(mExpandedViewContainer.getAnimationMatrix());
+
         mStackAnimationController.dump(fd, pw, args);
         mExpandedAnimationController.dump(fd, pw, args);
+
+        if (mExpandedBubble != null) {
+            pw.println("Expanded bubble state:");
+            pw.println("  expandedBubbleKey: " + mExpandedBubble.getKey());
+
+            final BubbleExpandedView expandedView = mExpandedBubble.getExpandedView();
+
+            if (expandedView != null) {
+                pw.println("  expandedViewVis:    " + expandedView.getVisibility());
+                pw.println("  expandedViewAlpha:  " + expandedView.getAlpha());
+                pw.println("  expandedViewTaskId: " + expandedView.getTaskId());
+
+                final ActivityView av = expandedView.getActivityView();
+
+                if (av != null) {
+                    pw.println("  activityViewVis:    " + av.getVisibility());
+                    pw.println("  activityViewAlpha:  " + av.getAlpha());
+                } else {
+                    pw.println("  activityView is null");
+                }
+            } else {
+                pw.println("Expanded bubble view state: expanded bubble view is null");
+            }
+        } else {
+            pw.println("Expanded bubble state: expanded bubble is null");
+        }
     }
 
     private BubbleController.BubbleExpandListener mExpandListener;
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 32ef063..0c34b27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -22,13 +22,13 @@
 import android.util.Log;
 import android.widget.Switch;
 
-import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import javax.inject.Inject;
 
@@ -39,19 +39,17 @@
         implements RecordingController.RecordingStateChangeCallback {
     private static final String TAG = "ScreenRecordTile";
     private RecordingController mController;
-    private ActivityStarter mActivityStarter;
+    private KeyguardDismissUtil mKeyguardDismissUtil;
     private long mMillisUntilFinished = 0;
     private Callback mCallback = new Callback();
-    private UiEventLogger mUiEventLogger;
 
     @Inject
     public ScreenRecordTile(QSHost host, RecordingController controller,
-            ActivityStarter activityStarter, UiEventLogger uiEventLogger) {
+            KeyguardDismissUtil keyguardDismissUtil) {
         super(host);
         mController = controller;
         mController.observe(this, mCallback);
-        mActivityStarter = activityStarter;
-        mUiEventLogger = uiEventLogger;
+        mKeyguardDismissUtil = keyguardDismissUtil;
     }
 
     @Override
@@ -69,7 +67,7 @@
         } else if (mController.isRecording()) {
             stopRecording();
         } else {
-            startCountdown();
+            mUiHandler.post(() -> showPrompt());
         }
         refreshState();
     }
@@ -114,11 +112,15 @@
         return mContext.getString(R.string.quick_settings_screen_record_label);
     }
 
-    private void startCountdown() {
-        // Close QS, otherwise the permission dialog appears beneath it
+    private void showPrompt() {
+        // Close QS, otherwise the dialog appears beneath it
         getHost().collapsePanels();
         Intent intent = mController.getPromptIntent();
-        mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+        ActivityStarter.OnDismissAction dismissAction = () -> {
+            mContext.startActivity(intent);
+            return false;
+        };
+        mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false);
     }
 
     private void cancelCountdown() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index b253635..82ac1f6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -17,12 +17,17 @@
 package com.android.systemui.screenrecord;
 
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.CountDownTimer;
+import android.os.UserHandle;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.statusbar.policy.CallbackController;
 
 import java.util.ArrayList;
@@ -41,21 +46,30 @@
     private static final String SYSUI_SCREENRECORD_LAUNCHER =
             "com.android.systemui.screenrecord.ScreenRecordDialog";
 
-    private final Context mContext;
     private boolean mIsStarting;
     private boolean mIsRecording;
     private PendingIntent mStopIntent;
     private CountDownTimer mCountDownTimer = null;
+    private BroadcastDispatcher mBroadcastDispatcher;
 
     private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>();
 
+    @VisibleForTesting
+    protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (mStopIntent != null) {
+                stopRecording();
+            }
+        }
+    };
+
     /**
      * Create a new RecordingController
-     * @param context Context for the controller
      */
     @Inject
-    public RecordingController(Context context) {
-        mContext = context;
+    public RecordingController(BroadcastDispatcher broadcastDispatcher) {
+        mBroadcastDispatcher = broadcastDispatcher;
     }
 
     /**
@@ -99,6 +113,9 @@
                 }
                 try {
                     startIntent.send();
+                    IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+                    mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null,
+                            UserHandle.ALL);
                     Log.d(TAG, "sent start intent");
                 } catch (PendingIntent.CanceledException e) {
                     Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
@@ -146,11 +163,16 @@
      */
     public void stopRecording() {
         try {
-            mStopIntent.send();
+            if (mStopIntent != null) {
+                mStopIntent.send();
+            } else {
+                Log.e(TAG, "Stop intent was null");
+            }
             updateState(false);
         } catch (PendingIntent.CanceledException e) {
             Log.e(TAG, "Error stopping: " + e.getMessage());
         }
+        mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 8759726..476ec79 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -32,6 +32,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 import android.widget.Toast;
@@ -40,6 +41,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.LongRunning;
+import com.android.systemui.settings.CurrentUserContextTracker;
 
 import java.io.IOException;
 import java.util.concurrent.Executor;
@@ -58,7 +60,6 @@
     private static final String TAG = "RecordingService";
     private static final String CHANNEL_ID = "screen_record";
     private static final String EXTRA_RESULT_CODE = "extra_resultCode";
-    private static final String EXTRA_DATA = "extra_data";
     private static final String EXTRA_PATH = "extra_path";
     private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
     private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
@@ -79,14 +80,17 @@
     private final Executor mLongExecutor;
     private final UiEventLogger mUiEventLogger;
     private final NotificationManager mNotificationManager;
+    private final CurrentUserContextTracker mUserContextTracker;
 
     @Inject
     public RecordingService(RecordingController controller, @LongRunning Executor executor,
-            UiEventLogger uiEventLogger, NotificationManager notificationManager) {
+            UiEventLogger uiEventLogger, NotificationManager notificationManager,
+            CurrentUserContextTracker userContextTracker) {
         mController = controller;
         mLongExecutor = executor;
         mUiEventLogger = uiEventLogger;
         mNotificationManager = notificationManager;
+        mUserContextTracker = userContextTracker;
     }
 
     /**
@@ -95,8 +99,6 @@
      * @param context    Context from the requesting activity
      * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int, int,
      *                   android.content.Intent)}
-     * @param data       The data from {@link android.app.Activity#onActivityResult(int, int,
-     *                   android.content.Intent)}
      * @param audioSource   The ordinal value of the audio source
      *                      {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource}
      * @param showTaps   True to make touches visible while recording
@@ -118,6 +120,8 @@
         String action = intent.getAction();
         Log.d(TAG, "onStartCommand " + action);
 
+        int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId();
+        UserHandle currentUser = new UserHandle(mCurrentUserId);
         switch (action) {
             case ACTION_START:
                 mAudioSource = ScreenRecordingAudioSource
@@ -132,8 +136,8 @@
                 setTapsVisible(mShowTaps);
 
                 mRecorder = new ScreenMediaRecorder(
-                        getApplicationContext(),
-                        getUserId(),
+                        mUserContextTracker.getCurrentUserContext(),
+                        mCurrentUserId,
                         mAudioSource,
                         this
                 );
@@ -148,7 +152,14 @@
                 } else {
                     mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_END_QS_TILE);
                 }
-                stopRecording();
+                // Check user ID - we may be getting a stop intent after user switch, in which case
+                // we want to post the notifications for that user, which is NOT current user
+                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                if (userId == -1) {
+                    userId = mUserContextTracker.getCurrentUserContext().getUserId();
+                }
+                Log.d(TAG, "notifying for user " + userId);
+                stopRecording(userId);
                 mNotificationManager.cancel(NOTIFICATION_RECORDING_ID);
                 stopSelf();
                 break;
@@ -165,7 +176,7 @@
                 sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
 
                 // Remove notification
-                mNotificationManager.cancel(NOTIFICATION_VIEW_ID);
+                mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
 
                 startActivity(Intent.createChooser(shareIntent, shareLabel)
                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
@@ -184,7 +195,7 @@
                         Toast.LENGTH_LONG).show();
 
                 // Remove notification
-                mNotificationManager.cancel(NOTIFICATION_VIEW_ID);
+                mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
                 Log.d(TAG, "Deleted recording " + uri);
                 break;
         }
@@ -215,11 +226,12 @@
             mController.updateState(true);
             createRecordingNotification();
             mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
-        } catch (IOException | RemoteException e) {
+        } catch (IOException | RemoteException | IllegalStateException e) {
             Toast.makeText(this,
                     R.string.screenrecord_start_error, Toast.LENGTH_LONG)
                     .show();
             e.printStackTrace();
+            mController.updateState(false);
         }
     }
 
@@ -242,7 +254,6 @@
                 ? res.getString(R.string.screenrecord_ongoing_screen_only)
                 : res.getString(R.string.screenrecord_ongoing_screen_and_audio);
 
-
         Intent stopIntent = getNotificationIntent(this);
         Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
                 .setSmallIcon(R.drawable.ic_screenrecord)
@@ -254,7 +265,7 @@
                 .setOngoing(true)
                 .setContentIntent(
                         PendingIntent.getService(this, REQUEST_CODE, stopIntent,
-                                PendingIntent.FLAG_UPDATE_CURRENT))
+                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
                 .addExtras(extras);
         startForeground(NOTIFICATION_RECORDING_ID, builder.build());
     }
@@ -265,11 +276,17 @@
         String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
                 ? res.getString(R.string.screenrecord_ongoing_screen_only)
                 : res.getString(R.string.screenrecord_ongoing_screen_and_audio);
+
+        Bundle extras = new Bundle();
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+                res.getString(R.string.screenrecord_name));
+
         Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID)
                 .setContentTitle(notificationTitle)
                 .setContentText(
                         getResources().getString(R.string.screenrecord_background_processing_label))
-                .setSmallIcon(R.drawable.ic_screenrecord);
+                .setSmallIcon(R.drawable.ic_screenrecord)
+                .addExtras(extras);
         return builder.build();
     }
 
@@ -287,7 +304,7 @@
                         this,
                         REQUEST_CODE,
                         getShareIntent(this, uri.toString()),
-                        PendingIntent.FLAG_UPDATE_CURRENT))
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
                 .build();
 
         Notification.Action deleteAction = new Notification.Action.Builder(
@@ -297,7 +314,7 @@
                         this,
                         REQUEST_CODE,
                         getDeleteIntent(this, uri.toString()),
-                        PendingIntent.FLAG_UPDATE_CURRENT))
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
                 .build();
 
         Bundle extras = new Bundle();
@@ -328,34 +345,36 @@
         return builder.build();
     }
 
-    private void stopRecording() {
+    private void stopRecording(int userId) {
         setTapsVisible(mOriginalShowTaps);
         if (getRecorder() != null) {
             getRecorder().end();
-            saveRecording();
+            saveRecording(userId);
         } else {
             Log.e(TAG, "stopRecording called, but recorder was null");
         }
         mController.updateState(false);
     }
 
-    private void saveRecording() {
-        mNotificationManager.notify(NOTIFICATION_PROCESSING_ID, createProcessingNotification());
+    private void saveRecording(int userId) {
+        UserHandle currentUser = new UserHandle(userId);
+        mNotificationManager.notifyAsUser(null, NOTIFICATION_PROCESSING_ID,
+                createProcessingNotification(), currentUser);
 
         mLongExecutor.execute(() -> {
             try {
                 Log.d(TAG, "saving recording");
                 Notification notification = createSaveNotification(getRecorder().save());
                 if (!mController.isRecording()) {
-                    Log.d(TAG, "showing saved notification");
-                    mNotificationManager.notify(NOTIFICATION_VIEW_ID, notification);
+                    mNotificationManager.notifyAsUser(null, NOTIFICATION_VIEW_ID, notification,
+                            currentUser);
                 }
             } catch (IOException e) {
                 Log.e(TAG, "Error saving screen recording: " + e.getMessage());
                 Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
                         .show();
             } finally {
-                mNotificationManager.cancel(NOTIFICATION_PROCESSING_ID);
+                mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser);
             }
         });
     }
@@ -371,7 +390,9 @@
      * @return
      */
     public static Intent getStopIntent(Context context) {
-        return new Intent(context, RecordingService.class).setAction(ACTION_STOP);
+        return new Intent(context, RecordingService.class)
+                .setAction(ACTION_STOP)
+                .putExtra(Intent.EXTRA_USER_HANDLE, context.getUserId());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
index edbc3cf..df03c3e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.screenrecord;
 
-import android.content.Context;
 import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioPlaybackCaptureConfiguration;
@@ -39,7 +38,6 @@
     private static String TAG = "ScreenAudioRecorder";
     private static final int TIMEOUT = 500;
     private static final float MIC_VOLUME_SCALE = 1.4f;
-    private final Context mContext;
     private AudioRecord mAudioRecord;
     private AudioRecord mAudioRecordMic;
     private Config mConfig = new Config();
@@ -49,17 +47,14 @@
     private long mPresentationTime;
     private long mTotalBytes;
     private MediaMuxer mMuxer;
-    private String mOutFile;
     private boolean mMic;
 
     private int mTrackId = -1;
 
-    public ScreenInternalAudioRecorder(String outFile, Context context,
-            MediaProjection mp, boolean includeMicInput) throws IOException {
+    public ScreenInternalAudioRecorder(String outFile, MediaProjection mp, boolean includeMicInput)
+            throws IOException {
         mMic = includeMicInput;
-        mOutFile = outFile;
         mMuxer = new MediaMuxer(outFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        mContext = context;
         mMediaProjection = mp;
         Log.d(TAG, "creating audio file " + outFile);
         setupSimple();
@@ -266,8 +261,9 @@
 
     /**
     * start recording
+     * @throws IllegalStateException if recording fails to initialize
     */
-    public void start() {
+    public void start() throws IllegalStateException {
         if (mThread != null) {
             Log.e(TAG, "a recording is being done in parallel or stop is not called");
         }
@@ -276,8 +272,7 @@
         Log.d(TAG, "channel count " + mAudioRecord.getChannelCount());
         mCodec.start();
         if (mAudioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
-            Log.e(TAG, "Error starting audio recording");
-            return;
+            throw new IllegalStateException("Audio recording failed to start");
         }
         mThread.start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 1c7d987..1a9abb9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -166,7 +166,7 @@
                 mAudioSource == MIC_AND_INTERNAL) {
             mTempAudioFile = File.createTempFile("temp", ".aac",
                     mContext.getCacheDir());
-            mAudio = new ScreenInternalAudioRecorder(mTempAudioFile.getAbsolutePath(), mContext,
+            mAudio = new ScreenInternalAudioRecorder(mTempAudioFile.getAbsolutePath(),
                     mMediaProjection, mAudioSource == MIC_AND_INTERNAL);
         }
 
@@ -175,7 +175,7 @@
     /**
     * Start screen recording
     */
-    void start() throws IOException, RemoteException {
+    void start() throws IOException, RemoteException, IllegalStateException {
         Log.d(TAG, "start recording");
         prepare();
         mMediaRecorder.start();
@@ -205,7 +205,7 @@
         }
     }
 
-    private  void recordInternalAudio() {
+    private  void recordInternalAudio() throws IllegalStateException {
         if (mAudioSource == INTERNAL || mAudioSource == MIC_AND_INTERNAL) {
             mAudio.start();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 8347def..dc47ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -23,16 +23,19 @@
 
 import android.app.Activity;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.os.Bundle;
 import android.view.Gravity;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.WindowManager;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.Spinner;
 import android.widget.Switch;
 
 import com.android.systemui.R;
+import com.android.systemui.settings.CurrentUserContextTracker;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -48,16 +51,17 @@
     private static final String TAG = "ScreenRecordDialog";
 
     private final RecordingController mController;
+    private final CurrentUserContextTracker mCurrentUserContextTracker;
     private Switch mTapsSwitch;
     private Switch mAudioSwitch;
     private Spinner mOptions;
     private List<ScreenRecordingAudioSource> mModes;
-    private int mSelected;
-
 
     @Inject
-    public ScreenRecordDialog(RecordingController controller) {
+    public ScreenRecordDialog(RecordingController controller,
+            CurrentUserContextTracker currentUserContextTracker) {
         mController = controller;
+        mCurrentUserContextTracker = currentUserContextTracker;
     }
 
     @Override
@@ -68,6 +72,7 @@
         // Inflate the decor view, so the attributes below are not overwritten by the theme.
         window.getDecorView();
         window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
         window.setGravity(Gravity.TOP);
         setTitle(R.string.screenrecord_name);
 
@@ -100,24 +105,24 @@
         mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
             mAudioSwitch.setChecked(true);
         });
-
     }
 
     private void requestScreenCapture() {
+        Context userContext = mCurrentUserContextTracker.getCurrentUserContext();
         boolean showTaps = mTapsSwitch.isChecked();
         ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
                 ? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
                 : NONE;
-        PendingIntent startIntent = PendingIntent.getForegroundService(this,
+        PendingIntent startIntent = PendingIntent.getForegroundService(userContext,
                 RecordingService.REQUEST_CODE,
                 RecordingService.getStartIntent(
-                        ScreenRecordDialog.this, RESULT_OK,
+                        userContext, RESULT_OK,
                         audioMode.ordinal(), showTaps),
-                PendingIntent.FLAG_UPDATE_CURRENT);
-        PendingIntent stopIntent = PendingIntent.getService(this,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+        PendingIntent stopIntent = PendingIntent.getService(userContext,
                 RecordingService.REQUEST_CODE,
-                RecordingService.getStopIntent(this),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                RecordingService.getStopIntent(userContext),
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9bbc4dd..8e878dd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -58,7 +58,6 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -1179,11 +1178,15 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
-            Intent actionIntent = pendingIntent.getIntent();
             String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
-            Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent);
+            Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
             ActivityOptions opts = ActivityOptions.makeBasic();
-            context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
+
+            try {
+                pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
+            } catch (PendingIntent.CanceledException e) {
+                Log.e(TAG, "Pending intent canceled", e);
+            }
 
             ScreenshotSmartActions.notifyScreenshotAction(
                     context, intent.getStringExtra(EXTRA_ID), actionType, true);
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 f2eec39..c321331 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -166,6 +166,7 @@
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
     private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
     private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
+    private final BiometricUnlockController mBiometricUnlockController;
     private final NotificationPanelView mView;
     private final MetricsLogger mMetricsLogger;
     private final ActivityManager mActivityManager;
@@ -227,7 +228,8 @@
                             mBarState == StatusBarState.KEYGUARD
                                     || mBarState == StatusBarState.SHADE_LOCKED;
                     if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
-                            && !mDelayShowingKeyguardStatusBar) {
+                            && !mDelayShowingKeyguardStatusBar
+                            && !mBiometricUnlockController.isBiometricUnlock()) {
                         mFirstBypassAttempt = false;
                         animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
                     }
@@ -487,6 +489,7 @@
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             ConversationNotificationManager conversationNotificationManager,
             MediaHierarchyManager mediaHierarchyManager,
+            BiometricUnlockController biometricUnlockController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
@@ -511,6 +514,7 @@
         mDisplayId = displayId;
         mPulseExpansionHandler = pulseExpansionHandler;
         mDozeParameters = dozeParameters;
+        mBiometricUnlockController = biometricUnlockController;
         pulseExpansionHandler.setPulseExpandAbortListener(() -> {
             if (mQs != null) {
                 mQs.animateHeaderSlidingOut();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 06d35a3..5bb8fab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -688,8 +688,8 @@
         if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown");
         mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
         // Reset talkback priority
-        mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
-                View.ACCESSIBILITY_LIVE_REGION_NONE);
+        mHandler.post(() -> mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
+                View.ACCESSIBILITY_LIVE_REGION_NONE));
     }
 
     @Override
@@ -698,7 +698,7 @@
         mIconController.setIcon(mSlotScreenRecord,
                 R.drawable.stat_sys_screen_record,
                 mResources.getString(R.string.screenrecord_ongoing_screen_only));
-        mIconController.setIconVisibility(mSlotScreenRecord, true);
+        mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, true));
     }
 
     @Override
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 e502459..5a68238 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
@@ -29,13 +29,12 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -44,18 +43,16 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ScreenRecordTileTest extends SysuiTestCase {
 
     @Mock
     private RecordingController mController;
     @Mock
-    private ActivityStarter mActivityStarter;
-    @Mock
     private QSTileHost mHost;
     @Mock
-    private UiEventLogger mUiEventLogger;
+    private KeyguardDismissUtil mKeyguardDismissUtil;
 
     private TestableLooper mTestableLooper;
     private ScreenRecordTile mTile;
@@ -67,11 +64,10 @@
         mTestableLooper = TestableLooper.get(this);
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mController = mDependency.injectMockDependency(RecordingController.class);
-        mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
 
         when(mHost.getContext()).thenReturn(mContext);
 
-        mTile = new ScreenRecordTile(mHost, mController, mActivityStarter, mUiEventLogger);
+        mTile = new ScreenRecordTile(mHost, mController, mKeyguardDismissUtil);
     }
 
     // Test that the tile is inactive and labeled correctly when the controller is neither starting
@@ -89,6 +85,7 @@
                 mContext.getString(R.string.quick_settings_screen_record_start)));
 
         mTile.handleClick();
+        mTestableLooper.processAllMessages();
         verify(mController, times(1)).getPromptIntent();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index b877c7f..11ef3e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -22,12 +22,14 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.PendingIntent;
+import android.content.Intent;
 import android.os.Looper;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -45,14 +47,18 @@
 public class RecordingControllerTest extends SysuiTestCase {
 
     @Mock
-    RecordingController.RecordingStateChangeCallback mCallback;
+    private RecordingController.RecordingStateChangeCallback mCallback;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
 
-    RecordingController mController;
+    private RecordingController mController;
+
+    private static final int USER_ID = 10;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new RecordingController(mContext);
+        mController = new RecordingController(mBroadcastDispatcher);
         mController.addCallback(mCallback);
     }
 
@@ -121,4 +127,27 @@
         assertFalse(mController.isRecording());
         verify(mCallback).onRecordingEnd();
     }
+
+    // Test that switching users will stop an ongoing recording
+    @Test
+    public void testUserChange() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        // If we are recording
+        PendingIntent startIntent = Mockito.mock(PendingIntent.class);
+        PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
+        mController.startCountdown(0, 0, startIntent, stopIntent);
+        mController.updateState(true);
+
+        // and user is changed
+        Intent intent = new Intent(Intent.ACTION_USER_SWITCHED)
+                .putExtra(Intent.EXTRA_USER_HANDLE, USER_ID);
+        mController.mUserChangeReceiver.onReceive(mContext, intent);
+
+        // Ensure that the recording was stopped
+        verify(mCallback).onRecordingEnd();
+        assertFalse(mController.isRecording());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 283a47c..e98b6b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -32,6 +32,7 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.CurrentUserContextTracker;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -58,6 +59,8 @@
     private Notification mNotification;
     @Mock
     private Executor mExecutor;
+    @Mock
+    private CurrentUserContextTracker mUserContextTracker;
 
     private RecordingService mRecordingService;
 
@@ -65,7 +68,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mUiEventLogger,
-                mNotificationManager));
+                mNotificationManager, mUserContextTracker));
 
         // Return actual context info
         doReturn(mContext).when(mRecordingService).getApplicationContext();
@@ -80,6 +83,8 @@
 
         doNothing().when(mRecordingService).startForeground(anyInt(), any());
         doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder();
+
+        doReturn(mContext).when(mUserContextTracker).getCurrentUserContext();
     }
 
     @Test
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 c2d2181..b0b66b8 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
@@ -180,6 +180,8 @@
     @Mock
     private ConversationNotificationManager mConversationNotificationManager;
     @Mock
+    private BiometricUnlockController mBiometricUnlockController;
+    @Mock
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
 
@@ -238,7 +240,7 @@
                 mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController,
                 mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
                 mConversationNotificationManager, mMediaHiearchyManager,
-                mStatusBarKeyguardViewManager);
+                mBiometricUnlockController, mStatusBarKeyguardViewManager);
         mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager,
                 mNotificationShelf, mNotificationAreaController, mScrimController);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b9240c7..9bc702d 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -855,8 +855,7 @@
                 mTempLayer = 0;
                 mDisplayContent.forAllWindows((w) -> {
                     if (w.isOnScreen() && w.isVisibleLw()
-                            && (w.mAttrs.alpha != 0)
-                            && !w.mWinAnimator.mEnterAnimationPending) {
+                            && (w.mAttrs.alpha != 0)) {
                         mTempLayer++;
                         outWindows.put(mTempLayer, w);
                     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index afe40b8..86ef0d8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3437,9 +3437,10 @@
                 "Proposed new IME target: " + target + " for display: " + getDisplayId());
 
         // Now, a special case -- if the last target's window is in the process of exiting, but
-        // not removed, keep on the last target to avoid IME flicker.
+        // not removed, keep on the last target to avoid IME flicker. The exception is if the
+        // current target is home since we want opening apps to become the IME target right away.
         if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayedLw()
-                && curTarget.isClosing()) {
+                && curTarget.isClosing() && !curTarget.isActivityTypeHome()) {
             if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Not changing target till current window is"
                     + " closing and not removed");
             return curTarget;
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 4e2f9a4..924ad7f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -377,11 +377,11 @@
     }
 
     private BlobMetadata createBlobMetadataMock(long blobId, File blobFile,
-            BlobHandle blobHandle, boolean hasLeases) {
+            BlobHandle blobHandle, boolean hasValidLeases) {
         final BlobMetadata blobMetadata = mock(BlobMetadata.class);
         doReturn(blobId).when(blobMetadata).getBlobId();
         doReturn(blobFile).when(blobMetadata).getBlobFile();
-        doReturn(hasLeases).when(blobMetadata).hasLeases();
+        doReturn(hasValidLeases).when(blobMetadata).hasValidLeases();
         doReturn(blobHandle).when(blobMetadata).getBlobHandle();
         doCallRealMethod().when(blobMetadata).shouldBeDeleted(anyBoolean());
         doReturn(true).when(blobMetadata).hasLeaseWaitTimeElapsedForAll();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ef4d5db..16aa87b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1339,14 +1339,15 @@
     }
 
     @Test
-    @Ignore
     public void testPostCancelPostNotifiesListeners() throws Exception {
         // WHEN a notification is posted
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
                 sbn.getNotification(), sbn.getUserId());
+        Thread.sleep(1);  // make sure the system clock advances before the next step
         // THEN it is canceled
         mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
+        Thread.sleep(1);  // here too
         // THEN it is posted again (before the cancel has a chance to finish)
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
                 sbn.getNotification(), sbn.getUserId());