Merge "Use openPipeHelper() for streaming IconsContentProvider data." into rvc-dev
diff --git a/Android.bp b/Android.bp
index ec77404..14a2bff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -534,6 +534,8 @@
     static_libs: [
         "exoplayer2-extractor",
         "android.hardware.wifi-V1.0-java-constants",
+        // Additional dependencies needed to build the ike API classes.
+        "ike-internals",
     ],
     apex_available: ["//apex_available:platform"],
     visibility: [
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index ecc78ce..113f8fe 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -51,6 +51,7 @@
     };
 
     private static final int LIMIT_BLOB_TAG_LENGTH = 128; // characters
+    private static final int LIMIT_BLOB_LABEL_LENGTH = 100; // characters
 
     /**
      * Cyrptographically secure hash algorithm used to generate hash of the blob this handle is
@@ -128,6 +129,9 @@
      *
      * @param digest the SHA-256 hash of the blob this is representing.
      * @param label a label indicating what the blob is, that can be surfaced to the user.
+     *              The length of the label cannot be more than 100 characters. It is recommended
+     *              to keep this brief. This may be truncated and ellipsized if it is too long
+     *              to be displayed to the user.
      * @param expiryTimeMillis the time in secs after which the blob should be invalidated and not
      *                         allowed to be accessed by any other app,
      *                         in {@link System#currentTimeMillis()} timebase or {@code 0} to
@@ -205,9 +209,9 @@
         final BlobHandle other = (BlobHandle) obj;
         return this.algorithm.equals(other.algorithm)
                 && Arrays.equals(this.digest, other.digest)
-                && this.label.equals(other.label)
+                && this.label.toString().equals(other.label.toString())
                 && this.expiryTimeMillis == other.expiryTimeMillis
-                && this.tag.equals(tag);
+                && this.tag.equals(other.tag);
     }
 
     @Override
@@ -233,6 +237,7 @@
         Preconditions.checkArgumentIsSupported(SUPPORTED_ALGOS, algorithm);
         Preconditions.checkByteArrayNotEmpty(digest, "digest");
         Preconditions.checkStringNotEmpty(label, "label must not be null");
+        Preconditions.checkArgument(label.length() <= LIMIT_BLOB_LABEL_LENGTH, "label too long");
         Preconditions.checkArgumentNonnegative(expiryTimeMillis,
                 "expiryTimeMillis must not be negative");
         Preconditions.checkStringNotEmpty(tag, "tag must not be null");
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 9c1acaf..39f7526 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -347,7 +347,9 @@
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param description a short description string that can be surfaced
-     *                    to the user explaining what the blob is used for.
+     *                    to the user explaining what the blob is used for. It is recommended to
+     *                    keep this description brief. This may be truncated and ellipsized
+     *                    if it is too long to be displayed to the user.
      * @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
      *                              automatically released, in {@link System#currentTimeMillis()}
      *                              timebase. If its value is {@code 0}, then the behavior of this
@@ -458,7 +460,9 @@
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param description a short description string that can be surfaced
-     *                    to the user explaining what the blob is used for.
+     *                    to the user explaining what the blob is used for. It is recommended to
+     *                    keep this description brief. This may be truncated and
+     *                    ellipsized if it is too long to be displayed to the user.
      *
      * @throws IOException when there is an I/O error while acquiring a lease to the blob.
      * @throws SecurityException when the blob represented by the {@code blobHandle} does not
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index 79cd1b1..bb9f13f 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -25,6 +25,7 @@
 import android.os.Environment;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
 import android.util.DataUnit;
 import android.util.Log;
 import android.util.Slog;
@@ -171,6 +172,13 @@
         public static int MAX_BLOB_ACCESS_PERMITTED_PACKAGES =
                 DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
 
+        /**
+         * Denotes the maximum number of characters that a lease description can have.
+         */
+        public static final String KEY_LEASE_DESC_CHAR_LIMIT = "lease_desc_char_limit";
+        public static int DEFAULT_LEASE_DESC_CHAR_LIMIT = 300;
+        public static int LEASE_DESC_CHAR_LIMIT = DEFAULT_LEASE_DESC_CHAR_LIMIT;
+
         static void refresh(Properties properties) {
             if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
                 return;
@@ -221,6 +229,10 @@
                         MAX_BLOB_ACCESS_PERMITTED_PACKAGES = properties.getInt(key,
                                 DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES);
                         break;
+                    case KEY_LEASE_DESC_CHAR_LIMIT:
+                        LEASE_DESC_CHAR_LIMIT = properties.getInt(key,
+                                DEFAULT_LEASE_DESC_CHAR_LIMIT);
+                        break;
                     default:
                         Slog.wtf(TAG, "Unknown key in device config properties: " + key);
                 }
@@ -262,6 +274,8 @@
             fout.println(String.format(dumpFormat, KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
                     MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
                     DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES));
+            fout.println(String.format(dumpFormat, KEY_LEASE_DESC_CHAR_LIMIT,
+                    LEASE_DESC_CHAR_LIMIT, DEFAULT_LEASE_DESC_CHAR_LIMIT));
         }
     }
 
@@ -368,6 +382,18 @@
         return DeviceConfigProperties.MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
     }
 
+    /**
+     * Returns the lease description truncated to
+     * {@link DeviceConfigProperties#LEASE_DESC_CHAR_LIMIT} characters.
+     */
+    public static CharSequence getTruncatedLeaseDescription(CharSequence description) {
+        if (TextUtils.isEmpty(description)) {
+            return description;
+        }
+        return TextUtils.trimToLengthWithEllipsis(description,
+                DeviceConfigProperties.LEASE_DESC_CHAR_LIMIT);
+    }
+
     @Nullable
     public static File prepareBlobFile(long sessionId) {
         final File blobsDir = prepareBlobsDir();
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 520e8bb..d37dfde 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -1500,6 +1500,8 @@
                     "leaseExpiryTimeMillis must not be negative");
             Objects.requireNonNull(packageName, "packageName must not be null");
 
+            description = BlobStoreConfig.getTruncatedLeaseDescription(description);
+
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4bd7b05..591a714 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -65,6 +65,13 @@
     /**
      * A device, returned in the activity result of the {@link IntentSender} received in
      * {@link Callback#onDeviceFound}
+     *
+     * Type is:
+     * <ul>
+     *     <li>for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}</li>
+     *     <li>for Bluetooth LE - {@link android.bluetooth.le.ScanResult}</li>
+     *     <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
+     * </ul>
      */
     public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
 
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 08aa534..2d99c41 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -312,7 +312,12 @@
          * setting it to the {@link
          * android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. If you
          * provide a dataset in the result, it will replace the authenticated dataset and
-         * will be immediately filled in. If you provide a response, it will replace the
+         * will be immediately filled in. An exception to this behavior is if the original
+         * dataset represents a pinned inline suggestion (i.e. any of the field in the dataset
+         * has a pinned inline presentation, see {@link InlinePresentation#isPinned()}), then
+         * the original dataset will not be replaced,
+         * so that it can be triggered as a pending intent again.
+         * If you provide a response, it will replace the
          * current response and the UI will be refreshed. For example, if you provided
          * credit card information without the CVV for the data set in the {@link FillResponse
          * response} then the returned data set should contain the CVV entry.
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 9cf1b87..91416948 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -50,7 +50,11 @@
 
     /**
      * Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
-     * host.
+     * host. However, it's eventually up to the host whether the UI is pinned or not.
+     *
+     * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+     * new data set returned from authentication intent. See
+     * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
      */
     private final boolean mPinned;
 
@@ -90,7 +94,11 @@
      *   Specifies the UI specification for the inline suggestion.
      * @param pinned
      *   Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
-     *   host.
+     *   host. However, it's eventually up to the host whether the UI is pinned or not.
+     *
+     *   <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+     *   new data set returned from authentication intent. See
+     *   {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
      */
     @DataClass.Generated.Member
     public InlinePresentation(
@@ -126,7 +134,11 @@
 
     /**
      * Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
-     * host.
+     * host. However, it's eventually up to the host whether the UI is pinned or not.
+     *
+     * <p> Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+     * new data set returned from authentication intent. See
+     * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
      */
     @DataClass.Generated.Member
     public boolean isPinned() {
@@ -232,7 +244,7 @@
     };
 
     @DataClass.Generated(
-            time = 1586992400667L,
+            time = 1593131904745L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
             inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final  boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index bb0de12..f937bc9 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -471,6 +471,13 @@
     }
 
     private void performDrawFinished() {
+        if (mDeferredDestroySurfaceControl != null) {
+            synchronized (mSurfaceControlLock) {
+                mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
+                mDeferredDestroySurfaceControl = null;
+            }
+        }
+
         if (mPendingReportDraws > 0) {
             mDrawFinished = true;
             if (mAttachedToWindow) {
@@ -1194,13 +1201,6 @@
                     + "finishedDrawing");
         }
 
-        if (mDeferredDestroySurfaceControl != null) {
-            synchronized (mSurfaceControlLock) {
-                mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
-                mDeferredDestroySurfaceControl = null;
-            }
-        }
-
         runOnUiThread(this::performDrawFinished);
     }
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8c2358d..14cf258 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2040,6 +2040,9 @@
         if (!isUserRunning(userHandle)) {
             return false;
         }
+        if (!isUserUnlocked(userHandle)) {
+            return false;
+        }
         if (isQuietModeEnabled(userHandle)) {
             return false;
         }
@@ -2892,6 +2895,12 @@
     }
 
     @VisibleForTesting
+    protected boolean isUserUnlocked(UserHandle userHandle) {
+        UserManager userManager = getSystemService(UserManager.class);
+        return userManager.isUserUnlocked(userHandle);
+    }
+
+    @VisibleForTesting
     protected boolean isQuietModeEnabled(UserHandle userHandle) {
         UserManager userManager = getSystemService(UserManager.class);
         return userManager.isQuietModeEnabled(userHandle);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index fba4675a..233231c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1279,13 +1279,17 @@
     }
 
     private void safelyStartActivityInternal(TargetInfo cti) {
-        if (mPersonalPackageMonitor != null) {
-            mPersonalPackageMonitor.unregister();
+        // If the target is suspended, the activity will not be successfully launched.
+        // Do not unregister from package manager updates in this case
+        if (!cti.isSuspended()) {
+            if (mPersonalPackageMonitor != null) {
+                mPersonalPackageMonitor.unregister();
+            }
+            if (mWorkPackageMonitor != null) {
+                mWorkPackageMonitor.unregister();
+            }
+            mRegistered = false;
         }
-        if (mWorkPackageMonitor != null) {
-            mWorkPackageMonitor.unregister();
-        }
-        mRegistered = false;
         // If needed, show that intent is forwarded
         // from managed profile to owner or other way around.
         if (mProfileSwitchMessageId != -1) {
diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml
index 889a615..3b6b407 100644
--- a/core/res/res/anim/screen_rotate_180_enter.xml
+++ b/core/res/res/anim/screen_rotate_180_enter.xml
@@ -25,4 +25,10 @@
         android:fillBefore="true" android:fillAfter="true"
         android:interpolator="@interpolator/fast_out_slow_in"
         android:duration="@android:integer/config_screen_rotation_total_180" />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/screen_rotation_alpha_in"
+        android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
+        android:duration="@android:integer/config_screen_rotation_fade_in" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 766fcfa..26fb6d8 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -25,4 +25,9 @@
         android:fillBefore="true" android:fillAfter="true"
         android:interpolator="@interpolator/fast_out_slow_in"
         android:duration="@android:integer/config_screen_rotation_total_180" />
-</set>
\ No newline at end of file
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+        android:fillEnabled="true"
+        android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/screen_rotation_alpha_out"
+        android:duration="@android:integer/config_screen_rotation_fade_out" />
+</set>
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 0bf10cb..090645f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -1996,7 +1996,7 @@
     }
 
     @Test
-    public void testWorkTab_selectingWorkTabWithLockedWorkUser_directShareTargetsNotQueried() {
+    public void testWorkTab_selectingWorkTabWithNotRunningWorkUser_directShareTargetsNotQueried() {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
@@ -2035,6 +2035,70 @@
     }
 
     @Test
+    public void testWorkTab_workUserNotRunning_workTargetsShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(3);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+        sOverrides.isWorkProfileUserRunning = false;
+
+        final ChooserWrapperActivity activity =
+                mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        assertEquals(3, activity.getWorkListAdapter().getCount());
+    }
+
+    @Test
+    public void testWorkTab_selectingWorkTabWithLockedWorkUser_directShareTargetsNotQueried() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(3);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        sOverrides.isWorkProfileUserUnlocked = false;
+        boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
+        sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
+            isQueryDirectShareCalledOnWorkProfile[0] =
+                    (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+            return null;
+        };
+        boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
+        sOverrides.onQueryTargetServices = chooserListAdapter -> {
+            isQueryTargetServicesCalledOnWorkProfile[0] =
+                    (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+            return null;
+        };
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        assertFalse("Direct share targets were queried on a locked work profile user",
+                isQueryDirectShareCalledOnWorkProfile[0]);
+        assertFalse("Target services were queried on a locked work profile user",
+                isQueryTargetServicesCalledOnWorkProfile[0]);
+    }
+
+    @Test
     public void testWorkTab_workUserLocked_workTargetsShown() {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -2046,7 +2110,7 @@
         setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendTextIntent();
         sendIntent.setType("TestType");
-        sOverrides.isWorkProfileUserRunning = false;
+        sOverrides.isWorkProfileUserUnlocked = false;
 
         final ChooserWrapperActivity activity =
                 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index b7d6c61..d3d5caf 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -229,9 +229,20 @@
 
     @Override
     protected boolean isUserRunning(UserHandle userHandle) {
+        if (userHandle.equals(UserHandle.SYSTEM)) {
+            return super.isUserRunning(userHandle);
+        }
         return sOverrides.isWorkProfileUserRunning;
     }
 
+    @Override
+    protected boolean isUserUnlocked(UserHandle userHandle) {
+        if (userHandle.equals(UserHandle.SYSTEM)) {
+            return super.isUserUnlocked(userHandle);
+        }
+        return sOverrides.isWorkProfileUserUnlocked;
+    }
+
     /**
      * We cannot directly mock the activity created since instrumentation creates it.
      * <p>
@@ -258,6 +269,7 @@
         public boolean hasCrossProfileIntents;
         public boolean isQuietModeEnabled;
         public boolean isWorkProfileUserRunning;
+        public boolean isWorkProfileUserUnlocked;
         public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
         public PackageManager packageManager;
 
@@ -281,6 +293,7 @@
             hasCrossProfileIntents = true;
             isQuietModeEnabled = false;
             isWorkProfileUserRunning = true;
+            isWorkProfileUserUnlocked = true;
             packageManager = null;
             multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
                 @Override
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8520fff..c710bed 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -424,6 +424,8 @@
         <permission name="android.permission.LOCATION_HARDWARE" />
         <!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
         <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
+        <!-- Permissions required for CTS test - AdbManagerTest -->
+        <permission name="android.permission.MANAGE_DEBUGGING" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index bfc623f..33ab16d 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1663,12 +1663,6 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
     },
-    "1108406230": {
-      "message": "stopFreezingDisplayLocked: Returning mWaitingForConfig=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_ORIENTATION",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "1112047265": {
       "message": "finishDrawingWindow: %s mDrawState=%s",
       "level": "DEBUG",
@@ -1729,6 +1723,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1246035185": {
+      "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteRotation=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "1254403969": {
       "message": "Surface returned was null: %s",
       "level": "VERBOSE",
@@ -1951,12 +1951,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/AppTransition.java"
     },
-    "1591969812": {
-      "message": "updateImeControlTarget %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_IME",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "1628345525": {
       "message": "Now opening app %s",
       "level": "VERBOSE",
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b64a9e7..570c2ab3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -314,6 +314,9 @@
     <!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
     <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
 
+    <!-- Permissions required for CTS test - AdbManagerTest -->
+    <uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index da4cb24..3c641af 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -207,8 +207,8 @@
     <com.android.internal.widget.CachingIconView
         android:id="@+id/icon"
         android:tint="@color/media_primary_text"
-        android:layout_width="20dp"
-        android:layout_height="20dp" />
+        android:layout_width="@dimen/qs_media_icon_size"
+        android:layout_height="@dimen/qs_media_icon_size" />
 
     <!-- Buttons to remove this view when no longer needed -->
     <include
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 87e1499..eb8758c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1269,10 +1269,10 @@
     <dimen name="qs_media_padding">16dp</dimen>
     <dimen name="qs_media_panel_outer_padding">16dp</dimen>
     <dimen name="qs_media_album_size">52dp</dimen>
+    <dimen name="qs_media_icon_size">16dp</dimen>
     <dimen name="qs_center_guideline_padding">10dp</dimen>
-    <dimen name="qs_seamless_icon_size">20dp</dimen>
-    <dimen name="qs_seamless_fallback_icon_size">20dp</dimen>
-    <dimen name="qs_seamless_fallback_top_margin">18dp</dimen>
+    <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen>
+    <dimen name="qs_seamless_fallback_icon_size">@dimen/qs_seamless_icon_size</dimen>
     <dimen name="qs_seamless_fallback_end_margin">16dp</dimen>
     <dimen name="qqs_media_spacing">16dp</dimen>
     <dimen name="qs_footer_horizontal_margin">22dp</dimen>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index 7009cf9..ee958f2 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -19,8 +19,8 @@
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <Constraint
         android:id="@+id/icon"
-        android:layout_width="16dp"
-        android:layout_height="16dp"
+        android:layout_width="@dimen/qs_media_icon_size"
+        android:layout_height="@dimen/qs_media_icon_size"
         android:layout_marginStart="18dp"
         app:layout_constraintTop_toTopOf="@id/app_name"
         app:layout_constraintBottom_toBottomOf="@id/app_name"
@@ -59,14 +59,14 @@
         android:id="@+id/media_seamless_fallback"
         android:layout_width="@dimen/qs_seamless_fallback_icon_size"
         android:layout_height="@dimen/qs_seamless_fallback_icon_size"
-        android:layout_marginTop="@dimen/qs_seamless_fallback_top_margin"
         android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
         android:layout_marginStart="@dimen/qs_center_guideline_padding"
         android:alpha="0.5"
         android:visibility="gone"
         app:layout_constraintHorizontal_bias="1"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toTopOf="@id/app_name"
+        app:layout_constraintBottom_toBottomOf="@id/app_name"
         app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
         />
 
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index dc275ba..d5a02c2 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -19,8 +19,8 @@
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <Constraint
         android:id="@+id/icon"
-        android:layout_width="16dp"
-        android:layout_height="16dp"
+        android:layout_width="@dimen/qs_media_icon_size"
+        android:layout_height="@dimen/qs_media_icon_size"
         android:layout_marginStart="18dp"
         app:layout_constraintTop_toTopOf="@id/app_name"
         app:layout_constraintBottom_toBottomOf="@id/app_name"
@@ -59,14 +59,14 @@
         android:id="@+id/media_seamless_fallback"
         android:layout_width="@dimen/qs_seamless_fallback_icon_size"
         android:layout_height="@dimen/qs_seamless_fallback_icon_size"
-        android:layout_marginTop="@dimen/qs_seamless_fallback_top_margin"
         android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
         android:layout_marginStart="@dimen/qs_center_guideline_padding"
         android:alpha="0.5"
         android:visibility="gone"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toTopOf="@id/app_name"
+        app:layout_constraintBottom_toBottomOf="@id/app_name"
         app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="1"
         />
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index c170ee2..ec4304f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -476,6 +476,9 @@
                 if (DEBUG_BUBBLE_DATA) {
                     Log.d(TAG, "Cancel overflow bubble: " + b);
                 }
+                if (b != null) {
+                    b.stopInflation();
+                }
                 mLogger.logOverflowRemove(b, reason);
                 mStateChange.bubbleRemoved(b, reason);
                 mOverflowBubbles.remove(b);
@@ -483,6 +486,7 @@
             return;
         }
         Bubble bubbleToRemove = mBubbles.get(indexToRemove);
+        bubbleToRemove.stopInflation();
         if (mBubbles.size() == 1) {
             // Going to become empty, handle specially.
             setExpandedInternal(false);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index cd16924..1a730c3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -299,6 +299,7 @@
         if (numPages == 1) {
             pageIndicator.setLocation(0f)
         }
+        updatePageIndicatorAlpha()
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 07a7e61..570de63 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -1,6 +1,5 @@
 package com.android.systemui.media
 
-import android.graphics.PointF
 import android.graphics.Rect
 import android.util.ArraySet
 import android.view.View
@@ -101,7 +100,7 @@
                 }
                 // This will trigger a state change that ensures that we now have a state available
                 state.measurementInput = input
-                return mediaHostStatesManager.getPlayerDimensions(state)
+                return mediaHostStatesManager.updateCarouselDimensions(location, state)
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
index f90af2a..d3954b7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
@@ -32,6 +32,12 @@
     private val controllers: MutableSet<MediaViewController> = mutableSetOf()
 
     /**
+     * The overall sizes of the carousel. This is needed to make sure all players in the carousel
+     * have equal size.
+     */
+    val carouselSizes: MutableMap<Int, MeasurementOutput> = mutableMapOf()
+
+    /**
      * A map with all media states of all locations.
      */
     val mediaHostStates: MutableMap<Int, MediaHostState> = mutableMapOf()
@@ -45,6 +51,7 @@
         if (!hostState.equals(currentState)) {
             val newState = hostState.copy()
             mediaHostStates.put(location, newState)
+            updateCarouselDimensions(location, hostState)
             // First update all the controllers to ensure they get the chance to measure
             for (controller in controllers) {
                 controller.stateCallback.onHostStateChanged(location, newState)
@@ -61,7 +68,10 @@
      * Get the dimensions of all players combined, which determines the overall height of the
      * media carousel and the media hosts.
      */
-    fun getPlayerDimensions(hostState: MediaHostState): MeasurementOutput {
+    fun updateCarouselDimensions(
+        @MediaLocation location: Int,
+        hostState: MediaHostState
+    ): MeasurementOutput {
         val result = MeasurementOutput(0, 0)
         for (controller in controllers) {
             val measurement = controller.getMeasurementsForState(hostState)
@@ -74,6 +84,7 @@
                 }
             }
         }
+        carouselSizes[location] = result
         return result
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 033a42a..83cfdd5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -79,6 +79,16 @@
     private val tmpState = TransitionViewState()
 
     /**
+     * A temporary state used to store intermediate measurements.
+     */
+    private val tmpState2 = TransitionViewState()
+
+    /**
+     * A temporary state used to store intermediate measurements.
+     */
+    private val tmpState3 = TransitionViewState()
+
+    /**
      * A temporary cache key to be used to look up cache entries
      */
     private val tmpKey = CacheKey()
@@ -304,8 +314,8 @@
         // Obtain the view state that we'd want to be at the end
         // The view might not be bound yet or has never been measured and in that case will be
         // reset once the state is fully available
-        val endViewState = obtainViewState(endHostState) ?: return
-
+        var endViewState = obtainViewState(endHostState) ?: return
+        endViewState = updateViewStateToCarouselSize(endViewState, endLocation, tmpState2)!!
         layoutController.setMeasureState(endViewState)
 
         // If the view isn't bound, we can drop the animation, otherwise we'll execute it
@@ -315,7 +325,8 @@
         }
 
         val result: TransitionViewState
-        val startViewState = obtainViewState(startHostState)
+        var startViewState = obtainViewState(startHostState)
+        startViewState = updateViewStateToCarouselSize(startViewState, startLocation, tmpState3)
 
         if (!endHostState.visible) {
             // Let's handle the case where the end is gone first. In this case we take the
@@ -350,6 +361,22 @@
                 animationDelay)
     }
 
+    private fun updateViewStateToCarouselSize(
+        viewState: TransitionViewState?,
+        location: Int,
+        outState: TransitionViewState
+    ) : TransitionViewState? {
+        val result = viewState?.copy(outState) ?: return null
+        val overrideSize = mediaHostStatesManager.carouselSizes[location]
+        overrideSize?.let {
+            // To be safe we're using a maximum here. The override size should always be set
+            // properly though.
+            result.height = Math.max(it.measuredHeight, result.height)
+            result.width = Math.max(it.measuredWidth, result.width)
+        }
+        return result
+    }
+
     /**
      * Retrieves the [TransitionViewState] and [MediaHostState] of a [@MediaLocation].
      * In the event of [location] not being visible, [locationWhenHidden] will be used instead.
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index 1842564..68b6785 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -94,7 +94,7 @@
             // a request with EXTRA_RECENT; if they don't, no resume controls
             MediaBrowser.MediaItem child = children.get(0);
             MediaDescription desc = child.getDescription();
-            if (child.isPlayable()) {
+            if (child.isPlayable() && mMediaBrowser != null) {
                 mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
                         ResumeMediaBrowser.this);
             } else {
@@ -129,7 +129,7 @@
         @Override
         public void onConnected() {
             Log.d(TAG, "Service connected for " + mComponentName);
-            if (mMediaBrowser.isConnected()) {
+            if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
                 String root = mMediaBrowser.getRoot();
                 if (!TextUtils.isEmpty(root)) {
                     mCallback.onConnected();
@@ -187,7 +187,7 @@
                     @Override
                     public void onConnected() {
                         Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected());
-                        if (!mMediaBrowser.isConnected()) {
+                        if (mMediaBrowser == null || !mMediaBrowser.isConnected()) {
                             mCallback.onError();
                             return;
                         }
@@ -246,7 +246,7 @@
                     @Override
                     public void onConnected() {
                         Log.d(TAG, "connected");
-                        if (!mMediaBrowser.isConnected()
+                        if (mMediaBrowser == null || !mMediaBrowser.isConnected()
                                 || TextUtils.isEmpty(mMediaBrowser.getRoot())) {
                             mCallback.onError();
                         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 2980f11..ead1786 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -18,16 +18,16 @@
 
 import android.animation.AnimationHandler;
 import android.animation.Animator;
+import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.Interpolators;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -76,7 +76,6 @@
                 || direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
     }
 
-    private final Interpolator mFastOutSlowInInterpolator;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
 
     private PipTransitionAnimator mCurrentAnimator;
@@ -90,8 +89,6 @@
 
     @Inject
     PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_slow_in);
         mSurfaceTransactionHelper = helper;
     }
 
@@ -113,10 +110,11 @@
     }
 
     @SuppressWarnings("unchecked")
-    PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
+    PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
+            Rect sourceHintRect) {
         if (mCurrentAnimator == null) {
             mCurrentAnimator = setupPipTransitionAnimator(
-                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
         } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                 && mCurrentAnimator.isRunning()) {
             // If we are still animating the fade into pip, then just move the surface and ensure
@@ -131,7 +129,7 @@
         } else {
             mCurrentAnimator.cancel();
             mCurrentAnimator = setupPipTransitionAnimator(
-                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
         }
         return mCurrentAnimator;
     }
@@ -142,7 +140,7 @@
 
     private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
         animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         animator.setFloatValues(FRACTION_START, FRACTION_END);
         animator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
         return animator;
@@ -341,6 +339,7 @@
                 @Override
                 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
                     getSurfaceTransactionHelper()
+                            .resetScale(tx, leash, getDestinationBounds())
                             .crop(tx, leash, getDestinationBounds())
                             .round(tx, leash, shouldApplyCornerRadius());
                     tx.show(leash);
@@ -356,35 +355,46 @@
         }
 
         static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
-                Rect startValue, Rect endValue) {
+                Rect startValue, Rect endValue, Rect sourceHintRect) {
+            // Just for simplicity we'll interpolate between the source rect hint insets and empty
+            // insets to calculate the window crop
+            final Rect initialStartValue = new Rect(startValue);
+            final Rect sourceHintRectInsets = sourceHintRect != null
+                    ? new Rect(sourceHintRect.left - startValue.left,
+                            sourceHintRect.top - startValue.top,
+                            startValue.right - sourceHintRect.right,
+                            startValue.bottom - sourceHintRect.bottom)
+                    : null;
+            final Rect sourceInsets = new Rect(0, 0, 0, 0);
+
             // construct new Rect instances in case they are recycled
             return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
                     endValue, new Rect(startValue), new Rect(endValue)) {
-                private final Rect mTmpRect = new Rect();
-
-                private int getCastedFractionValue(float start, float end, float fraction) {
-                    return (int) (start * (1 - fraction) + end * fraction + .5f);
-                }
+                private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
+                private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
 
                 @Override
                 void applySurfaceControlTransaction(SurfaceControl leash,
                         SurfaceControl.Transaction tx, float fraction) {
                     final Rect start = getStartValue();
                     final Rect end = getEndValue();
-                    mTmpRect.set(
-                            getCastedFractionValue(start.left, end.left, fraction),
-                            getCastedFractionValue(start.top, end.top, fraction),
-                            getCastedFractionValue(start.right, end.right, fraction),
-                            getCastedFractionValue(start.bottom, end.bottom, fraction));
-                    setCurrentValue(mTmpRect);
+                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
+                    setCurrentValue(bounds);
                     if (inScaleTransition()) {
                         if (isOutPipDirection(getTransitionDirection())) {
-                            getSurfaceTransactionHelper().scale(tx, leash, end, mTmpRect);
+                            getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
                         } else {
-                            getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
+                            getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
                         }
                     } else {
-                        getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
+                        if (sourceHintRectInsets != null) {
+                            Rect insets = mInsetsEvaluator.evaluate(fraction, sourceInsets,
+                                    sourceHintRectInsets);
+                            getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialStartValue,
+                                    bounds, insets);
+                        } else {
+                            getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
+                        }
                     }
                     tx.apply();
                 }
@@ -400,11 +410,11 @@
 
                 @Override
                 void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
-                    if (!inScaleTransition()) return;
                     // NOTE: intentionally does not apply the transaction here.
                     // this end transaction should get executed synchronously with the final
                     // WindowContainerTransaction in task organizer
-                    getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
+                    getSurfaceTransactionHelper()
+                            .resetScale(tx, leash, getDestinationBounds())
                             .crop(tx, leash, getDestinationBounds());
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 0d3a16e..8bbd15b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -289,6 +289,24 @@
     }
 
     /**
+     * Updatest the display info and display layout on rotation change. This is needed even when we
+     * aren't in PIP because the rotation layout is used to calculate the proper insets for the
+     * next enter animation into PIP.
+     */
+    public void onDisplayRotationChangedNotInPip(int toRotation) {
+        // Update the display layout, note that we have to do this on every rotation even if we
+        // aren't in PIP since we need to update the display layout to get the right resources
+        mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
+
+        // Populate the new {@link #mDisplayInfo}.
+        // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
+        // therefore, the width/height may require a swap first.
+        // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
+        mDisplayInfo.rotation = toRotation;
+        updateDisplayInfoIfNeeded();
+    }
+
+    /**
      * Updates the display info, calculating and returning the new stack and movement bounds in the
      * new orientation of the device if necessary.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
index fc41d2e..65ea887 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
@@ -44,6 +44,7 @@
     private final float[] mTmpFloat9 = new float[9];
     private final RectF mTmpSourceRectF = new RectF();
     private final RectF mTmpDestinationRectF = new RectF();
+    private final Rect mTmpDestinationRect = new Rect();
 
     @Inject
     public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) {
@@ -90,7 +91,30 @@
         mTmpDestinationRectF.set(destinationBounds);
         mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
-                .setPosition(leash, destinationBounds.left, destinationBounds.top);
+                .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
+        return this;
+    }
+
+    /**
+     * Operates the scale (setMatrix) on a given transaction and leash
+     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+     */
+    PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash,
+            Rect sourceBounds, Rect destinationBounds, Rect insets) {
+        mTmpSourceRectF.set(sourceBounds);
+        mTmpDestinationRect.set(sourceBounds);
+        mTmpDestinationRect.inset(insets);
+        // Scale by the shortest edge and offset such that the top/left of the scaled inset source
+        // rect aligns with the top/left of the destination bounds
+        final float scale = sourceBounds.width() <= sourceBounds.height()
+                ? (float) destinationBounds.width() / sourceBounds.width()
+                : (float) destinationBounds.height() / sourceBounds.height();
+        final float left = destinationBounds.left - insets.left * scale;
+        final float top = destinationBounds.top - insets.top * scale;
+        mTmpTransform.setScale(scale, scale);
+        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+                .setWindowCrop(leash, mTmpDestinationRect)
+                .setPosition(leash, left, top);
         return this;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index c8a1ca0..0141dee 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -143,8 +143,10 @@
             case MSG_RESIZE_ANIMATE: {
                 Rect currentBounds = (Rect) args.arg2;
                 Rect toBounds = (Rect) args.arg3;
+                Rect sourceHintRect = (Rect) args.arg4;
                 int duration = args.argi2;
-                animateResizePip(currentBounds, toBounds, args.argi1 /* direction */, duration);
+                animateResizePip(currentBounds, toBounds, sourceHintRect,
+                        args.argi1 /* direction */, duration);
                 if (updateBoundsCallback != null) {
                     updateBoundsCallback.accept(toBounds);
                 }
@@ -307,7 +309,8 @@
                 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                     t.apply();
                     scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
-                            direction, animationDurationMs, null /* updateBoundsCallback */);
+                            null /* sourceHintRect */, direction, animationDurationMs,
+                            null /* updateBoundsCallback */);
                     mInPip = false;
                 }
             });
@@ -380,7 +383,8 @@
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
 
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-            scheduleAnimateResizePip(currentBounds, destinationBounds,
+            final Rect sourceHintRect = getValidSourceHintRect(info, currentBounds);
+            scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect,
                     TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
                     null /* updateBoundsCallback */);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
@@ -391,6 +395,21 @@
         }
     }
 
+    /**
+     * Returns the source hint rect if it is valid (if provided and is contained by the current
+     * task bounds).
+     */
+    private Rect getValidSourceHintRect(ActivityManager.RunningTaskInfo info, Rect sourceBounds) {
+        final Rect sourceHintRect = info.pictureInPictureParams != null
+                && info.pictureInPictureParams.hasSourceBoundsHint()
+                ? info.pictureInPictureParams.getSourceRectHint()
+                : null;
+        if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
+            return sourceHintRect;
+        }
+        return null;
+    }
+
     private void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) {
         // If we are fading the PIP in, then we should move the pip to the final location as
         // soon as possible, but set the alpha immediately since the transaction can take a
@@ -611,13 +630,13 @@
             Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
             return;
         }
-        scheduleAnimateResizePip(mLastReportedBounds, toBounds,
+        scheduleAnimateResizePip(mLastReportedBounds, toBounds, null /* sourceHintRect */,
                 TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
     }
 
     private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
-            @PipAnimationController.TransitionDirection int direction, int durationMs,
-            Consumer<Rect> updateBoundsCallback) {
+            Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
+            int durationMs, Consumer<Rect> updateBoundsCallback) {
         if (!mInPip) {
             // TODO: tend to use shouldBlockResizeRequest here as well but need to consider
             // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
@@ -629,6 +648,7 @@
         args.arg1 = updateBoundsCallback;
         args.arg2 = currentBounds;
         args.arg3 = destinationBounds;
+        args.arg4 = sourceHintRect;
         args.argi1 = direction;
         args.argi2 = durationMs;
         mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
@@ -732,7 +752,8 @@
         }
         final Rect destinationBounds = new Rect(originalBounds);
         destinationBounds.offset(xOffset, yOffset);
-        animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
+        animateResizePip(originalBounds, destinationBounds, null /* sourceHintRect */,
+                TRANSITION_DIRECTION_SAME, durationMs);
     }
 
     private void resizePip(Rect destinationBounds) {
@@ -838,7 +859,8 @@
         return WINDOWING_MODE_UNDEFINED;
     }
 
-    private void animateResizePip(Rect currentBounds, Rect destinationBounds,
+
+    private void animateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect,
             @PipAnimationController.TransitionDirection int direction, int durationMs) {
         if (Looper.myLooper() != mUpdateHandler.getLooper()) {
             throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
@@ -850,7 +872,7 @@
             return;
         }
         mPipAnimationController
-                .getAnimator(mLeash, currentBounds, destinationBounds)
+                .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect)
                 .setTransitionDirection(direction)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(durationMs)
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 40a86b7..75d3d04 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -96,7 +96,9 @@
     private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
             int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
         if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
-            // Skip if we aren't in PIP or haven't actually entered PIP yet
+            // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update
+            // the display layout in the bounds handler in this case.
+            mPipBoundsHandler.onDisplayRotationChangedNotInPip(toRotation);
             return;
         }
         // If there is an animation running (ie. from a shelf offset), then ensure that we calculate
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index a4edace..1ca53f9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -55,7 +55,9 @@
 import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.util.DeviceConfigProxy;
 
+import java.io.PrintWriter;
 import java.util.concurrent.Executor;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 /**
@@ -94,7 +96,7 @@
     private final Rect mTmpBottomLeftCorner = new Rect();
     private final Rect mTmpBottomRightCorner = new Rect();
     private final Rect mDisplayBounds = new Rect();
-    private final Supplier<Rect> mMovementBoundsSupplier;
+    private final Function<Rect, Rect> mMovementBoundsSupplier;
     private final Runnable mUpdateMovementBoundsRunnable;
 
     private int mDelta;
@@ -113,7 +115,7 @@
 
     public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
             PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
-            PipTaskOrganizer pipTaskOrganizer, Supplier<Rect> movementBoundsSupplier,
+            PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
             Runnable updateMovementBoundsRunnable, SysUiState sysUiState) {
         mContext = context;
         mDisplayId = context.getDisplayId();
@@ -244,10 +246,15 @@
         return mTmpRegion.contains(x, y);
     }
 
+    public boolean willStartResizeGesture(MotionEvent ev) {
+        return mEnableUserResize && isInValidSysUiState()
+                && isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY());
+    }
+
     private void setCtrlType(int x, int y) {
         final Rect currentPipBounds = mMotionHelper.getBounds();
 
-        Rect movementBounds = mMovementBoundsSupplier.get();
+        Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
         mDisplayBounds.set(movementBounds.left,
                 movementBounds.top,
                 movementBounds.right + currentPipBounds.width(),
@@ -353,6 +360,16 @@
         mMinSize.set(minX, minY);
     }
 
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mAllowGesture=" + mAllowGesture);
+        pw.println(innerPrefix + "mIsAttached=" + mIsAttached);
+        pw.println(innerPrefix + "mIsEnabled=" + mIsEnabled);
+        pw.println(innerPrefix + "mEnableUserResize=" + mEnableUserResize);
+        pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
+    }
+
     class SysUiInputEventReceiver extends BatchedInputEventReceiver {
         SysUiInputEventReceiver(InputChannel channel, Looper looper) {
             super(channel, looper, Choreographer.getSfInstance());
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 79f99f4..b6e4e16 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -303,8 +303,11 @@
                     hideDismissTarget();
                 });
 
-                MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
-                        PipUtils.getTopPipActivity(mContext, mActivityManager));
+                Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
+                        mActivityManager);
+                if (topPipActivity.first != null) {
+                    MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, topPipActivity);
+                }
             }
         });
 
@@ -641,12 +644,12 @@
         }
 
         MotionEvent ev = (MotionEvent) inputEvent;
-        if (!mTouchState.isDragging()
-                && !mMagnetizedPip.getObjectStuckToTarget()
-                && !mMotionHelper.isAnimating()
-                && mPipResizeGestureHandler.isWithinTouchRegion(
-                        (int) ev.getRawX(), (int) ev.getRawY())) {
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
+                && mPipResizeGestureHandler.willStartResizeGesture(ev)) {
+            // Initialize the touch state for the gesture, but immediately reset to invalidate the
+            // gesture
             mTouchState.onTouchEvent(ev);
+            mTouchState.reset();
             return true;
         }
 
@@ -1029,8 +1032,11 @@
                 isMenuExpanded  && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
     }
 
-    private Rect getMovementBounds() {
-        return mMovementBounds;
+    private Rect getMovementBounds(Rect curBounds) {
+        Rect movementBounds = new Rect();
+        mSnapAlgorithm.getMovementBounds(curBounds, mInsetBounds,
+                movementBounds, mIsImeShowing ? mImeHeight : 0);
+        return movementBounds;
     }
 
     /**
@@ -1062,6 +1068,9 @@
         pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
         mTouchState.dump(pw, innerPrefix);
         mMotionHelper.dump(pw, innerPrefix);
+        if (mPipResizeGestureHandler != null) {
+            mPipResizeGestureHandler.dump(pw, innerPrefix);
+        }
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 8e878dd..d6e1a16 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -18,6 +18,7 @@
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
@@ -72,6 +73,7 @@
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
@@ -87,6 +89,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.ArrayList;
@@ -220,6 +223,10 @@
 
     private MediaActionSound mCameraSound;
 
+    private int mNavMode;
+    private int mLeftInset;
+    private int mRightInset;
+
     // standard material ease
     private final Interpolator mFastOutSlowIn;
 
@@ -301,6 +308,15 @@
         mDismissButton.getBoundsOnScreen(dismissRect);
         touchRegion.op(dismissRect, Region.Op.UNION);
 
+        if (QuickStepContract.isGesturalMode(mNavMode)) {
+            // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
+            Rect inset = new Rect(0, 0, mLeftInset, mDisplayMetrics.heightPixels);
+            touchRegion.op(inset, Region.Op.UNION);
+            inset.set(mDisplayMetrics.widthPixels - mRightInset, 0, mDisplayMetrics.widthPixels,
+                    mDisplayMetrics.heightPixels);
+            touchRegion.op(inset, Region.Op.UNION);
+        }
+
         inoutInfo.touchableRegion.set(touchRegion);
     }
 
@@ -356,6 +372,9 @@
         if (needsUpdate) {
             reloadAssets();
         }
+
+        mNavMode = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_navBarInteractionMode);
     }
 
     /**
@@ -370,6 +389,25 @@
 
         // Inflate the screenshot layout
         mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
+        // TODO(159460485): Remove this when focus is handled properly in the system
+        mScreenshotLayout.setOnTouchListener((v, event) -> {
+            if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
+                // Once the user touches outside, stop listening for input
+                setWindowFocusable(false);
+            }
+            return false;
+        });
+        mScreenshotLayout.setOnApplyWindowInsetsListener((v, insets) -> {
+            if (QuickStepContract.isGesturalMode(mNavMode)) {
+                Insets gestureInsets = insets.getInsets(
+                        WindowInsets.Type.systemGestures());
+                mLeftInset = gestureInsets.left;
+                mRightInset = gestureInsets.right;
+            } else {
+                mLeftInset = mRightInset = 0;
+            }
+            return mScreenshotLayout.onApplyWindowInsets(insets);
+        });
         mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() {
             @Override
             public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -432,6 +470,21 @@
         }
     }
 
+    /**
+     * Updates the window focusability.  If the window is already showing, then it updates the
+     * window immediately, otherwise the layout params will be applied when the window is next
+     * shown.
+     */
+    private void setWindowFocusable(boolean focusable) {
+        if (focusable) {
+            mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
+        } else {
+            mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
+        }
+        if (mScreenshotLayout.isAttachedToWindow()) {
+            mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
+        }
+    }
 
     /**
      * Creates a new worker thread and saves the screenshot to the media store.
@@ -500,6 +553,10 @@
         if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
             mDismissAnimation.cancel();
         }
+
+        // The window is focusable by default
+        setWindowFocusable(true);
+
         // Start the post-screenshot animation
         startAnimation(finisher, screenRect, screenInsets, showFlash);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index b846aa0..eca4c80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -316,7 +316,7 @@
          * of the timer and should be removed externally.
          * @return true if the notification is sticky
          */
-        protected boolean isSticky() {
+        public boolean isSticky() {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index c1acfbad..285cf7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -382,9 +382,8 @@
 
         final NotificationEntry entry = mNotificationSet.get(sbn.getKey());
         if (entry == null) {
-            crashIfNotInitializing(
-                    new IllegalStateException("No notification to remove with key "
-                            + sbn.getKey()));
+            // TODO (b/160008901): Throw an exception here
+            mLogger.logNoNotificationToRemoveWithKey(sbn.getKey());
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 76751ea..f8a778d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -121,6 +121,14 @@
         })
     }
 
+    fun logNoNotificationToRemoveWithKey(key: String) {
+        buffer.log(TAG, ERROR, {
+            str1 = key
+        }, {
+            "No notification to remove with key $str1"
+        })
+    }
+
     fun logRankingMissing(key: String, rankingMap: RankingMap) {
         buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
         buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 52f7c2c..7bd192d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -124,6 +124,9 @@
      */
     Drawable resolveImage(Uri uri) throws IOException {
         BitmapDrawable image = resolveImageInternal(uri);
+        if (image == null || image.getBitmap() == null) {
+            throw new IOException("resolveImageInternal returned null for uri: " + uri);
+        }
         Bitmap bitmap = image.getBitmap();
         image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight));
         return image;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 3dcf7ed..e05ba12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -428,7 +428,7 @@
 
 
         @Override
-        protected boolean isSticky() {
+        public boolean isSticky() {
             return super.isSticky() || mMenuShownPinned;
         }
 
@@ -568,6 +568,17 @@
                 }
                 mKeysToRemoveWhenLeavingKeyguard.clear();
             }
+            if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) {
+                ArrayList<String> keysToRemove = new ArrayList<>();
+                for (AlertEntry entry : mAlertEntries.values()) {
+                    if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) {
+                        keysToRemove.add(entry.mEntry.getKey());
+                    }
+                }
+                for (String key : keysToRemove) {
+                    removeAlertEntry(key);
+                }
+            }
         }
 
         @Override
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 c321331..375af6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -435,6 +435,11 @@
     private Runnable mExpandAfterLayoutRunnable;
 
     /**
+     * Is this a collapse that started on the panel where we should allow the panel to intercept
+     */
+    private boolean mIsPanelCollapseOnQQS;
+
+    /**
      * If face auth with bypass is running for the first time after you turn on the screen.
      * (From aod or screen off)
      */
@@ -1064,7 +1069,11 @@
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
-                if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                if (mKeyguardShowing
+                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                    // Dragging down on the lockscreen statusbar should prohibit other interactions
+                    // immediately, otherwise we'll wait on the touchslop. This is to allow
+                    // dragging down to expanded quick settings directly on the lockscreen.
                     mView.getParent().requestDisallowInterceptTouchEvent(true);
                 }
                 if (mQsExpansionAnimator != null) {
@@ -1097,9 +1106,10 @@
                     trackMovement(event);
                     return true;
                 }
-                if (Math.abs(h) > getTouchSlop(event)
+                if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded))
                         && Math.abs(h) > Math.abs(x - mInitialTouchX)
                         && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
+                    mView.getParent().requestDisallowInterceptTouchEvent(true);
                     mQsTracking = true;
                     onQsExpansionStarted();
                     notifyExpandingFinished();
@@ -1139,6 +1149,7 @@
             mDownX = event.getX();
             mDownY = event.getY();
             mCollapsedOnDown = isFullyCollapsed();
+            mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY);
             mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
             mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
             mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
@@ -1154,6 +1165,24 @@
         }
     }
 
+    /**
+     * Can the panel collapse in this motion because it was started on QQS?
+     *
+     * @param downX the x location where the touch started
+     * @param downY the y location where the touch started
+     *
+     * @return true if the panel could be collapsed because it stared on QQS
+     */
+    private boolean canPanelCollapseOnQQS(float downX, float downY) {
+        if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) {
+            return false;
+        }
+        View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+        return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
+                        && downY <= header.getBottom();
+
+    }
+
     private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
         float vel = getCurrentQSVelocity();
         final boolean expandsQs = flingExpandsQs(vel);
@@ -1903,10 +1932,11 @@
     }
 
     @Override
-    protected boolean isScrolledToBottom() {
+    protected boolean canCollapsePanelOnTouch() {
         if (!isInSettings()) {
             return mBarState == StatusBarState.KEYGUARD
-                    || mNotificationStackScroller.isScrolledToBottom();
+                    || mNotificationStackScroller.isScrolledToBottom()
+                    || mIsPanelCollapseOnQQS;
         } else {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index caddc4a..732f25f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -460,7 +460,7 @@
         }
     }
 
-    protected boolean isScrolledToBottom() {
+    protected boolean canCollapsePanelOnTouch() {
         return true;
     }
 
@@ -1081,7 +1081,7 @@
              * upwards. This allows closing the shade from anywhere inside the panel.
              *
              * We only do this if the current content is scrolled to the bottom,
-             * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling
+             * i.e canCollapsePanelOnTouch() is true and therefore there is no conflicting scrolling
              * gesture
              * possible.
              */
@@ -1092,7 +1092,7 @@
             }
             final float x = event.getX(pointerIndex);
             final float y = event.getY(pointerIndex);
-            boolean scrolledToBottom = isScrolledToBottom();
+            boolean canCollapsePanel = canCollapsePanelOnTouch();
 
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
@@ -1139,7 +1139,7 @@
                 case MotionEvent.ACTION_MOVE:
                     final float h = y - mInitialTouchY;
                     addMovement(event);
-                    if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
+                    if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) {
                         float hAbs = Math.abs(h);
                         float touchSlop = getTouchSlop(event);
                         if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 0d5a149..07de388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -368,7 +368,7 @@
         protected boolean expanded;
 
         @Override
-        protected boolean isSticky() {
+        public boolean isSticky() {
             return (mEntry.isRowPinned() && expanded)
                     || remoteInputActive || hasFullScreenIntent(mEntry);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index b7a2633..536cae4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -82,7 +82,7 @@
     @Test
     public void getAnimator_withBounds_returnBoundsAnimator() {
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, new Rect(), new Rect());
+                .getAnimator(mLeash, new Rect(), new Rect(), null);
 
         assertEquals("Expect ANIM_TYPE_BOUNDS animation",
                 animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -94,12 +94,12 @@
         final Rect endValue1 = new Rect(100, 100, 200, 200);
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
-                .getAnimator(mLeash, startValue, endValue1);
+                .getAnimator(mLeash, startValue, endValue1, null);
         oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
         oldAnimator.start();
 
         final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
-                .getAnimator(mLeash, startValue, endValue2);
+                .getAnimator(mLeash, startValue, endValue2, null);
 
         assertEquals("getAnimator with same type returns same animator",
                 oldAnimator, newAnimator);
@@ -129,7 +129,7 @@
         final Rect endValue1 = new Rect(100, 100, 200, 200);
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, startValue, endValue1);
+                .getAnimator(mLeash, startValue, endValue1, null);
 
         animator.updateEndValue(endValue2);
 
@@ -141,7 +141,7 @@
         final Rect startValue = new Rect(0, 0, 100, 100);
         final Rect endValue = new Rect(100, 100, 200, 200);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, startValue, endValue);
+                .getAnimator(mLeash, startValue, endValue, null);
         animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
 
         animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 363fe95..359faba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -1273,8 +1273,8 @@
         verify(mInterceptor3, never()).shouldInterceptDismissal(clearable);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testClearNotificationThrowsIfMissing() {
+    @Test
+    public void testClearNotificationDoesntThrowIfMissing() {
         // GIVEN that enough time has passed that we're beyond the forgiveness window
         mClock.advanceTime(5001);
 
@@ -1287,7 +1287,8 @@
                 container.getSbn(),
                 new RankingMap(new Ranking[]{ container.getRanking() }));
 
-        // THEN an exception is thrown
+        // THEN the event is ignored
+        verify(mCollectionListener, never()).onEntryRemoved(any(NotificationEntry.class), anyInt());
     }
 
     @Test
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d2b1bd1..499a271 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -251,7 +251,7 @@
     //TODO: Remove this hack
     private boolean mInitialized;
 
-    private Point mTempPoint;
+    private Point mTempPoint = new Point();
     private boolean mIsAccessibilityButtonShown;
 
     private AccessibilityUserState getCurrentUserStateLocked() {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 373d47e..6f2e626 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -309,14 +309,9 @@
 
     @Override
     public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        // Try to use the standard accessibility API to long click
-        if (!mAms.performActionOnAccessibilityFocusedItem(
-                AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)) {
-            Slog.e(LOG_TAG, "ACTION_LONG_CLICK failed.");
-            if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
-                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
-                mState.startDelegating();
-            }
+        if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
+            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+            mState.startDelegating();
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9b3d075..7ab4369 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -73,6 +73,7 @@
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
 import android.service.autofill.InternalSanitizer;
 import android.service.autofill.InternalValidator;
 import android.service.autofill.SaveInfo;
@@ -1437,7 +1438,10 @@
                     mClientState = newClientState;
                 }
                 final Dataset dataset = (Dataset) result;
-                authenticatedResponse.getDatasets().set(datasetIdx, dataset);
+                final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx);
+                if (!isPinnedDataset(oldDataset)) {
+                    authenticatedResponse.getDatasets().set(datasetIdx, dataset);
+                }
                 autoFill(requestId, datasetIdx, dataset, false);
             } else {
                 Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id "
@@ -1455,6 +1459,27 @@
         }
     }
 
+    /**
+     * A dataset can potentially have multiple fields, and it's possible that some of the fields'
+     * has inline presentation and some don't. It's also possible that some of the fields'
+     * inline presentation is pinned and some isn't. So the concept of whether a dataset is
+     * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a
+     * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient
+     * for most of the cases.
+     */
+    private static boolean isPinnedDataset(@Nullable Dataset dataset) {
+        if (dataset != null && dataset.getFieldIds() != null) {
+            final int numOfFields = dataset.getFieldIds().size();
+            for (int i = 0; i < numOfFields; i++) {
+                final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(i);
+                if (inlinePresentation != null && inlinePresentation.isPinned()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     @GuardedBy("mLock")
     void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) {
         final Dataset dataset = (data == null) ? null :
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index ef81d71..29bb542 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -34,6 +34,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -509,6 +510,14 @@
     }
 
     @Override
+    public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
+            ParcelFileDescriptor err, String[] args) {
+        return new AdbShellCommand(this).exec(
+                this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+                args);
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
 
diff --git a/services/core/java/com/android/server/adb/AdbShellCommand.java b/services/core/java/com/android/server/adb/AdbShellCommand.java
new file mode 100644
index 0000000..7691852
--- /dev/null
+++ b/services/core/java/com/android/server/adb/AdbShellCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.server.adb;
+
+import android.os.BasicShellCommandHandler;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * Interprets and executes 'adb shell cmd adb [args]'.
+ */
+class AdbShellCommand extends BasicShellCommandHandler {
+
+    private final AdbService mService;
+
+    AdbShellCommand(AdbService service) {
+        mService = Objects.requireNonNull(service);
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(null);
+        }
+
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "is-wifi-supported": {
+                pw.println(Boolean.toString(mService.isAdbWifiSupported()));
+                return 0;
+            }
+            case "is-wifi-qr-supported": {
+                pw.println(Boolean.toString(mService.isAdbWifiQrSupported()));
+                return 0;
+            }
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Adb service commands:");
+        pw.println("  help or -h");
+        pw.println("    Print this help text.");
+        pw.println("  is-wifi-supported");
+        pw.println("    Returns \"true\" if adb over wifi is supported.");
+        pw.println("  is-wifi-qr-supported");
+        pw.println("    Returns \"true\" if adb over wifi + QR pairing is supported.");
+        pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c9dbacd..a38d42b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18284,11 +18284,15 @@
             }
         }
 
-        // Now safely dispatch changes to device idle controller.
-        for (int i = 0; i < N; i++) {
-            PendingTempWhitelist ptw = list[i];
-            mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
-                    ptw.duration, true, ptw.tag);
+        // Now safely dispatch changes to device idle controller.  Skip this if we're early
+        // in boot and the controller hasn't yet been brought online:  we do not apply
+        // device idle policy anyway at this phase.
+        if (mLocalDeviceIdleController != null) {
+            for (int i = 0; i < N; i++) {
+                PendingTempWhitelist ptw = list[i];
+                mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
+                        ptw.duration, true, ptw.tag);
+            }
         }
 
         // And now we can safely remove them from the map.
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 1cc41b2..5124c4a 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -904,6 +904,10 @@
         } else if (r.intent.getData() != null) {
             b.append(r.intent.getData());
         }
+        if (DEBUG_BROADCAST) {
+            Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration
+                    + " : " + b.toString());
+        }
         mService.tempWhitelistUidLocked(uid, duration, b.toString());
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 088c5da..670b88e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15173,8 +15173,13 @@
             idleController.addPowerSaveTempWhitelistAppDirect(Process.myUid(),
                      idleDuration,
                     false, "integrity component");
+            final BroadcastOptions options = BroadcastOptions.makeBasic();
+            options.setTemporaryAppWhitelistDuration(idleDuration);
+
             mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM,
                     /* receiverPermission= */ null,
+                    /* appOp= */ AppOpsManager.OP_NONE,
+                    /* options= */ options.toBundle(),
                     new BroadcastReceiver() {
                         @Override
                         public void onReceive(Context context, Intent intent) {
@@ -15274,6 +15279,8 @@
                 DeviceIdleInternal idleController =
                         mInjector.getLocalDeviceIdleController();
                 final long idleDuration = getVerificationTimeout();
+                final BroadcastOptions options = BroadcastOptions.makeBasic();
+                options.setTemporaryAppWhitelistDuration(idleDuration);
 
                 /*
                  * If any sufficient verifiers were listed in the package
@@ -15293,7 +15300,9 @@
 
                             final Intent sufficientIntent = new Intent(verification);
                             sufficientIntent.setComponent(verifierComponent);
-                            mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
+                            mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
+                                    /* receiverPermission= */ null,
+                                    options.toBundle());
                         }
                     }
                 }
@@ -15312,6 +15321,8 @@
                             verifierUser.getIdentifier(), false, "package verifier");
                     mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
                             android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+                            /* appOp= */ AppOpsManager.OP_NONE,
+                            /* options= */ options.toBundle(),
                             new BroadcastReceiver() {
                                 @Override
                                 public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c4663ca..2e9f704 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3146,11 +3146,11 @@
     }
 
     @Override
-    boolean checkCompleteDeferredRemoval() {
+    boolean handleCompleteDeferredRemoval() {
         if (mIsExiting) {
             removeIfPossible();
         }
-        return super.checkCompleteDeferredRemoval();
+        return super.handleCompleteDeferredRemoval();
     }
 
     void onRemovedFromDisplay() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index b4bc0f5..8f0de73 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -86,7 +86,6 @@
 import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
 import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
-import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
 import static com.android.server.wm.TaskProto.DISPLAY_ID;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
 import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
@@ -248,11 +247,6 @@
     private Rect mTmpRect = new Rect();
     private Rect mTmpRect2 = new Rect();
 
-    /** Detach this stack from its display when animation completes. */
-    // TODO: maybe tie this to WindowContainer#removeChild some how...
-    // TODO: This is no longer set. Okay to remove or was the set removed by accident?
-    private boolean mDeferRemoval;
-
     // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
     // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
     // would otherwise apply while resizing, while resizing in the bounds animating mode.
@@ -3232,9 +3226,6 @@
 
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
-        if (mDeferRemoval) {
-            pw.println(prefix + "mDeferRemoval=true");
-        }
         super.dump(pw, prefix, dumpAll);
         if (!mExitingActivities.isEmpty()) {
             pw.println();
@@ -3304,15 +3295,12 @@
     }
 
     /** Returns true if a removal action is still being deferred. */
-    boolean checkCompleteDeferredRemoval() {
+    boolean handleCompleteDeferredRemoval() {
         if (isAnimating(TRANSITION | CHILDREN)) {
             return true;
         }
-        if (mDeferRemoval) {
-            removeImmediately();
-        }
 
-        return super.checkCompleteDeferredRemoval();
+        return super.handleCompleteDeferredRemoval();
     }
 
     public DisplayInfo getDisplayInfo() {
@@ -3380,7 +3368,6 @@
             mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
         }
 
-        proto.write(DEFER_REMOVAL, mDeferRemoval);
         proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
 
         if (mSurfaceControl != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 241de2e..c564407 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2739,6 +2739,7 @@
     @Override
     void removeImmediately() {
         mRemovingDisplay = true;
+        mDeferredRemoval = false;
         try {
             if (mParentWindow != null) {
                 mParentWindow.removeEmbeddedDisplayContent(this);
@@ -2771,31 +2772,14 @@
 
     /** Returns true if a removal action is still being deferred. */
     @Override
-    boolean checkCompleteDeferredRemoval() {
-        boolean stillDeferringRemoval = false;
-
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final DisplayChildWindowContainer child = getChildAt(i);
-            stillDeferringRemoval |= child.checkCompleteDeferredRemoval();
-            if (getChildCount() == 0) {
-                // If this display is pending to be removed because it contains an activity with
-                // {@link ActivityRecord#mIsExiting} is true, this display may be removed when
-                // completing the removal of the last activity from
-                // {@link ActivityRecord#checkCompleteDeferredRemoval}.
-                return false;
-            }
-        }
+    boolean handleCompleteDeferredRemoval() {
+        final boolean stillDeferringRemoval = super.handleCompleteDeferredRemoval();
 
         if (!stillDeferringRemoval && mDeferredRemoval) {
             removeImmediately();
             return false;
         }
-        return true;
-    }
-
-    /** @return 'true' if removal of this display content is deferred due to active animation. */
-    boolean isRemovalDeferred() {
-        return mDeferredRemoval;
+        return stillDeferringRemoval;
     }
 
     void adjustForImeIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 1b58fc1..851b533 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1426,8 +1426,8 @@
     private void removeUnreachableHiddenTasks(int windowingMode) {
         for (int i = mHiddenTasks.size() - 1; i >= 0; i--) {
             final Task hiddenTask = mHiddenTasks.get(i);
-            if (!hiddenTask.hasChild()) {
-                // The task was removed by other path.
+            if (!hiddenTask.hasChild() || hiddenTask.inRecents) {
+                // The task was removed by other path or it became reachable (added to recents).
                 mHiddenTasks.remove(i);
                 continue;
             }
@@ -1449,6 +1449,9 @@
      * of task as the given one.
      */
     private void removeForAddTask(Task task) {
+        // The adding task will be in recents so it is not hidden.
+        mHiddenTasks.remove(task);
+
         final int removeIndex = findRemoveIndexForAddTask(task);
         if (removeIndex == -1) {
             // Nothing to trim
@@ -1460,8 +1463,6 @@
         // callbacks here.
         final Task removedTask = mTasks.remove(removeIndex);
         if (removedTask != task) {
-            // The added task is in recents so it is not hidden.
-            mHiddenTasks.remove(task);
             if (removedTask.hasChild()) {
                 // A non-empty task is replaced by a new task. Because the removed task is no longer
                 // managed by the recent tasks list, add it to the hidden list to prevent the task
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2d30b73..119b188 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -990,9 +990,7 @@
         }
 
         // Remove all deferred displays stacks, tasks, and activities.
-        for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
-            mChildren.get(displayNdx).checkCompleteDeferredRemoval();
-        }
+        handleCompleteDeferredRemoval();
 
         forAllDisplays(dc -> {
             dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3360951..205a8d2 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1746,10 +1746,15 @@
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
-        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = getStackAt(stackNdx);
-            stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                    notifyClients);
+        mAtmService.mStackSupervisor.beginActivityVisibilityUpdate();
+        try {
+            for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = getStackAt(stackNdx);
+                stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
+                        notifyClients);
+            }
+        } finally {
+            mAtmService.mStackSupervisor.endActivityVisibilityUpdate();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2977968..5a73fab 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1044,13 +1044,25 @@
         return mChildren.peekLast();
     }
 
-    /** Returns true if there is still a removal being deferred */
-    boolean checkCompleteDeferredRemoval() {
+    /**
+     * Removes the containers which were deferred.
+     *
+     * @return {@code true} if there is still a removal being deferred.
+     */
+    boolean handleCompleteDeferredRemoval() {
         boolean stillDeferringRemoval = false;
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
-            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
+            stillDeferringRemoval |= wc.handleCompleteDeferredRemoval();
+            if (!hasChild()) {
+                // All child containers of current level could be removed from a removal of
+                // descendant. E.g. if a display is pending to be removed because it contains an
+                // activity with {@link ActivityRecord#mIsExiting} is true, the display may be
+                // removed when completing the removal of the last activity from
+                // {@link ActivityRecord#checkCompleteDeferredRemoval}.
+                return false;
+            }
         }
 
         return stillDeferringRemoval;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b1756b0..f4281fc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5606,17 +5606,28 @@
         }
 
         final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
-        final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
-        final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
-        if (waitingForConfig || mAppsFreezingScreen > 0
+        final int numOpeningApps;
+        final boolean waitingForConfig;
+        final boolean waitingForRemoteRotation;
+        if (displayContent != null) {
+            numOpeningApps = displayContent.mOpeningApps.size();
+            waitingForConfig = displayContent.mWaitingForConfig;
+            waitingForRemoteRotation =
+                    displayContent.getDisplayRotation().isWaitingForRemoteRotation();
+        } else {
+            waitingForConfig = waitingForRemoteRotation = false;
+            numOpeningApps = 0;
+        }
+        if (waitingForConfig || waitingForRemoteRotation || mAppsFreezingScreen > 0
                 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
                 || mClientFreezingScreen || numOpeningApps > 0) {
-            ProtoLog.d(WM_DEBUG_ORIENTATION,
-                                "stopFreezingDisplayLocked: Returning mWaitingForConfig=%b, "
-                                        + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
-                                        + "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
-                                waitingForConfig, mAppsFreezingScreen, mWindowsFreezingScreen,
-                                mClientFreezingScreen, numOpeningApps);
+            ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Returning "
+                    + "waitingForConfig=%b, waitingForRemoteRotation=%b, "
+                    + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
+                    + "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+                    waitingForConfig, waitingForRemoteRotation,
+                    mAppsFreezingScreen, mWindowsFreezingScreen,
+                    mClientFreezingScreen, numOpeningApps);
             return;
         }
 
@@ -5627,7 +5638,6 @@
         // We must make a local copy of the displayId as it can be potentially overwritten later on
         // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
         // of update rotation, but we reference the frozen display after that call in this method.
-        final int displayId = mFrozenDisplayId;
         mFrozenDisplayId = INVALID_DISPLAY;
         mDisplayFrozen = false;
         mInputManagerCallback.thawInputDispatchingLw();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f1acee5..d4d2f4d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -81,6 +81,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
@@ -2372,6 +2373,11 @@
             return false;
         }
 
+        if (mAttrs.type == TYPE_SCREENSHOT) {
+            // Disallow screenshot windows from being IME targets
+            return false;
+        }
+
         final boolean windowsAreFocusable = mActivityRecord == null || mActivityRecord.windowsAreFocusable();
         if (!windowsAreFocusable) {
             // This window can't be an IME target if the app's windows should not be focusable.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 30af1d3..ddb186a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1328,9 +1328,10 @@
 
         final DisplayRotation dr = dc.getDisplayRotation();
         doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
-        Mockito.doReturn(ROTATION_90).when(dr).rotationForOrientation(anyInt(), anyInt());
+        // Rotate 180 degree so the display doesn't have configuration change. This condition is
+        // used for the later verification of stop-freezing (without setting mWaitingForConfig).
+        doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt());
         final boolean[] continued = new boolean[1];
-        // TODO(display-merge): Remove cast
         doAnswer(
                 invocation -> {
                     continued[0] = true;
@@ -1356,9 +1357,16 @@
         dc.setRotationAnimation(null);
 
         mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */);
+        // If remote rotation is not finished, the display should not be able to unfreeze.
+        mWm.stopFreezingDisplayLocked();
+        assertTrue(mWm.mDisplayFrozen);
+
         assertTrue(called[0]);
         waitUntilHandlersIdle();
         assertTrue(continued[0]);
+
+        mWm.stopFreezingDisplayLocked();
+        assertFalse(mWm.mDisplayFrozen);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 5005c07..fd16901 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -464,15 +464,19 @@
         mRecentTasks.add(task1);
         final Task task2 = taskBuilder.apply(true /* visible */);
         mRecentTasks.add(task2);
-        // Only the last task is kept in recents and the previous 2 tasks will becomes untracked
+        final Task task3 = createTaskBuilder(className).build();
+        mRecentTasks.add(task3);
+        // Only the last added task is kept in recents and the previous 2 tasks will become hidden
         // tasks because their intents are identical.
-        mRecentTasks.add(createTaskBuilder(className).build());
+        mRecentTasks.add(task1);
         // Go home to trigger the removal of untracked tasks.
         mRecentTasks.add(createTaskBuilder(".Home").setStack(mTaskContainer.getRootHomeTask())
                 .build());
 
+        // The task was added into recents again so it is not hidden and shouldn't be removed.
+        assertNotNull(task1.getTopNonFinishingActivity());
         // All activities in the invisible task should be finishing or removed.
-        assertNull(task1.getTopNonFinishingActivity());
+        assertNull(task3.getTopNonFinishingActivity());
         // The visible task should not be affected.
         assertNotNull(task2.getTopNonFinishingActivity());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 87485ea..efc03df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -825,6 +826,31 @@
     }
 
     @Test
+    public void testHandleCompleteDeferredRemoval() {
+        final DisplayContent displayContent = createNewDisplay();
+        // Do not reparent activity to default display when removing the display.
+        doReturn(true).when(displayContent).shouldDestroyContentOnRemove();
+        final ActivityRecord r = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+                .setDisplay(displayContent).build().getTopMostActivity();
+        // Add a window and make the activity animating so the removal of activity is deferred.
+        createWindow(null, TYPE_BASE_APPLICATION, r, "win");
+        doReturn(true).when(r).isAnimating(anyInt(), anyInt());
+
+        displayContent.remove();
+        // Ensure that ActivityRecord#onRemovedFromDisplay is called.
+        r.destroyed("test");
+        // The removal is deferred, so the activity is still in the display.
+        assertEquals(r, displayContent.getTopMostActivity());
+
+        // Assume the animation is done so the deferred removal can continue.
+        doReturn(false).when(r).isAnimating(anyInt(), anyInt());
+
+        assertFalse(displayContent.handleCompleteDeferredRemoval());
+        assertFalse(displayContent.hasChild());
+        assertFalse(r.hasChild());
+    }
+
+    @Test
     public void testTaskCanApplyAnimation() {
         final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);