Merge "Add simulate-device-(dis)appeared CDM shell commands" into tm-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d6a067d..2c0b6e9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -800,6 +800,7 @@
     method public boolean hasRequestForegroundServiceExemption();
     method public boolean isPrivilegedApp();
     method public boolean isSystemApp();
+    method public void setEnableOnBackInvokedCallback(boolean);
     field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
     field public int privateFlags;
   }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c7c654a..3b1943b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2537,8 +2537,8 @@
      * restriction} for a certain app-op.
      */
     private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
-            null, //COARSE_LOCATION
-            null, //FINE_LOCATION
+            new RestrictionBypass(true, false, false), //COARSE_LOCATION
+            new RestrictionBypass(true, false, false), //FINE_LOCATION
             null, //GPS
             null, //VIBRATE
             null, //READ_CONTACTS
@@ -2547,7 +2547,7 @@
             null, //WRITE_CALL_LOG
             null, //READ_CALENDAR
             null, //WRITE_CALENDAR
-            new RestrictionBypass(true, false), //WIFI_SCAN
+            new RestrictionBypass(false, true, false), //WIFI_SCAN
             null, //POST_NOTIFICATION
             null, //NEIGHBORING_CELLS
             null, //CALL_PHONE
@@ -2561,10 +2561,10 @@
             null, //READ_ICC_SMS
             null, //WRITE_ICC_SMS
             null, //WRITE_SETTINGS
-            new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW
+            new RestrictionBypass(false, true, false), //SYSTEM_ALERT_WINDOW
             null, //ACCESS_NOTIFICATIONS
             null, //CAMERA
-            new RestrictionBypass(false, true), //RECORD_AUDIO
+            new RestrictionBypass(false, false, true), //RECORD_AUDIO
             null, //PLAY_AUDIO
             null, //READ_CLIPBOARD
             null, //WRITE_CLIPBOARD
@@ -2582,7 +2582,7 @@
             null, //MONITOR_HIGH_POWER_LOCATION
             null, //GET_USAGE_STATS
             null, //MUTE_MICROPHONE
-            new RestrictionBypass(true, false), //TOAST_WINDOW
+            new RestrictionBypass(false, true, false), //TOAST_WINDOW
             null, //PROJECT_MEDIA
             null, //ACTIVATE_VPN
             null, //WALLPAPER
@@ -2614,7 +2614,7 @@
             null, // ACCEPT_HANDOVER
             null, // MANAGE_IPSEC_HANDOVERS
             null, // START_FOREGROUND
-            new RestrictionBypass(true, false), // BLUETOOTH_SCAN
+            new RestrictionBypass(false, true, false), // BLUETOOTH_SCAN
             null, // USE_BIOMETRIC
             null, // ACTIVITY_RECOGNITION
             null, // SMS_FINANCIAL_TRANSACTIONS
@@ -3331,6 +3331,9 @@
      * @hide
      */
     public static class RestrictionBypass {
+        /** Does the app need to be system uid to bypass the restriction */
+        public boolean isSystemUid;
+
         /** Does the app need to be privileged to bypass the restriction */
         public boolean isPrivileged;
 
@@ -3340,12 +3343,14 @@
          */
         public boolean isRecordAudioRestrictionExcept;
 
-        public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) {
+        public RestrictionBypass(boolean isSystemUid, boolean isPrivileged,
+                boolean isRecordAudioRestrictionExcept) {
+            this.isSystemUid = isSystemUid;
             this.isPrivileged = isPrivileged;
             this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept;
         }
 
-        public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true);
+        public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(false, true, true);
     }
 
     /**
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 436eac3..7c83d58 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -45,7 +45,9 @@
  * referenced in the manifest via {@code android:localeConfig} on
  * {@code <application>}.
  *
- * For more information, see TODO(b/214154050): add link to guide
+ * <p>For more information, see
+ * <a href="https://developer.android.com/about/versions/13/features/app-languages#use-localeconfig">
+ * the section on per-app language preferences</a>.
  *
  * @attr ref android.R.styleable#LocaleConfig_Locale_name
  * @attr ref android.R.styleable#AndroidManifestApplication_localeConfig
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 974d20a..e820733 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7770,10 +7770,11 @@
      * user will always see the normal notification view.
      *
      * <p>
-     * If the app is targeting Android P and above, it is required to use the {@link Person}
-     * class in order to get an optimal rendering of the notification and its avatars. For
-     * conversations involving multiple people, the app should also make sure that it marks the
-     * conversation as a group with {@link #setGroupConversation(boolean)}.
+     * If the app is targeting Android {@link android.os.Build.VERSION_CODES#P} and above, it is
+     * required to use the {@link Person} class in order to get an optimal rendering of the
+     * notification and its avatars. For conversations involving multiple people, the app should
+     * also make sure that it marks the conversation as a group with
+     * {@link #setGroupConversation(boolean)}.
      *
      * <p>
      * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior.
@@ -7846,8 +7847,8 @@
          * @param user Required - The person displayed for any messages that are sent by the
          * user. Any messages added with {@link #addMessage(Notification.MessagingStyle.Message)}
          * who don't have a Person associated with it will be displayed as if they were sent
-         * by this user. The user also needs to have a valid name associated with it, which will
-         * be enforced starting in Android P.
+         * by this user. The user also needs to have a valid name associated with it, which is
+         * enforced starting in Android {@link android.os.Build.VERSION_CODES#P}.
          */
         public MessagingStyle(@NonNull Person user) {
             mUser = user;
@@ -7904,9 +7905,9 @@
         /**
          * Sets the title to be displayed on this conversation. May be set to {@code null}.
          *
-         * <p>Starting in {@link Build.VERSION_CODES#R, this conversation title will be ignored if a
-         * valid shortcutId is added via {@link Notification.Builder#setShortcutId(String)}. In this
-         * case, {@link ShortcutInfo#getLongLabel()} (or, if missing,
+         * <p>Starting in {@link Build.VERSION_CODES#R}, this conversation title will be ignored
+         * if a valid shortcutId is added via {@link Notification.Builder#setShortcutId(String)}.
+         * In this case, {@link ShortcutInfo#getLongLabel()} (or, if missing,
          * {@link ShortcutInfo#getShortLabel()}) will be shown as the conversation title
          * instead.
          *
@@ -8065,7 +8066,7 @@
         }
 
         /**
-         * Gets the list of {@code Message} objects that represent the notification
+         * Gets the list of {@code Message} objects that represent the notification.
          */
         public List<Message> getMessages() {
             return mMessages;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 315bd71..0a2b421 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1811,10 +1811,6 @@
      * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
      * #PROVISIONING_MODE_MANAGED_PROFILE} and {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
      *
-     * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
-     * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
-     * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
-     *
      * <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}. In
      * that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have
      * the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -1834,6 +1830,10 @@
      * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
      * contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}.
      *
+     * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
+     * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
+     * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
+     *
      * <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}. In
      * that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have the
      * {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -13249,8 +13249,9 @@
      * Called by a device owner, profile owner of a managed profile or delegated app with
      * {@link #DELEGATION_NETWORK_LOGGING} to control the network logging feature.
      *
-     * <p> When network logging is enabled by a profile owner, the network logs will only include
-     * work profile network activity, not activity on the personal profile.
+     * <p> Supported for a device owner from Android 8. Supported for a profile owner of a managed
+     * profile from Android 12. When network logging is enabled by a profile owner, the network logs
+     * will only include work profile network activity, not activity on the personal profile.
      *
      * <p> Network logs contain DNS lookup and connect() library call events. The following library
      *     functions are recorded while network logging is active:
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2961b55..24c3836 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2752,4 +2752,21 @@
             mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
         }
     }
+
+    /**
+     * Sets whether the application will use the {@link android.window.OnBackInvokedCallback}
+     * navigation system instead of the {@link android.view.KeyEvent#KEYCODE_BACK} and related
+     * callbacks. Intended to be used from tests only.
+     *
+     * @see #isOnBackInvokedCallbackEnabled()
+     * @hide
+     */
+    @TestApi
+    public void setEnableOnBackInvokedCallback(boolean isEnable) {
+        if (isEnable) {
+            privateFlagsExt |= PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+        } else {
+            privateFlagsExt &= ~PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+        }
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 44dc28d..52e64e8 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2618,6 +2618,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                targetCode)) {
+            Slog.w(TAG, "Package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + targetCode
@@ -2689,6 +2698,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                minCode)) {
+            Slog.w(TAG, "Package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 6d74b81..bde71bb 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -315,6 +315,15 @@
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        minCode)) {
+            Slog.w(TAG, "Parsed package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -367,16 +376,29 @@
             return input.success(targetVers);
         }
 
-        if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
-        }
-
         // If it's a pre-release SDK and the codename matches this platform, it
         // definitely targets this SDK.
         if (matchTargetCode(platformSdkCodenames, targetCode)) {
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        targetCode)) {
+            Slog.w(TAG, "Parsed package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
+        try {
+            if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
+                return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+            }
+        } catch (IllegalArgumentException e) {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index a392afd..9cf329c 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -50,5 +50,7 @@
     void suppressToggleSensorPrivacyReminders(int userId, int sensor, IBinder token,
             boolean suppress);
 
+    boolean requiresAuthentication();
+
     void showSensorUseDialog(int sensor);
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 0460e58..99b58c9 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -327,6 +327,8 @@
     @NonNull
     private boolean mToggleListenerRegistered = false;
 
+    private Boolean mRequiresAuthentication = null;
+
     /**
      * Private constructor to ensure only a single instance is created.
      */
@@ -761,6 +763,23 @@
     }
 
     /**
+     * @return whether the device is required to be unlocked to change software state.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public boolean requiresAuthentication() {
+        if (mRequiresAuthentication == null) {
+            try {
+                mRequiresAuthentication = mService.requiresAuthentication();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return mRequiresAuthentication;
+    }
+
+    /**
      * If sensor privacy for the provided sensor is enabled then this call will show the user the
      * dialog which is shown when an application attempts to use that sensor. If privacy isn't
      * enabled then this does nothing.
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 1263da6..4d0ba63 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -72,7 +72,7 @@
 import java.util.concurrent.Executor;
 
 public final class CameraExtensionSessionImpl extends CameraExtensionSession {
-    private static final int PREVIEW_QUEUE_SIZE = 3;
+    private static final int PREVIEW_QUEUE_SIZE = 10;
     private static final String TAG = "CameraExtensionSessionImpl";
 
     private final Executor mExecutor;
@@ -1057,15 +1057,8 @@
                                                 mClientRequest));
 
                         if (mCaptureResultHandler != null) {
-                            CameraMetadataNative captureResults = new CameraMetadataNative();
-                            for (CaptureResult.Key key : mSupportedResultKeys) {
-                                Object value = result.get(key);
-                                if (value != null) {
-                                    captureResults.set(key, value);
-                                }
-                            }
                             mCaptureResultHandler.onCaptureCompleted(timestamp,
-                                    captureResults);
+                                    initializeFilteredResults(result));
                         }
                     } finally {
                         Binder.restoreCallingIdentity(ident);
@@ -1127,6 +1120,11 @@
 
         private class ImageCallback implements OnImageAvailableListener {
             @Override
+            public void onImageDropped(long timestamp) {
+                notifyCaptureFailed();
+            }
+
+            @Override
             public void onImageAvailable(ImageReader reader, Image img) {
                 if (mCaptureFailed) {
                     img.close();
@@ -1160,6 +1158,9 @@
 
     private class ImageLoopbackCallback implements OnImageAvailableListener {
         @Override
+        public void onImageDropped(long timestamp) { }
+
+        @Override
         public void onImageAvailable(ImageReader reader, Image img) {
             img.close();
         }
@@ -1221,7 +1222,8 @@
     }
 
     private interface OnImageAvailableListener {
-        public void onImageAvailable (ImageReader reader, Image img);
+        void onImageDropped(long timestamp);
+        void onImageAvailable (ImageReader reader, Image img);
     }
 
     private class CameraOutputImageCallback implements ImageReader.OnImageAvailableListener,
@@ -1263,6 +1265,29 @@
             } else {
                 mImageListenerMap.put(img.getTimestamp(), new Pair<>(img, null));
             }
+
+            notifyDroppedImages(timestamp);
+        }
+
+        private void notifyDroppedImages(long timestamp) {
+            Set<Long> timestamps = mImageListenerMap.keySet();
+            ArrayList<Long> removedTs = new ArrayList<>();
+            for (long ts : timestamps) {
+                if (ts < timestamp) {
+                    Log.e(TAG, "Dropped image with ts: " + ts);
+                    Pair<Image, OnImageAvailableListener> entry = mImageListenerMap.get(ts);
+                    if (entry.second != null) {
+                        entry.second.onImageDropped(ts);
+                    }
+                    if (entry.first != null) {
+                        entry.first.close();
+                    }
+                    removedTs.add(ts);
+                }
+            }
+            for (long ts : removedTs) {
+                mImageListenerMap.remove(ts);
+            }
         }
 
         public void registerListener(Long timestamp, OnImageAvailableListener listener) {
@@ -1291,6 +1316,12 @@
                     entry.first.close();
                 }
             }
+            for (long timestamp : mImageListenerMap.keySet()) {
+                Pair<Image, OnImageAvailableListener> entry = mImageListenerMap.get(timestamp);
+                if (entry.second != null) {
+                    entry.second.onImageDropped(timestamp);
+                }
+            }
             mImageListenerMap.clear();
         }
     }
@@ -1447,7 +1478,6 @@
             } else {
                 notifyConfigurationFailure();
             }
-
         }
 
         @Override
@@ -1536,7 +1566,16 @@
                     } else if (mPreviewProcessorType ==
                             IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR) {
                         int idx = mPendingResultMap.indexOfKey(timestamp);
-                        if (idx >= 0) {
+
+                        if ((idx >= 0) && (mPendingResultMap.get(timestamp).first == null)) {
+                            // Image was dropped before we can receive the capture results
+                            if ((mCaptureResultHandler != null)) {
+                                mCaptureResultHandler.onCaptureCompleted(timestamp,
+                                        initializeFilteredResults(result));
+                            }
+                            discardPendingRepeatingResults(idx, mPendingResultMap, false);
+                        } else  if (idx >= 0) {
+                            // Image came before the capture results
                             ParcelImage parcelImage = initializeParcelImage(
                                     mPendingResultMap.get(timestamp).first);
                             try {
@@ -1563,6 +1602,7 @@
                             }
                             discardPendingRepeatingResults(idx, mPendingResultMap, false);
                         } else {
+                            // Image not yet available
                             notifyClient = false;
                             mPendingResultMap.put(timestamp,
                                     new Pair<>(null,
@@ -1581,16 +1621,8 @@
                                                 mClientRequest));
                                 if ((mCaptureResultHandler != null) && (mPreviewProcessorType !=
                                         IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR)) {
-                                    CameraMetadataNative captureResults =
-                                            new CameraMetadataNative();
-                                    for (CaptureResult.Key key : mSupportedResultKeys) {
-                                        Object value = result.get(key);
-                                        if (value != null) {
-                                            captureResults.set(key, value);
-                                        }
-                                    }
                                     mCaptureResultHandler.onCaptureCompleted(timestamp,
-                                            captureResults);
+                                            initializeFilteredResults(result));
                                 }
                             } else {
                                 mExecutor.execute(
@@ -1657,19 +1689,24 @@
             for (int i = idx; i >= 0; i--) {
                 if (previewMap.valueAt(i).first != null) {
                     previewMap.valueAt(i).first.close();
-                } else {
-                    if (mClientNotificationsEnabled && ((i != idx) || notifyCurrentIndex)) {
-                        Log.w(TAG, "Preview frame drop with timestamp: " + previewMap.keyAt(i));
-                        final long ident = Binder.clearCallingIdentity();
-                        try {
-                            mExecutor.execute(
-                                    () -> mCallbacks
-                                            .onCaptureFailed(CameraExtensionSessionImpl.this,
-                                                    mClientRequest));
-                        } finally {
-                            Binder.restoreCallingIdentity(ident);
-                        }
+                } else if (mClientNotificationsEnabled && (previewMap.valueAt(i).second != null) &&
+                        ((i != idx) || notifyCurrentIndex)) {
+                    TotalCaptureResult result = previewMap.valueAt(i).second;
+                    Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+                    mCaptureResultHandler.onCaptureCompleted(timestamp,
+                            initializeFilteredResults(result));
+
+                    Log.w(TAG, "Preview frame drop with timestamp: " + previewMap.keyAt(i));
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(
+                                () -> mCallbacks
+                                        .onCaptureFailed(CameraExtensionSessionImpl.this,
+                                                mClientRequest));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
                     }
+
                 }
                 previewMap.removeAt(i);
             }
@@ -1683,6 +1720,12 @@
             }
 
             @Override
+            public void onImageDropped(long timestamp) {
+                discardPendingRepeatingResults(mPendingResultMap.indexOfKey(timestamp),
+                        mPendingResultMap, true);
+            }
+
+            @Override
             public void onImageAvailable(ImageReader reader, Image img) {
                 if (img == null) {
                     Log.e(TAG, "Invalid image!");
@@ -1703,6 +1746,15 @@
         private class ImageProcessCallback implements OnImageAvailableListener {
 
             @Override
+            public void onImageDropped(long timestamp) {
+                discardPendingRepeatingResults(mPendingResultMap.indexOfKey(timestamp),
+                        mPendingResultMap, true);
+                // Add an empty frame&results entry to flag that we dropped a frame
+                // and valid capture results can immediately return to client.
+                mPendingResultMap.put(timestamp, new Pair<>(null, null));
+            }
+
+            @Override
             public void onImageAvailable(ImageReader reader, Image img) {
                 if (mPendingResultMap.size() + 1 >= PREVIEW_QUEUE_SIZE) {
                     // We reached the maximum acquired images limit. This is possible in case we
@@ -1768,6 +1820,17 @@
         }
     }
 
+    private CameraMetadataNative initializeFilteredResults(TotalCaptureResult result) {
+        CameraMetadataNative captureResults = new CameraMetadataNative();
+        for (CaptureResult.Key key : mSupportedResultKeys) {
+            Object value = result.get(key);
+            if (value != null) {
+                captureResults.set(key, value);
+            }
+        }
+        return captureResults;
+    }
+
     private static Size findSmallestAspectMatchedSize(@NonNull List<Size> sizes,
             @NonNull Size arSize) {
         final float TOLL = .01f;
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index b37c27c..fc6bc55 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -75,8 +75,15 @@
     /**
      * Sets the display id that the MouseCursorController will be forced to target. Pass
      * {@link android.view.Display#INVALID_DISPLAY} to clear the override.
+     *
+     * Note: This method generally blocks until the pointer display override has propagated.
+     * When setting a new override, the caller should ensure that an input device that can control
+     * the mouse pointer is connected. If a new override is set when no such input device is
+     * connected, the caller may be blocked for an arbitrary period of time.
+     *
+     * @return true if the pointer displayId was set successfully, or false if it fails.
      */
-    public abstract void setVirtualMousePointerDisplayId(int pointerDisplayId);
+    public abstract boolean setVirtualMousePointerDisplayId(int pointerDisplayId);
 
     /**
      * Gets the display id that the MouseCursorController is being forced to target. Returns
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index f9ed0e3d..3f49b73 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -37,6 +37,7 @@
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodSession;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
@@ -194,7 +195,9 @@
             case DO_START_INPUT: {
                 final SomeArgs args = (SomeArgs) msg.obj;
                 final IBinder startInputToken = (IBinder) args.arg1;
-                final IInputContext inputContext = (IInputContext) args.arg2;
+                final IInputContext inputContext = (IInputContext) ((SomeArgs) args.arg2).arg1;
+                final ImeOnBackInvokedDispatcher imeDispatcher =
+                        (ImeOnBackInvokedDispatcher) ((SomeArgs) args.arg2).arg2;
                 final EditorInfo info = (EditorInfo) args.arg3;
                 final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
                 final boolean restarting = args.argi5 == 1;
@@ -205,7 +208,7 @@
                         : null;
                 info.makeCompatible(mTargetSdkVersion);
                 inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
-                        navButtonFlags);
+                        navButtonFlags, imeDispatcher);
                 args.recycle();
                 return;
             }
@@ -348,13 +351,17 @@
     @Override
     public void startInput(IBinder startInputToken, IInputContext inputContext,
             EditorInfo attribute, boolean restarting,
-            @InputMethodNavButtonFlags int navButtonFlags) {
+            @InputMethodNavButtonFlags int navButtonFlags,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (mCancellationGroup == null) {
             Log.e(TAG, "startInput must be called after bindInput.");
             mCancellationGroup = new CancellationGroup();
         }
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = inputContext;
+        args.arg2 = imeDispatcher;
         mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
-                inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
+                args, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
     }
 
     @BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index efd4f06..25296bc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -101,6 +101,7 @@
 import android.view.Choreographer;
 import android.view.Gravity;
 import android.view.InputChannel;
+import android.view.InputDevice;
 import android.view.InputEventReceiver;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -134,6 +135,9 @@
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import android.window.ImeOnBackInvokedDispatcher;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
 import android.window.WindowMetricsHelper;
 
 import com.android.internal.annotations.GuardedBy;
@@ -344,6 +348,9 @@
      * A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
      **/
     private RingBuffer<MotionEvent> mPendingEvents;
+    private ImeOnBackInvokedDispatcher mImeDispatcher;
+    private Boolean mBackCallbackRegistered = false;
+    private final OnBackInvokedCallback mCompatBackCallback = this::compatHandleBack;
 
     /**
      * Returns whether {@link InputMethodService} is responsible for rendering the back button and
@@ -797,7 +804,13 @@
         @Override
         public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
                 @NonNull EditorInfo editorInfo, boolean restarting,
-                @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
+                @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
+                @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            mImeDispatcher = imeDispatcher;
+            if (mWindow != null) {
+                mWindow.getOnBackInvokedDispatcher().setImeOnBackInvokedDispatcher(
+                        imeDispatcher);
+            }
             mPrivOps.reportStartInputAsync(startInputToken);
             mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
             if (restarting) {
@@ -1496,6 +1509,10 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
         mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
+        if (mImeDispatcher != null) {
+            mWindow.getOnBackInvokedDispatcher()
+                    .setImeOnBackInvokedDispatcher(mImeDispatcher);
+        }
         mNavigationBarController.onSoftInputWindowCreated(mWindow);
         {
             final Window window = mWindow.getWindow();
@@ -1608,6 +1625,8 @@
             // when IME developers are doing something unsupported.
             InputMethodPrivilegedOperationsRegistry.remove(mToken);
         }
+        unregisterCompatOnBackInvokedCallback();
+        mImeDispatcher = null;
     }
 
     /**
@@ -2568,9 +2587,47 @@
         cancelImeSurfaceRemoval();
         mInShowWindow = false;
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        registerCompatOnBackInvokedCallback();
     }
 
 
+    /**
+     * Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
+     *  back dispatching is enabled. We keep the {@link KeyEvent#KEYCODE_BACK} based legacy code
+     *  around to handle back on older devices.
+     */
+    private void registerCompatOnBackInvokedCallback() {
+        if (mBackCallbackRegistered) {
+            return;
+        }
+        if (mWindow != null) {
+            mWindow.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                    OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatBackCallback);
+            mBackCallbackRegistered = true;
+        }
+    }
+
+    private void unregisterCompatOnBackInvokedCallback() {
+        if (!mBackCallbackRegistered) {
+            return;
+        }
+        if (mWindow != null) {
+            mWindow.getOnBackInvokedDispatcher()
+                    .unregisterOnBackInvokedCallback(mCompatBackCallback);
+            mBackCallbackRegistered = false;
+        }
+    }
+
+    private KeyEvent createBackKeyEvent(int action, boolean isTracking) {
+        final long when = SystemClock.uptimeMillis();
+        return new KeyEvent(when, when, action,
+                KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+                        | (isTracking ? KeyEvent.FLAG_TRACKING : 0),
+                InputDevice.SOURCE_KEYBOARD);
+    }
+
     private boolean prepareWindow(boolean showInput) {
         boolean doShowInput = false;
         mDecorViewVisible = true;
@@ -2658,6 +2715,7 @@
         }
         mLastWasInFullscreenMode = mIsFullscreen;
         updateFullscreenMode();
+        unregisterCompatOnBackInvokedCallback();
     }
 
     /**
@@ -3797,4 +3855,14 @@
             proto.end(token);
         }
     };
+
+    private void compatHandleBack() {
+        final KeyEvent downEvent = createBackKeyEvent(
+                KeyEvent.ACTION_DOWN, false /* isTracking */);
+        onKeyDown(KeyEvent.KEYCODE_BACK, downEvent);
+        final boolean hasStartedTracking =
+                (downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
+        final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
+        onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
+    }
 }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index ecdc803..022d213 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -1361,6 +1361,18 @@
             return false;
         }
 
+        // Apps with PROPERTY_NO_APP_DATA_STORAGE should not be allowed in scoped storage
+        final String packageName = AppGlobals.getInitialPackage();
+        try {
+            final PackageManager.Property noAppStorageProp = packageManager.getProperty(
+                    PackageManager.PROPERTY_NO_APP_DATA_STORAGE, packageName);
+            if (noAppStorageProp != null && noAppStorageProp.getBoolean()) {
+                return false;
+            }
+        } catch (PackageManager.NameNotFoundException ignore) {
+            // Property not defined for the package
+        }
+
         boolean defaultScopedStorage = Compatibility.isChangeEnabled(DEFAULT_SCOPED_STORAGE);
         boolean forceEnableScopedStorage = Compatibility.isChangeEnabled(
                 FORCE_ENABLE_SCOPED_STORAGE);
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0e5a65c..77c0067 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -1292,7 +1292,8 @@
                 USER_MISSED_LOW_RING_VOLUME,
                 USER_MISSED_NO_VIBRATE,
                 USER_MISSED_CALL_SCREENING_SERVICE_SILENCED,
-                USER_MISSED_CALL_FILTERS_TIMEOUT
+                USER_MISSED_CALL_FILTERS_TIMEOUT,
+                USER_MISSED_NEVER_RANG
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface MissedReason {}
@@ -1383,6 +1384,13 @@
         public static final long USER_MISSED_CALL_FILTERS_TIMEOUT = 1 << 22;
 
         /**
+         * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, set this bit when
+         * the call ended before ringing.
+         * @hide
+         */
+        public static final long USER_MISSED_NEVER_RANG = 1 << 23;
+
+        /**
          * Where the {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE},
          * indicates factors which may have lead the user to miss the call.
          * <P>Type: INTEGER</P>
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 37f44e9..9a2f7ba 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -738,6 +738,14 @@
      */
     public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE = "vendor_system_native";
 
+    /**
+     * Namespace for DevicePolicyManager related features.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_DEVICE_POLICY_MANAGER =
+            "device_policy_manager";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dac54cf..f35a458 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9695,6 +9695,40 @@
                 "active_unlock_on_biometric_fail";
 
         /**
+         * If active unlock triggers on biometric failures, include the following error codes
+         * as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}.
+         * Error codes should be separated by a pipe. For example: "1|4|5". If active unlock
+         * should never trigger on any face errors, this should be set to an empty string.
+         * A null value will use the system default value (TIMEOUT).
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_ON_FACE_ERRORS =
+                "active_unlock_on_face_errors";
+
+        /**
+         * If active unlock triggers on biometric failures, include the following acquired info
+         * as a "biometric failure". See {@link android.hardware.biometrics.BiometricFaceConstants}.
+         * Acquired codes should be separated by a pipe. For example: "1|4|5". If active unlock
+         * should never on trigger on any acquired info messages, this should be
+         * set to an empty string. A null value will use the system default value (none).
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO =
+                "active_unlock_on_face_acquire_info";
+
+        /**
+         * If active unlock triggers on biometric failures, then also request active unlock on
+         * unlock intent when each setting (BiometricType) is the only biometric type enrolled.
+         * Biometric types should be separated by a pipe. For example: "0|3" or "0". If this
+         * setting should be disabled, then this should be set to an empty string. A null value
+         * will use the system default value (0 / None).
+         *   0 = None, 1 = Any face, 2 = Any fingerprint, 3 = Under display fingerprint
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED =
+                "active_unlock_on_unlock_intent_when_biometric_enrolled";
+
+        /**
          * Whether the assist gesture should be enabled.
          *
          * @hide
diff --git a/core/java/android/service/quickaccesswallet/TEST_MAPPING b/core/java/android/service/quickaccesswallet/TEST_MAPPING
index 4d97ab6..5d2a3a8 100644
--- a/core/java/android/service/quickaccesswallet/TEST_MAPPING
+++ b/core/java/android/service/quickaccesswallet/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "CtsQuickAccessWalletTestCases"
     }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 3be4c3ed..24ded93 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -50,12 +50,21 @@
     public static final String SETTINGS_ENABLE_SECURITY_HUB = "settings_enable_security_hub";
     /** @hide */
     public static final String SETTINGS_SUPPORT_LARGE_SCREEN = "settings_support_large_screen";
+
     /**
      * Support per app's language selection
      * @hide
      */
     public static final String SETTINGS_APP_LANGUAGE_SELECTION = "settings_app_language_selection";
 
+    /**
+     * Support locale opt-out and opt-in switch for per app's language.
+     * @hide
+     */
+    public static final String SETTINGS_APP_LOCALE_OPT_IN_ENABLED =
+            "settings_app_locale_opt_in_enabled";
+
+
     /** @hide */
     public static final String SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS =
             "settings_enable_monitor_phantom_procs";
@@ -97,6 +106,7 @@
         DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
         DEFAULT_FLAGS.put("settings_search_always_expand", "true");
         DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "true");
+        DEFAULT_FLAGS.put(SETTINGS_APP_LOCALE_OPT_IN_ENABLED, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
         DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
         DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
@@ -106,6 +116,7 @@
     static {
         PERSISTENT_FLAGS = new HashSet<>();
         PERSISTENT_FLAGS.add(SETTINGS_APP_LANGUAGE_SELECTION);
+        PERSISTENT_FLAGS.add(SETTINGS_APP_LOCALE_OPT_IN_ENABLED);
         PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
         PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
         PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fd557e7..2e48c2b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -719,9 +719,14 @@
 
     private void releaseSurfaces(boolean releaseSurfacePackage) {
         mSurfaceAlpha = 1f;
-
-        synchronized (mSurfaceControlLock) {
+	
+        mSurfaceLock.lock();
+        try {
             mSurface.destroy();
+        } finally {
+            mSurfaceLock.unlock();
+        }
+        synchronized (mSurfaceControlLock) {
             if (mBlastBufferQueue != null) {
                 mBlastBufferQueue.destroy();
                 mBlastBufferQueue = null;
@@ -770,105 +775,99 @@
             Transaction surfaceUpdateTransaction) {
         boolean realSizeChanged = false;
 
-        mSurfaceLock.lock();
-        try {
-            mDrawingStopped = !mVisible;
+        mDrawingStopped = !mVisible;
 
-            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                    + "Cur surface: " + mSurface);
+        if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                + "Cur surface: " + mSurface);
 
-            // If we are creating the surface control or the parent surface has not
-            // changed, then set relative z. Otherwise allow the parent
-            // SurfaceChangedCallback to update the relative z. This is needed so that
-            // we do not change the relative z before the server is ready to swap the
-            // parent surface.
-            if (creating) {
-                updateRelativeZ(surfaceUpdateTransaction);
-                if (mSurfacePackage != null) {
-                    reparentSurfacePackage(surfaceUpdateTransaction, mSurfacePackage);
-                }
+        // If we are creating the surface control or the parent surface has not
+        // changed, then set relative z. Otherwise allow the parent
+        // SurfaceChangedCallback to update the relative z. This is needed so that
+        // we do not change the relative z before the server is ready to swap the
+        // parent surface.
+        if (creating) {
+            updateRelativeZ(surfaceUpdateTransaction);
+            if (mSurfacePackage != null) {
+                reparentSurfacePackage(surfaceUpdateTransaction, mSurfacePackage);
             }
-            mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
-
-            if (mViewVisibility) {
-                surfaceUpdateTransaction.show(mSurfaceControl);
-            } else {
-                surfaceUpdateTransaction.hide(mSurfaceControl);
-            }
-
-
-
-            updateBackgroundVisibility(surfaceUpdateTransaction);
-            updateBackgroundColor(surfaceUpdateTransaction);
-            if (mUseAlpha) {
-                float alpha = getFixedAlpha();
-                surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
-                mSurfaceAlpha = alpha;
-            }
-
-            surfaceUpdateTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
-            if ((sizeChanged || hintChanged) && !creating) {
-                setBufferSize(surfaceUpdateTransaction);
-            }
-            if (sizeChanged || creating || !isHardwareAccelerated()) {
-
-                // Set a window crop when creating the surface or changing its size to
-                // crop the buffer to the surface size since the buffer producer may
-                // use SCALING_MODE_SCALE and submit a larger size than the surface
-                // size.
-                if (mClipSurfaceToBounds && mClipBounds != null) {
-                    surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
-                } else {
-                    surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
-                            mSurfaceHeight);
-                }
-
-                surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
-                            mSurfaceHeight);
-
-                if (isHardwareAccelerated()) {
-                    // This will consume the passed in transaction and the transaction will be
-                    // applied on a render worker thread.
-                    replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight);
-                } else {
-                    onSetSurfacePositionAndScale(surfaceUpdateTransaction, mSurfaceControl,
-                            mScreenRect.left /*positionLeft*/,
-                            mScreenRect.top /*positionTop*/,
-                            mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
-                            mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
-                }
-                if (DEBUG_POSITION) {
-                    Log.d(TAG, String.format(
-                            "%d performSurfaceTransaction %s "
-                                + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
-                            System.identityHashCode(this),
-                            isHardwareAccelerated() ? "RenderWorker" : "UI Thread",
-                            mScreenRect.left, mScreenRect.top, mScreenRect.right,
-                            mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight));
-                }
-            }
-            applyTransactionOnVriDraw(surfaceUpdateTransaction);
-            updateEmbeddedAccessibilityMatrix(false);
-
-            mSurfaceFrame.left = 0;
-            mSurfaceFrame.top = 0;
-            if (translator == null) {
-                mSurfaceFrame.right = mSurfaceWidth;
-                mSurfaceFrame.bottom = mSurfaceHeight;
-            } else {
-                float appInvertedScale = translator.applicationInvertedScale;
-                mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
-                mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
-            }
-            final int surfaceWidth = mSurfaceFrame.right;
-            final int surfaceHeight = mSurfaceFrame.bottom;
-            realSizeChanged = mLastSurfaceWidth != surfaceWidth
-                    || mLastSurfaceHeight != surfaceHeight;
-            mLastSurfaceWidth = surfaceWidth;
-            mLastSurfaceHeight = surfaceHeight;
-        } finally {
-            mSurfaceLock.unlock();
         }
+        mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
+
+        if (mViewVisibility) {
+            surfaceUpdateTransaction.show(mSurfaceControl);
+        } else {
+            surfaceUpdateTransaction.hide(mSurfaceControl);
+        }
+
+
+
+        updateBackgroundVisibility(surfaceUpdateTransaction);
+        updateBackgroundColor(surfaceUpdateTransaction);
+        if (mUseAlpha) {
+            float alpha = getFixedAlpha();
+            surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
+            mSurfaceAlpha = alpha;
+        }
+
+        surfaceUpdateTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+        if ((sizeChanged || hintChanged) && !creating) {
+            setBufferSize(surfaceUpdateTransaction);
+        }
+        if (sizeChanged || creating || !isHardwareAccelerated()) {
+            // Set a window crop when creating the surface or changing its size to
+            // crop the buffer to the surface size since the buffer producer may
+            // use SCALING_MODE_SCALE and submit a larger size than the surface
+            // size.
+            if (mClipSurfaceToBounds && mClipBounds != null) {
+                surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+            } else {
+                surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+                        mSurfaceHeight);
+            }
+
+            surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
+                        mSurfaceHeight);
+
+            if (isHardwareAccelerated()) {
+                // This will consume the passed in transaction and the transaction will be
+                // applied on a render worker thread.
+                replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight);
+            } else {
+                onSetSurfacePositionAndScale(surfaceUpdateTransaction, mSurfaceControl,
+                        mScreenRect.left /*positionLeft*/,
+                        mScreenRect.top /*positionTop*/,
+                        mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
+                        mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
+            }
+            if (DEBUG_POSITION) {
+                Log.d(TAG, String.format(
+                        "%d performSurfaceTransaction %s "
+                            + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
+                        System.identityHashCode(this),
+                        isHardwareAccelerated() ? "RenderWorker" : "UI Thread",
+                        mScreenRect.left, mScreenRect.top, mScreenRect.right,
+                        mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight));
+            }
+        }
+        applyTransactionOnVriDraw(surfaceUpdateTransaction);
+        updateEmbeddedAccessibilityMatrix(false);
+         mSurfaceFrame.left = 0;
+        mSurfaceFrame.top = 0;
+        if (translator == null) {
+            mSurfaceFrame.right = mSurfaceWidth;
+            mSurfaceFrame.bottom = mSurfaceHeight;
+        } else {
+            float appInvertedScale = translator.applicationInvertedScale;
+            mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
+            mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+        }
+        final int surfaceWidth = mSurfaceFrame.right;
+        final int surfaceHeight = mSurfaceFrame.bottom;
+        realSizeChanged = mLastSurfaceWidth != surfaceWidth
+                || mLastSurfaceHeight != surfaceHeight;
+        mLastSurfaceWidth = surfaceWidth;
+        mLastSurfaceHeight = surfaceHeight;
+
         return realSizeChanged;
     }
 
@@ -1103,21 +1102,30 @@
      *                          Surface for compatibility reasons.
      */
     private void copySurface(boolean surfaceControlCreated, boolean bufferSizeChanged) {
-        if (surfaceControlCreated) {
-            mSurface.copyFrom(mBlastBufferQueue);
-        }
+        // Some legacy applications use the underlying native {@link Surface} object
+        // as a key to whether anything has changed. In these cases, updates to the
+        // existing {@link Surface} will be ignored when the size changes.
+        // Therefore, we must explicitly recreate the {@link Surface} in these
+        // cases.
+        boolean needsWorkaround = bufferSizeChanged &&
+            getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O;
+       if (!surfaceControlCreated && !needsWorkaround) {
+           return;
+       }
+       mSurfaceLock.lock();
+       try {
+           if (surfaceControlCreated) {
+               mSurface.copyFrom(mBlastBufferQueue);
+           }
 
-        if (bufferSizeChanged && getContext().getApplicationInfo().targetSdkVersion
-                < Build.VERSION_CODES.O) {
-            // Some legacy applications use the underlying native {@link Surface} object
-            // as a key to whether anything has changed. In these cases, updates to the
-            // existing {@link Surface} will be ignored when the size changes.
-            // Therefore, we must explicitly recreate the {@link Surface} in these
-            // cases.
-            if (mBlastBufferQueue != null) {
-                mSurface.transferFrom(mBlastBufferQueue.createSurfaceWithHandle());
-            }
-        }
+           if (needsWorkaround) {
+               if (mBlastBufferQueue != null) {
+                   mSurface.transferFrom(mBlastBufferQueue.createSurfaceWithHandle());
+               }
+           }
+       } finally {
+           mSurfaceLock.unlock();
+       }
     }
 
     private void setBufferSize(Transaction transaction) {
@@ -1200,8 +1208,10 @@
         }
         mTransformHint = viewRoot.getBufferTransformHint();
         mBlastSurfaceControl.setTransformHint(mTransformHint);
+
         mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */);
         mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
+        mBlastBufferQueue.setTransactionHangCallback(ViewRootImpl.sTransactionHangCallback);
     }
 
     private void onDrawFinished() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8901d86..d04b07c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8206,24 +8206,25 @@
         if (canNotifyAutofillEnterExitEvent()) {
             AutofillManager afm = getAutofillManager();
             if (afm != null) {
-                if (enter && isFocused()) {
+                if (enter) {
                     // We have not been laid out yet, hence cannot evaluate
                     // whether this view is visible to the user, we will do
                     // the evaluation once layout is complete.
                     if (!isLaidOut()) {
                         mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
                     } else if (isVisibleToUser()) {
-                        // TODO This is a potential problem that View gets focus before it's visible
-                        // to User. Ideally View should handle the event when isVisibleToUser()
-                        // becomes true where it should issue notifyViewEntered().
-                        afm.notifyViewEntered(this);
-                    } else {
-                        afm.enableFillRequestActivityStarted(this);
+                        if (isFocused()) {
+                            // TODO This is a potential problem that View gets focus before it's
+                            // visible to User. Ideally View should handle the event when
+                            // isVisibleToUser() becomes true where it should issue
+                            // notifyViewEntered().
+                            afm.notifyViewEntered(this);
+                        } else {
+                            afm.notifyViewEnteredForFillDialog(this);
+                        }
                     }
-                } else if (!enter && !isFocused()) {
+                } else if (!isFocused()) {
                     afm.notifyViewExited(this);
-                } else if (enter) {
-                    afm.enableFillRequestActivityStarted(this);
                 }
             }
         }
@@ -9921,7 +9922,7 @@
      * <ol>
      *   <li>It should only be called when content capture is enabled for the view.
      *   <li>It must call viewAppeared() before viewDisappeared()
-     *   <li>viewAppearead() can only be called when the view is visible and laidout
+     *   <li>viewAppeared() can only be called when the view is visible and laid out
      *   <li>It should not call the same event twice.
      * </ol>
      */
@@ -9998,6 +9999,11 @@
                     Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
                 }
             }
+
+            // We reset any translation state as views may be re-used (e.g., as in ListView and
+            // RecyclerView). We only need to do this for views important for content capture since
+            // views unimportant for content capture won't be translated anyway.
+            clearTranslationState();
         }
     }
 
@@ -12718,6 +12724,21 @@
     }
 
     /**
+     * @hide
+     */
+    public void clearTranslationState() {
+        if (mViewTranslationCallback != null) {
+            mViewTranslationCallback.onClearTranslation(this);
+        }
+        clearViewTranslationCallback();
+        clearViewTranslationResponse();
+        if (hasTranslationTransientState()) {
+            setHasTransientState(false);
+            setHasTranslationTransientState(false);
+        }
+    }
+
+    /**
      * Returns true if this view is currently attached to a window.
      */
     public boolean isAttachedToWindow() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 42b5691..48c102b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -590,7 +590,6 @@
     @Nullable
     int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
     boolean mPerformContentCapture;
-    boolean mPerformAutoFill;
 
 
     boolean mReportNextDraw;
@@ -858,6 +857,28 @@
      */
     private Bundle mRelayoutBundle = new Bundle();
 
+    private static volatile boolean sAnrReported = false;
+    static BLASTBufferQueue.TransactionHangCallback sTransactionHangCallback =
+        new BLASTBufferQueue.TransactionHangCallback() {
+            @Override
+            public void onTransactionHang(boolean isGPUHang) {
+                if (isGPUHang && !sAnrReported) {
+                    sAnrReported = true;
+                    try {
+                        ActivityManager.getService().appNotResponding(
+                            "Buffer processing hung up due to stuck fence. Indicates GPU hang");
+                    } catch (RemoteException e) {
+                        // We asked the system to crash us, but the system
+                        // already crashed. Unfortunately things may be
+                        // out of control.
+                    }
+                } else {
+                    // TODO: Do something with this later. For now we just ANR
+                    // in dequeue buffer later like we always have.
+                }
+            }
+        };
+
     private String mTag = TAG;
 
     public ViewRootImpl(Context context, Display display) {
@@ -890,7 +911,6 @@
         mPreviousTransparentRegion = new Region();
         mFirst = true; // true for the first time the view is added
         mPerformContentCapture = true; // also true for the first time the view is added
-        mPerformAutoFill = true;
         mAdded = false;
         mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                 context);
@@ -2100,6 +2120,7 @@
         }
         mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
                 mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
+        mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
         Surface blastSurface = mBlastBufferQueue.createSurface();
         // Only call transferFrom if the surface has changed to prevent inc the generation ID and
         // causing EGL resources to be recreated.
@@ -4308,18 +4329,6 @@
         if (mPerformContentCapture) {
             performContentCaptureInitialReport();
         }
-
-        if (mPerformAutoFill) {
-            notifyEnterForAutoFillIfNeeded();
-        }
-    }
-
-    private void notifyEnterForAutoFillIfNeeded() {
-        mPerformAutoFill = false;
-        final AutofillManager afm = getAutofillManager();
-        if (afm != null) {
-            afm.notifyViewEnteredForActivityStarted(mView);
-        }
     }
 
     /**
@@ -6086,6 +6095,28 @@
 
         @Override
         protected int onProcess(QueuedInputEvent q) {
+            if (q.mEvent instanceof KeyEvent) {
+                final KeyEvent event = (KeyEvent) q.mEvent;
+
+                // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
+                // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}.
+                if (isBack(event)
+                        && mContext != null
+                        && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
+                    OnBackInvokedCallback topCallback =
+                            getOnBackInvokedDispatcher().getTopCallback();
+                    if (event.getAction() == KeyEvent.ACTION_UP) {
+                        if (topCallback != null) {
+                            topCallback.onBackInvoked();
+                            return FINISH_HANDLED;
+                        }
+                    } else {
+                        // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
+                        return FINISH_NOT_HANDLED;
+                    }
+                }
+            }
+
             if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
                 mInputQueue.sendInputEvent(q.mEvent, q, true, this);
                 return DEFER;
@@ -6437,24 +6468,6 @@
                 return FINISH_HANDLED;
             }
 
-            // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
-            // view tree and invoke the appropriate {@link OnBackInvokedCallback}.
-            if (isBack(event)
-                    && mContext != null
-                    && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
-                OnBackInvokedCallback topCallback =
-                        getOnBackInvokedDispatcher().getTopCallback();
-                if (event.getAction() == KeyEvent.ACTION_UP) {
-                    if (topCallback != null) {
-                        topCallback.onBackInvoked();
-                        return FINISH_HANDLED;
-                    }
-                } else {
-                    // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
-                    return FINISH_NOT_HANDLED;
-                }
-            }
-
             // Deliver the key to the view hierarchy.
             if (mView.dispatchKeyEvent(event)) {
                 return FINISH_HANDLED;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 0a75992..dcedb30 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -102,6 +102,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import sun.misc.Cleaner;
 
@@ -645,16 +646,6 @@
     private boolean mEnabledForAugmentedAutofillOnly;
 
     /**
-     * Indicates whether there are any fields that need to do a fill request
-     * after the activity starts.
-     *
-     * Note: This field will be set to true multiple times if there are many
-     * autofillable views. So needs to check mIsFillRequested at the same time to
-     * avoid re-trigger autofill.
-     */
-    private boolean mRequireAutofill;
-
-    /**
      * Indicates whether there is already a field to do a fill request after
      * the activity started.
      *
@@ -663,7 +654,7 @@
      * triggered autofill, it is unnecessary to trigger again through
      * AutofillManager#notifyViewEnteredForActivityStarted.
      */
-    private boolean mIsFillRequested;
+    private AtomicBoolean mIsFillRequested;
 
     @Nullable private List<AutofillId> mFillDialogTriggerIds;
 
@@ -811,8 +802,7 @@
         mContext = Objects.requireNonNull(context, "context cannot be null");
         mService = service;
         mOptions = context.getAutofillOptions();
-        mIsFillRequested = false;
-        mRequireAutofill = false;
+        mIsFillRequested = new AtomicBoolean(false);
 
         mIsFillDialogEnabled = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_AUTOFILL,
@@ -1113,22 +1103,31 @@
     }
 
     /**
-     * The view have the allowed autofill hints, marked to perform a fill request after layout if
-     * the field does not trigger a fill request.
+     * The {@link #DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED} is {@code true} or the view have
+     * the allowed autofill hints, performs a fill request to know there is any field supported
+     * fill dialog.
      *
      * @hide
      */
-    public void enableFillRequestActivityStarted(View v) {
-        if (mRequireAutofill) {
+    public void notifyViewEnteredForFillDialog(View v) {
+        // Skip if the fill request has been performed for a view.
+        if (mIsFillRequested.get()) {
             return;
         }
 
         if (mIsFillDialogEnabled
                 || ArrayUtils.containsAny(v.getAutofillHints(), mFillDialogEnabledHints)) {
             if (sDebug) {
-                Log.d(TAG, "Trigger fill request at starting");
+                Log.d(TAG, "Trigger fill request at view entered");
             }
-            mRequireAutofill = true;
+
+            // Note: No need for atomic getAndSet as this method is called on the UI thread.
+            mIsFillRequested.set(true);
+
+            int flags = FLAG_SUPPORTS_FILL_DIALOG;
+            flags |= FLAG_VIEW_NOT_FOCUSED;
+            // use root view, so autofill UI does not trigger immediately.
+            notifyViewEntered(v.getRootView(), flags);
         }
     }
 
@@ -1136,25 +1135,6 @@
         return mIsFillDialogEnabled || !ArrayUtils.isEmpty(mFillDialogEnabledHints);
     }
 
-    /**
-     * Notify autofill to do a fill request while the activity started.
-     *
-     * @hide
-     */
-    public void notifyViewEnteredForActivityStarted(@NonNull View view) {
-        if (!hasAutofillFeature() || !hasFillDialogUiFeature()) {
-            return;
-        }
-
-        if (!mRequireAutofill || mIsFillRequested) {
-            return;
-        }
-
-        int flags = FLAG_SUPPORTS_FILL_DIALOG;
-        flags |= FLAG_VIEW_NOT_FOCUSED;
-        notifyViewEntered(view, flags);
-    }
-
     private int getImeStateFlag(View v) {
         final WindowInsets rootWindowInsets = v.getRootWindowInsets();
         if (rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsets.Type.ime())) {
@@ -1203,7 +1183,7 @@
         }
         AutofillCallback callback;
         synchronized (mLock) {
-            mIsFillRequested = true;
+            mIsFillRequested.set(true);
             callback = notifyViewEnteredLocked(view, flags);
         }
 
@@ -2119,8 +2099,7 @@
         mFillableIds = null;
         mSaveTriggerId = null;
         mIdShownFillUi = null;
-        mIsFillRequested = false;
-        mRequireAutofill = false;
+        mIsFillRequested.set(false);
         mShowAutofillDialogCalled = false;
         mFillDialogTriggerIds = null;
         if (resetEnteredIds) {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 6209b46..dbdc0da 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -29,6 +29,7 @@
 import android.view.InputChannel;
 import android.view.MotionEvent;
 import android.view.View;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -232,6 +233,10 @@
      *                        long as your implementation of {@link InputMethod} relies on such
      *                        IPCs
      * @param navButtonFlags {@link InputMethodNavButtonFlags} in the initial state of this session.
+     * @param imeDispatcher The {@link ImeOnBackInvokedDispatcher }} to be set on the
+     *                      IME's {@link android.window.WindowOnBackInvokedDispatcher}, so that IME
+     *                      {@link android.window.OnBackInvokedCallback}s can be forwarded to
+     *                      the client requesting to start input.
      * @see #startInput(InputConnection, EditorInfo)
      * @see #restartInput(InputConnection, EditorInfo)
      * @see EditorInfo
@@ -240,7 +245,8 @@
     @MainThread
     default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
             @NonNull EditorInfo editorInfo, boolean restarting,
-            @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
+            @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (restarting) {
             restartInput(inputConnection, editorInfo);
         } else {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d9bde58..e2e9a85 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -91,6 +91,8 @@
 import android.view.ViewRootImpl;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.autofill.AutofillManager;
+import android.window.ImeOnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.DirectBootAwareness;
@@ -105,6 +107,7 @@
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.IInputMethodSession;
@@ -279,6 +282,21 @@
     private static final String SUBTYPE_MODE_VOICE = "voice";
 
     /**
+     * Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus(
+     * int, IInputMethodClient, IBinder, int, int, int, EditorInfo, IInputContext, int)} to receive
+     * {@link android.window.OnBackInvokedCallback} registrations from IME.
+     */
+    private final ImeOnBackInvokedDispatcher mImeDispatcher =
+            new ImeOnBackInvokedDispatcher(Handler.getMain()) {
+        @Override
+        public WindowOnBackInvokedDispatcher getReceivingDispatcher() {
+            synchronized (mH) {
+                return mCurRootView != null ? mCurRootView.getOnBackInvokedDispatcher() : null;
+            }
+        }
+    };
+
+    /**
      * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
      * or indirectly relied on {@link #sInstance} via reflection or something like that.
      *
@@ -740,7 +758,8 @@
                             windowFlags,
                             null,
                             null, null,
-                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
+                            mImeDispatcher);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -1687,6 +1706,8 @@
             mServedConnecting = false;
             clearConnectionLocked();
         }
+        // Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
+        mImeDispatcher.clear();
     }
 
     public void displayCompletions(View view, CompletionInfo[] completions) {
@@ -2359,7 +2380,8 @@
                         softInputMode, windowFlags, tba, servedInputConnection,
                         servedInputConnection == null ? null
                                 : servedInputConnection.asIRemoteAccessibilityInputConnection(),
-                        view.getContext().getApplicationInfo().targetSdkVersion);
+                        view.getContext().getApplicationInfo().targetSdkVersion,
+                        mImeDispatcher);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 8cf032b..6bf2474 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -158,12 +158,7 @@
             case STATE_UI_TRANSLATION_FINISHED:
                 destroyTranslators();
                 runForEachView((view, callback) -> {
-                    callback.onClearTranslation(view);
-                    view.clearViewTranslationResponse();
-                    if (view.hasTranslationTransientState()) {
-                        view.setHasTransientState(false);
-                        view.setHasTranslationTransientState(false);
-                    }
+                    view.clearTranslationState();
                 });
                 notifyTranslationFinished(/* activityDestroyed= */ false);
                 synchronized (mLock) {
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.aidl b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl
new file mode 100644
index 0000000..04e6420
--- /dev/null
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/os/ParcelFileDescriptor.aidl
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.window;
+
+parcelable ImeOnBackInvokedDispatcher;
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
new file mode 100644
index 0000000..d5763aa
--- /dev/null
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * A {@link OnBackInvokedDispatcher} for IME that forwards {@link OnBackInvokedCallback}
+ * registrations from the IME process to the app process to be registered on the app window.
+ * <p>
+ * The app process creates and propagates an instance of {@link ImeOnBackInvokedDispatcher}
+ * to the IME to be set on the IME window's {@link WindowOnBackInvokedDispatcher}.
+ * <p>
+ * @see WindowOnBackInvokedDispatcher#setImeOnBackInvokedDispatcher
+ *
+ * @hide
+ */
+public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parcelable {
+
+    private static final String TAG = "ImeBackDispatcher";
+    static final String RESULT_KEY_ID = "id";
+    static final String RESULT_KEY_CALLBACK = "callback";
+    static final String RESULT_KEY_PRIORITY = "priority";
+    static final int RESULT_CODE_REGISTER = 0;
+    static final int RESULT_CODE_UNREGISTER = 1;
+    @NonNull
+    private final ResultReceiver mResultReceiver;
+
+    public ImeOnBackInvokedDispatcher(Handler handler) {
+        mResultReceiver = new ResultReceiver(handler) {
+            @Override
+            public void onReceiveResult(int resultCode, Bundle resultData) {
+                WindowOnBackInvokedDispatcher dispatcher = getReceivingDispatcher();
+                if (dispatcher != null) {
+                    receive(resultCode, resultData, dispatcher);
+                }
+            }
+        };
+    }
+
+    /**
+     * Override this method to return the {@link WindowOnBackInvokedDispatcher} of the window
+     * that should receive the forwarded callback.
+     */
+    @Nullable
+    protected WindowOnBackInvokedDispatcher getReceivingDispatcher() {
+        return null;
+    }
+
+    ImeOnBackInvokedDispatcher(Parcel in) {
+        mResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
+    }
+
+    @Override
+    public void registerOnBackInvokedCallback(
+            @OnBackInvokedDispatcher.Priority int priority,
+            @NonNull OnBackInvokedCallback callback) {
+        final Bundle bundle = new Bundle();
+        final IOnBackInvokedCallback iCallback =
+                new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback);
+        bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder());
+        bundle.putInt(RESULT_KEY_PRIORITY, priority);
+        bundle.putInt(RESULT_KEY_ID, callback.hashCode());
+        mResultReceiver.send(RESULT_CODE_REGISTER, bundle);
+    }
+
+    @Override
+    public void unregisterOnBackInvokedCallback(
+            @NonNull OnBackInvokedCallback callback) {
+        Bundle bundle = new Bundle();
+        bundle.putInt(RESULT_KEY_ID, callback.hashCode());
+        mResultReceiver.send(RESULT_CODE_UNREGISTER, bundle);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mResultReceiver, flags);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ImeOnBackInvokedDispatcher> CREATOR =
+            new Parcelable.Creator<ImeOnBackInvokedDispatcher>() {
+                public ImeOnBackInvokedDispatcher createFromParcel(Parcel in) {
+                    return new ImeOnBackInvokedDispatcher(in);
+                }
+                public ImeOnBackInvokedDispatcher[] newArray(int size) {
+                    return new ImeOnBackInvokedDispatcher[size];
+                }
+            };
+
+    private final HashMap<Integer, OnBackInvokedCallback> mImeCallbackMap = new HashMap<>();
+
+    private void receive(
+            int resultCode, Bundle resultData,
+            @NonNull OnBackInvokedDispatcher receivingDispatcher) {
+        final int callbackId = resultData.getInt(RESULT_KEY_ID);
+        if (resultCode == RESULT_CODE_REGISTER) {
+            int priority = resultData.getInt(RESULT_KEY_PRIORITY);
+            final IOnBackInvokedCallback callback = IOnBackInvokedCallback.Stub.asInterface(
+                    resultData.getBinder(RESULT_KEY_CALLBACK));
+            registerReceivedCallback(
+                    callback, priority, callbackId, receivingDispatcher);
+        } else if (resultCode == RESULT_CODE_UNREGISTER) {
+            unregisterReceivedCallback(callbackId, receivingDispatcher);
+        }
+    }
+
+    private void registerReceivedCallback(
+            @NonNull IOnBackInvokedCallback iCallback,
+            @OnBackInvokedDispatcher.Priority int priority,
+            int callbackId,
+            @NonNull OnBackInvokedDispatcher receivingDispatcher) {
+        final ImeOnBackInvokedCallback imeCallback =
+                new ImeOnBackInvokedCallback(iCallback);
+        mImeCallbackMap.put(callbackId, imeCallback);
+        receivingDispatcher.registerOnBackInvokedCallback(priority, imeCallback);
+    }
+
+    private void unregisterReceivedCallback(
+            int callbackId, OnBackInvokedDispatcher receivingDispatcher) {
+        final OnBackInvokedCallback callback = mImeCallbackMap.get(callbackId);
+        if (callback == null) {
+            Log.e(TAG, "Ime callback not found. Ignoring unregisterReceivedCallback. "
+                    + "callbackId: " + callbackId);
+            return;
+        }
+        receivingDispatcher.unregisterOnBackInvokedCallback(callback);
+    }
+
+    /** Clears all registered callbacks on the instance. */
+    public void clear() {
+        mImeCallbackMap.clear();
+    }
+
+    static class ImeOnBackInvokedCallback implements OnBackInvokedCallback {
+        @NonNull
+        private final IOnBackInvokedCallback mIOnBackInvokedCallback;
+
+        ImeOnBackInvokedCallback(@NonNull IOnBackInvokedCallback iCallback) {
+            mIOnBackInvokedCallback = iCallback;
+        }
+
+        @Override
+        public void onBackInvoked() {
+            try {
+                if (mIOnBackInvokedCallback != null) {
+                    mIOnBackInvokedCallback.onBackInvoked();
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
+            }
+        }
+
+        IOnBackInvokedCallback getIOnBackInvokedCallback() {
+            return mIOnBackInvokedCallback;
+        }
+    }
+}
diff --git a/core/java/android/window/OnBackInvokedDispatcher.java b/core/java/android/window/OnBackInvokedDispatcher.java
index 6bc2b50..3539049a 100644
--- a/core/java/android/window/OnBackInvokedDispatcher.java
+++ b/core/java/android/window/OnBackInvokedDispatcher.java
@@ -96,4 +96,19 @@
      * @hide
      */
     default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { }
+
+
+    /**
+     * Sets an {@link ImeOnBackInvokedDispatcher} to forward {@link OnBackInvokedCallback}s
+     * from IME to the app process to be registered on the app window.
+     *
+     * Only call this on the IME window. Create the {@link ImeOnBackInvokedDispatcher} from
+     * the application process and override
+     * {@link ImeOnBackInvokedDispatcher#getReceivingDispatcher()} to point to the app
+     * window's {@link WindowOnBackInvokedDispatcher}.
+     *
+     * @hide
+     */
+    default void setImeOnBackInvokedDispatcher(
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { }
 }
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 4409397..bedf503 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -49,6 +49,7 @@
     private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>();
     private final Object mLock = new Object();
     private OnBackInvokedDispatcher mActualDispatcher = null;
+    private ImeOnBackInvokedDispatcher mImeDispatcher;
 
     @Override
     public void registerOnBackInvokedCallback(
@@ -108,6 +109,9 @@
             Log.v(TAG, String.format("Proxy transferring %d callbacks to %s", mCallbacks.size(),
                     mActualDispatcher));
         }
+        if (mImeDispatcher != null) {
+            mActualDispatcher.setImeOnBackInvokedDispatcher(mImeDispatcher);
+        }
         for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
             int priority = callbackPair.second;
             if (priority >= 0) {
@@ -117,6 +121,7 @@
             }
         }
         mCallbacks.clear();
+        mImeDispatcher = null;
     }
 
     private void clearCallbacksOnDispatcher() {
@@ -142,6 +147,7 @@
         }
         synchronized (mLock) {
             mCallbacks.clear();
+            mImeDispatcher = null;
         }
     }
 
@@ -169,4 +175,14 @@
             transferCallbacksToDispatcher();
         }
     }
+
+    @Override
+    public void setImeOnBackInvokedDispatcher(
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+        if (mActualDispatcher != null) {
+            mActualDispatcher.setImeOnBackInvokedDispatcher(imeDispatcher);
+        } else {
+            mImeDispatcher = imeDispatcher;
+        }
+    }
 }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 781859c..edfdbcc 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -55,6 +55,8 @@
             .getInt("persist.wm.debug.predictive_back", 1) != 0;
     private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
             .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
+    @Nullable
+    private ImeOnBackInvokedDispatcher mImeDispatcher;
 
     /** Convenience hashmap to quickly decide if a callback has been added. */
     private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -94,6 +96,10 @@
 
     private void registerOnBackInvokedCallbackUnchecked(
             @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+        if (mImeDispatcher != null) {
+            mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
+            return;
+        }
         if (!mOnBackInvokedCallbacks.containsKey(priority)) {
             mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
         }
@@ -120,6 +126,10 @@
 
     @Override
     public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+        if (mImeDispatcher != null) {
+            mImeDispatcher.unregisterOnBackInvokedCallback(callback);
+            return;
+        }
         if (!mAllCallbacks.containsKey(callback)) {
             if (DEBUG) {
                 Log.i(TAG, "Callback not found. returning...");
@@ -153,6 +163,9 @@
         }
         mAllCallbacks.clear();
         mOnBackInvokedCallbacks.clear();
+        if (mImeDispatcher != null) {
+            mImeDispatcher = null;
+        }
     }
 
     private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
@@ -160,14 +173,18 @@
             return;
         }
         try {
-            if (callback == null) {
-                mWindowSession.setOnBackInvokedCallbackInfo(mWindow, null);
-            } else {
+            OnBackInvokedCallbackInfo callbackInfo = null;
+            if (callback != null) {
                 int priority = mAllCallbacks.get(callback);
-                mWindowSession.setOnBackInvokedCallbackInfo(
-                        mWindow, new OnBackInvokedCallbackInfo(
-                                new OnBackInvokedCallbackWrapper(callback), priority));
+                final IOnBackInvokedCallback iCallback =
+                        callback instanceof ImeOnBackInvokedDispatcher
+                                    .ImeOnBackInvokedCallback
+                                ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
+                                        callback).getIOnBackInvokedCallback()
+                                : new OnBackInvokedCallbackWrapper(callback);
+                callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority);
             }
+            mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
             if (DEBUG && callback == null) {
                 Log.d(TAG, TextUtils.formatSimple("setTopOnBackInvokedCallback(null) Callers:%s",
                         Debug.getCallers(5, "  ")));
@@ -190,7 +207,7 @@
         return null;
     }
 
-    private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+    static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
         private final WeakReference<OnBackInvokedCallback> mCallback;
 
         OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
@@ -270,4 +287,10 @@
 
         return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK);
     }
+
+    @Override
+    public void setImeOnBackInvokedDispatcher(
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+        mImeDispatcher = imeDispatcher;
+    }
 }
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 7db4243..0976f45 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -21,8 +21,10 @@
 
 import android.annotation.AnyThread;
 import android.annotation.BinderThread;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.app.IWindowToken;
 import android.app.ResourcesManager;
 import android.content.Context;
@@ -33,7 +35,6 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IWindowManager;
@@ -42,6 +43,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.lang.ref.WeakReference;
 
@@ -76,7 +78,7 @@
 
     private boolean mAttachToWindowContainer;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Handler mHandler = ActivityThread.currentActivityThread().getHandler();
 
     /**
      * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
@@ -188,8 +190,8 @@
     @BinderThread
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
-        mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId,
-                true /* shouldReportConfigChange */));
+        mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
+                newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
     }
 
     // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
@@ -279,12 +281,16 @@
     @BinderThread
     @Override
     public void onWindowTokenRemoved() {
-        mHandler.post(() -> {
-            final Context context = mContextRef.get();
-            if (context != null) {
-                context.destroy();
-                mContextRef.clear();
-            }
-        });
+        mHandler.post(PooledLambda.obtainRunnable(
+                WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse());
+    }
+
+    @MainThread
+    private void onWindowTokenRemovedInner() {
+        final Context context = mContextRef.get();
+        if (context != null) {
+            context.destroy();
+            mContextRef.clear();
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3fee914..781b6d5 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -971,7 +971,8 @@
             getChooserActivityLogger().logShareTargetSelected(
                     SELECTION_TYPE_COPY,
                     "",
-                    -1);
+                    -1,
+                    false);
 
             setResult(RESULT_OK);
             finish();
@@ -1155,7 +1156,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_NEARBY,
                             "",
-                            -1);
+                            -1,
+                            false);
                     // Action bar is user-independent, always start as primary
                     safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
                     finish();
@@ -1177,7 +1179,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_EDIT,
                             "",
-                            -1);
+                            -1,
+                            false);
                     // Action bar is user-independent, always start as primary
                     safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
                     finish();
@@ -1754,7 +1757,8 @@
                             target.getComponentName().getPackageName()
                                     + target.getTitle().toString(),
                             mMaxHashSaltDays);
-                    directTargetAlsoRanked = getRankedPosition((SelectableTargetInfo) targetInfo);
+                    SelectableTargetInfo selectableTargetInfo = (SelectableTargetInfo) targetInfo;
+                    directTargetAlsoRanked = getRankedPosition(selectableTargetInfo);
 
                     if (mCallerChooserTargets != null) {
                         numCallerProvided = mCallerChooserTargets.length;
@@ -1762,7 +1766,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_SERVICE,
                             targetInfo.getResolveInfo().activityInfo.processName,
-                            value
+                            value,
+                            selectableTargetInfo.isPinned()
                     );
                     break;
                 case ChooserListAdapter.TARGET_CALLER:
@@ -1773,7 +1778,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_APP,
                             targetInfo.getResolveInfo().activityInfo.processName,
-                            value
+                            value,
+                            targetInfo.isPinned()
                     );
                     break;
                 case ChooserListAdapter.TARGET_STANDARD_AZ:
@@ -1784,7 +1790,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_STANDARD,
                             targetInfo.getResolveInfo().activityInfo.processName,
-                            value
+                            value,
+                            false
                     );
                     break;
             }
diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java
index 3217307..bb7d50a 100644
--- a/core/java/com/android/internal/app/ChooserActivityLogger.java
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -34,7 +34,8 @@
             int appProvidedApp, boolean isWorkprofile, int previewType, String intent);
 
     /** Logs a UiEventReported event for the system sharesheet when the user selects a target. */
-    void logShareTargetSelected(int targetType, String packageName, int positionPicked);
+    void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+            boolean isPinned);
 
     /** Logs a UiEventReported event for the system sharesheet being triggered by the user. */
     default void logSharesheetTriggered() {
diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
index 48bdba3..e3cc4f1 100644
--- a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
+++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
@@ -51,12 +51,14 @@
     }
 
     @Override
-    public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+    public void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+            boolean isPinned) {
         FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
                 /* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(),
                 /* package_name = 2 */ packageName,
                 /* instance_id = 3 */ getInstanceId().getId(),
-                /* position_picked = 4 */ positionPicked);
+                /* position_picked = 4 */ positionPicked,
+                /* is_pinned = 5 */ isPinned);
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index c5b21ac..e7f80a7 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -589,7 +589,7 @@
                 MetricsLogger metricsLogger = new MetricsLogger();
                 LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED);
                 log.setComponentName(mRankerServiceName);
-                log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed);
+                log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed ? 1 : 0);
                 log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos);
                 metricsLogger.write(log);
             }
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index 983e0fe..ffbf646 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -69,7 +69,8 @@
                     /* event_id = 1 */ eventID,
                     /* package_name = 2 */ packageName,
                     /* instance_id = 3 */ 0,
-                    /* position_picked = 4 */ position);
+                    /* position_picked = 4 */ position,
+                    /* is_pinned = 5 */ false);
         }
     }
 
@@ -82,7 +83,8 @@
                     /* event_id = 1 */ eventID,
                     /* package_name = 2 */ packageName,
                     /* instance_id = 3 */ instance.getId(),
-                    /* position_picked = 4 */ position);
+                    /* position_picked = 4 */ position,
+                    /* is_pinned = 5 */ false);
         } else if ((eventID > 0)) {
             logWithPosition(event, uid, packageName, position);
         }
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index 397b2c0..62dea9d 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -137,6 +137,18 @@
      */
     public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
             int maxHeight) {
+        return buildScaledBitmap(drawable, maxWidth, maxHeight, false);
+    }
+
+    /**
+     * Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
+     *
+     * @param allowUpscaling if true, the drawable will not only be scaled down, but also scaled up
+     *                       to fit within the maximum size given. This is useful for converting
+     *                       vectorized icons which usually have a very small intrinsic size.
+     */
+    public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
+            int maxHeight, boolean allowUpscaling) {
         if (drawable == null) {
             return null;
         }
@@ -155,7 +167,9 @@
         // a large notification icon if necessary
         float ratio = Math.min((float) maxWidth / (float) originalWidth,
                 (float) maxHeight / (float) originalHeight);
-        ratio = Math.min(1.0f, ratio);
+        if (!allowUpscaling) {
+            ratio = Math.min(1.0f, ratio);
+        }
         int scaledWidth = (int) (ratio * originalWidth);
         int scaledHeight = (int) (ratio * originalHeight);
         Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 40d89db..4e2526a 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -23,6 +23,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.IInputContext;
@@ -47,7 +48,8 @@
     void unbindInput();
 
     void startInput(in IBinder startInputToken, in IInputContext inputContext,
-            in EditorInfo attribute, boolean restarting, int navigationBarFlags);
+            in EditorInfo attribute, boolean restarting, int navigationBarFlags,
+            in ImeOnBackInvokedDispatcher imeDispatcher);
 
     void onNavButtonFlagsChanged(int navButtonFlags);
 
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index d7bb2cb..3157760 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -20,6 +20,7 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.EditorInfo;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.InputBindResult;
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
@@ -57,7 +58,7 @@
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, in EditorInfo attribute, in IInputContext inputContext,
             in IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion);
+            int unverifiedTargetSdkVersion, in ImeOnBackInvokedDispatcher imeDispatcher);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index c0f7b41..4af28ea 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -47,6 +47,43 @@
     return env;
 }
 
+  struct {
+    jmethodID onTransactionHang;
+} gTransactionHangCallback;
+
+class TransactionHangCallbackWrapper : public LightRefBase<TransactionHangCallbackWrapper> {
+public:
+    explicit TransactionHangCallbackWrapper(JNIEnv* env, jobject jobject) {
+        env->GetJavaVM(&mVm);
+        mTransactionHangObject = env->NewGlobalRef(jobject);
+        LOG_ALWAYS_FATAL_IF(!mTransactionHangObject, "Failed to make global ref");
+    }
+
+    ~TransactionHangCallbackWrapper() {
+        if (mTransactionHangObject) {
+            getenv()->DeleteGlobalRef(mTransactionHangObject);
+            mTransactionHangObject = nullptr;
+        }
+    }
+
+    void onTransactionHang(bool isGpuHang) {
+        if (mTransactionHangObject) {
+            getenv()->CallVoidMethod(mTransactionHangObject,
+                                     gTransactionHangCallback.onTransactionHang, isGpuHang);
+        }
+    }
+
+private:
+    JavaVM* mVm;
+    jobject mTransactionHangObject;
+
+    JNIEnv* getenv() {
+        JNIEnv* env;
+        mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+        return env;
+    }
+};
+
 static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
                           jboolean updateDestinationFrame) {
     ScopedUtfChars name(env, jName);
@@ -141,6 +178,20 @@
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
     return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
 }
+  
+static void nativeSetTransactionHangCallback(JNIEnv* env, jclass clazz, jlong ptr,
+                                             jobject transactionHangCallback) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    if (transactionHangCallback == nullptr) {
+        queue->setTransactionHangCallback(nullptr);
+    } else {
+        sp<TransactionHangCallbackWrapper> wrapper =
+                new TransactionHangCallbackWrapper{env, transactionHangCallback};
+        queue->setTransactionHangCallback([wrapper](bool isGpuHang) {
+            wrapper->onTransactionHang(isGpuHang);
+        });
+    }
+}
 
 static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr,
                                                jlong frameNum) {
@@ -163,7 +214,10 @@
         {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
         {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
         {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
-        {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions}
+        {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions},
+        {"nativeSetTransactionHangCallback",
+         "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V",
+         (void*)nativeSetTransactionHangCallback},
         // clang-format on
 };
 
@@ -180,6 +234,11 @@
     jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
     gTransactionConsumer.accept =
             GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
+    jclass transactionHangClass =
+            FindClassOrDie(env, "android/graphics/BLASTBufferQueue$TransactionHangCallback");
+    gTransactionHangCallback.onTransactionHang =
+            GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang", "(Z)V");
+
     return 0;
 }
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index edaf8cf..689ff66 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5451,6 +5451,8 @@
     <bool name="config_supportsHardwareCamToggle">false</bool>
     <!-- Whether a camera intent is launched when the lens cover is toggled -->
     <bool name="config_launchCameraOnCameraLensCoverToggle">true</bool>
+    <!-- Whether changing sensor privacy SW setting requires device to be unlocked -->
+    <bool name="config_sensorPrivacyRequiresAuthentication">true</bool>
 
     <!-- List containing the allowed install sources for accessibility service. -->
     <string-array name="config_accessibility_allowed_install_source" translatable="false"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b882123..443f9a6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4661,6 +4661,7 @@
   <java-symbol type="bool" name="config_supportsHardwareMicToggle" />
   <java-symbol type="bool" name="config_supportsHardwareCamToggle" />
   <java-symbol type="bool" name="config_launchCameraOnCameraLensCoverToggle" />
+  <java-symbol type="bool" name="config_sensorPrivacyRequiresAuthentication" />
 
   <java-symbol type="dimen" name="starting_surface_icon_size" />
   <java-symbol type="dimen" name="starting_surface_default_icon_size" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
index 2ecc261..0dca638 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
@@ -44,6 +44,7 @@
         // share completed fields
         public int targetType;
         public int positionPicked;
+        public boolean isPinned;
 
         CallRecord(int atomId, UiEventLogger.UiEventEnum eventId,
                 String packageName, InstanceId instanceId) {
@@ -68,12 +69,13 @@
         }
 
         CallRecord(int atomId, String packageName, InstanceId instanceId, int targetType,
-                int positionPicked) {
+                int positionPicked, boolean isPinned) {
             this.atomId = atomId;
             this.packageName = packageName;
             this.instanceId = instanceId;
             this.targetType = targetType;
             this.positionPicked = positionPicked;
+            this.isPinned = isPinned;
         }
 
     }
@@ -112,9 +114,11 @@
     }
 
     @Override
-    public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+    public void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+            boolean isPinned) {
         mCalls.add(new CallRecord(FrameworkStatsLog.RANKING_SELECTED, packageName, getInstanceId(),
-                SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked));
+                SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked,
+                isPinned));
     }
 
     @Override
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 891c82d..12d3d64 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2485,6 +2485,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "323235828": {
+      "message": "Delaying app transition for recents animation to finish",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/AppTransitionController.java"
+    },
     "327461496": {
       "message": "Complete pause: %s",
       "level": "VERBOSE",
@@ -2575,12 +2581,6 @@
       "group": "WM_DEBUG_ANIM",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
-    "397105698": {
-      "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "397382873": {
       "message": "Moving to PAUSED: %s %s",
       "level": "VERBOSE",
@@ -3109,6 +3109,12 @@
       "group": "WM_DEBUG_LOCKTASK",
       "at": "com\/android\/server\/wm\/LockTaskController.java"
     },
+    "958338552": {
+      "message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "959486822": {
       "message": "setSyncGroup #%d on %s",
       "level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl
index 13b4096..54f8808 100644
--- a/data/keyboards/Vendor_0957_Product_0001.kl
+++ b/data/keyboards/Vendor_0957_Product_0001.kl
@@ -44,33 +44,37 @@
 key 11    0
 
 # custom keys
-key usage 0x000c019C    PROFILE_SWITCH
-key usage 0x000c01A2    ALL_APPS
 key usage 0x000c01BB    TV_INPUT
-key usage 0x000c022A    BOOKMARK
-key usage 0x000c0096    SETTINGS
-key usage 0x000c009F    NOTIFICATION
-key usage 0x000c008D    GUIDE
-key usage 0x000c0089    TV
-key usage 0x000c009C    CHANNEL_UP
-key usage 0x000c009D    CHANNEL_DOWN
-key usage 0x000c00CD    MEDIA_PLAY_PAUSE
-key usage 0x000c00B2    MEDIA_RECORD
-key usage 0x000c00B4    MEDIA_SKIP_BACKWARD
-key usage 0x000c00B3    MEDIA_SKIP_FORWARD
-key usage 0x000c0226    MEDIA_STOP
 
-key usage 0x000c0077    BUTTON_3     WAKE #YouTube
-key usage 0x000c0078    BUTTON_4     WAKE #Netflix
-key usage 0x000c0079    BUTTON_6     WAKE #Disney+
-key usage 0x000c007A    BUTTON_7     WAKE #HBOmax
-
-key usage 0x00070037    PERIOD
-key usage 0x000c01BD    INFO
-key usage 0x000c0061    CAPTIONS
 key usage 0x000c0185    TV_TELETEXT
+key usage 0x000c0061    CAPTIONS
+
+key usage 0x000c01BD    INFO
+key usage 0x000c0037    PERIOD
 
 key usage 0x000c0069    PROG_RED
 key usage 0x000c006A    PROG_GREEN
+key usage 0x000c006C    PROG_YELLOW
 key usage 0x000c006B    PROG_BLUE
-key usage 0x000c006C    PROG_YELLOW
\ No newline at end of file
+key usage 0x000c00B4    MEDIA_SKIP_BACKWARD
+key usage 0x000c00CD    MEDIA_PLAY_PAUSE
+key usage 0x000c00B2    MEDIA_RECORD
+key usage 0x000c00B3    MEDIA_SKIP_FORWARD
+
+key usage 0x000c022A    BOOKMARK
+key usage 0x000c01A2    ALL_APPS
+key usage 0x000c019C    PROFILE_SWITCH
+
+key usage 0x000c0096    SETTINGS
+key usage 0x000c009F    NOTIFICATION
+
+key usage 0x000c008D    GUIDE
+key usage 0x000c0089    TV
+
+key usage 0x000c009C    CHANNEL_UP
+key usage 0x000c009D    CHANNEL_DOWN
+
+key usage 0x000c0077    BUTTON_3     WAKE #YouTube
+key usage 0x000c0078    BUTTON_4     WAKE #Netflix
+key usage 0x000c0079    BUTTON_6     WAKE
+key usage 0x000c007A    BUTTON_7     WAKE
\ No newline at end of file
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 4b723d1..1c41d06 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -43,6 +43,12 @@
     private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr);
     private static native SurfaceControl.Transaction nativeGatherPendingTransactions(long ptr,
             long frameNumber);
+    private static native void nativeSetTransactionHangCallback(long ptr,
+            TransactionHangCallback callback);
+
+    public interface TransactionHangCallback {
+        void onTransactionHang(boolean isGpuHang);
+    }
 
     /** Create a new connection with the surface flinger. */
     public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@@ -184,4 +190,8 @@
     public SurfaceControl.Transaction gatherPendingTransactions(long frameNumber) {
         return nativeGatherPendingTransactions(mNativeObject, frameNumber);
     }
+
+    public void setTransactionHangCallback(TransactionHangCallback hangCallback) {
+        nativeSetTransactionHangCallback(mNativeObject, hangCallback);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index c3fbe55..8fa9f56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -246,9 +246,13 @@
      * {@link BackAnimationController}
      */
     public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) {
-        if (action == MotionEvent.ACTION_DOWN) {
-            initAnimation(event);
-        } else if (action == MotionEvent.ACTION_MOVE) {
+        if (action == MotionEvent.ACTION_MOVE) {
+            if (!mBackGestureStarted) {
+                // Let the animation initialized here to make sure the onPointerDownOutsideFocus
+                // could be happened when ACTION_DOWN, it may change the current focus that we
+                // would access it when startBackNavigation.
+                initAnimation(event);
+            }
             onMove(event, swipeEdge);
         } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 4b125b1..6305959 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -24,6 +24,7 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.GestureDetector;
@@ -37,6 +38,8 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
@@ -80,7 +83,6 @@
     private final Rect mTempRect = new Rect();
     private FrameLayout mDividerBar;
 
-
     static final Property<DividerView, Integer> DIVIDER_HEIGHT_PROPERTY =
             new Property<DividerView, Integer>(Integer.class, "height") {
                 @Override
@@ -109,6 +111,74 @@
         }
     };
 
+    private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            final DividerSnapAlgorithm snapAlgorithm = mSplitLayout.mDividerSnapAlgorithm;
+            if (isLandscape()) {
+                info.addAction(new AccessibilityAction(R.id.action_move_tl_full,
+                        mContext.getString(R.string.accessibility_action_divider_left_full)));
+                if (snapAlgorithm.isFirstSplitTargetAvailable()) {
+                    info.addAction(new AccessibilityAction(R.id.action_move_tl_70,
+                            mContext.getString(R.string.accessibility_action_divider_left_70)));
+                }
+                if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) {
+                    // Only show the middle target if there are more than 1 split target
+                    info.addAction(new AccessibilityAction(R.id.action_move_tl_50,
+                            mContext.getString(R.string.accessibility_action_divider_left_50)));
+                }
+                if (snapAlgorithm.isLastSplitTargetAvailable()) {
+                    info.addAction(new AccessibilityAction(R.id.action_move_tl_30,
+                            mContext.getString(R.string.accessibility_action_divider_left_30)));
+                }
+                info.addAction(new AccessibilityAction(R.id.action_move_rb_full,
+                        mContext.getString(R.string.accessibility_action_divider_right_full)));
+            } else {
+                info.addAction(new AccessibilityAction(R.id.action_move_tl_full,
+                        mContext.getString(R.string.accessibility_action_divider_top_full)));
+                if (snapAlgorithm.isFirstSplitTargetAvailable()) {
+                    info.addAction(new AccessibilityAction(R.id.action_move_tl_70,
+                            mContext.getString(R.string.accessibility_action_divider_top_70)));
+                }
+                if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) {
+                    // Only show the middle target if there are more than 1 split target
+                    info.addAction(new AccessibilityAction(R.id.action_move_tl_50,
+                            mContext.getString(R.string.accessibility_action_divider_top_50)));
+                }
+                if (snapAlgorithm.isLastSplitTargetAvailable()) {
+                    info.addAction(new AccessibilityAction(R.id.action_move_tl_30,
+                            mContext.getString(R.string.accessibility_action_divider_top_30)));
+                }
+                info.addAction(new AccessibilityAction(R.id.action_move_rb_full,
+                        mContext.getString(R.string.accessibility_action_divider_bottom_full)));
+            }
+        }
+
+        @Override
+        public boolean performAccessibilityAction(@NonNull View host, int action,
+                @Nullable Bundle args) {
+            DividerSnapAlgorithm.SnapTarget nextTarget = null;
+            DividerSnapAlgorithm snapAlgorithm = mSplitLayout.mDividerSnapAlgorithm;
+            if (action == R.id.action_move_tl_full) {
+                nextTarget = snapAlgorithm.getDismissEndTarget();
+            } else if (action == R.id.action_move_tl_70) {
+                nextTarget = snapAlgorithm.getLastSplitTarget();
+            } else if (action == R.id.action_move_tl_50) {
+                nextTarget = snapAlgorithm.getMiddleTarget();
+            } else if (action == R.id.action_move_tl_30) {
+                nextTarget = snapAlgorithm.getFirstSplitTarget();
+            } else if (action == R.id.action_move_rb_full) {
+                nextTarget = snapAlgorithm.getDismissStartTarget();
+            }
+            if (nextTarget != null) {
+                mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), nextTarget);
+                return true;
+            }
+            return super.performAccessibilityAction(host, action, args);
+        }
+    };
+
     public DividerView(@NonNull Context context) {
         super(context);
     }
@@ -179,6 +249,7 @@
         mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener());
         mInteractive = true;
         setOnTouchListener(this);
+        mHandle.setAccessibilityDelegate(mHandleDelegate);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 72c8141..dfd4362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -29,6 +29,7 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -63,6 +64,7 @@
             Context context,
             TvPipBoundsState tvPipBoundsState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
+            PipAppOpsListener pipAppOpsListener,
             PipTaskOrganizer pipTaskOrganizer,
             TvPipMenuController tvPipMenuController,
             PipMediaController pipMediaController,
@@ -79,6 +81,7 @@
                         context,
                         tvPipBoundsState,
                         tvPipBoundsAlgorithm,
+                        pipAppOpsListener,
                         pipTaskOrganizer,
                         pipTransitionController,
                         tvPipMenuController,
@@ -140,8 +143,11 @@
     @Provides
     static TvPipNotificationController provideTvPipNotificationController(Context context,
             PipMediaController pipMediaController,
+            PipParamsChangedForwarder pipParamsChangedForwarder,
+            TvPipBoundsState tvPipBoundsState,
             @ShellMainThread Handler mainHandler) {
-        return new TvPipNotificationController(context, pipMediaController, mainHandler);
+        return new TvPipNotificationController(context, pipMediaController,
+                pipParamsChangedForwarder, tvPipBoundsState, mainHandler);
     }
 
     @WMSingleton
@@ -185,4 +191,12 @@
     static PipParamsChangedForwarder providePipParamsChangedForwarder() {
         return new PipParamsChangedForwarder();
     }
+
+    @WMSingleton
+    @Provides
+    static PipAppOpsListener providePipAppOpsListener(Context context,
+            PipTaskOrganizer pipTaskOrganizer,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        return new PipAppOpsListener(context, pipTaskOrganizer::removePip, mainExecutor);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 3335673..db6131a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -78,7 +78,6 @@
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.phone.PipAppOpsListener;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.recents.RecentTasksController;
@@ -435,14 +434,6 @@
         return new FloatingContentCoordinator();
     }
 
-    @WMSingleton
-    @Provides
-    static PipAppOpsListener providePipAppOpsListener(Context context,
-            PipTouchHandler pipTouchHandler,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
-    }
-
     // Needs handler for registering broadcast receivers
     @WMSingleton
     @Provides
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 18a7215..1bc9e31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -51,6 +51,7 @@
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
@@ -63,7 +64,6 @@
 import com.android.wm.shell.pip.PipTransitionState;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
-import com.android.wm.shell.pip.phone.PipAppOpsListener;
 import com.android.wm.shell.pip.phone.PipController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
@@ -325,6 +325,14 @@
 
     @WMSingleton
     @Provides
+    static PipAppOpsListener providePipAppOpsListener(Context context,
+            PipTouchHandler pipTouchHandler,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
+    }
+
+    @WMSingleton
+    @Provides
     static PipMotionHelper providePipMotionHelper(Context context,
             PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
             PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java
index d97d2d6..48a3fc2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAppOpsListener.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.pip.phone;
+package com.android.wm.shell.pip;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
@@ -28,7 +28,6 @@
 import android.util.Pair;
 
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.pip.PipUtils;
 
 public class PipAppOpsListener {
     private static final String TAG = PipAppOpsListener.class.getSimpleName();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 8a50f22..65a12d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -32,6 +32,7 @@
 import android.graphics.drawable.Icon;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
+import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.Handler;
@@ -64,7 +65,7 @@
      */
     public interface ActionListener {
         /**
-         * Called when the media actions changes.
+         * Called when the media actions changed.
          */
         void onMediaActionsChanged(List<RemoteAction> actions);
     }
@@ -74,11 +75,21 @@
      */
     public interface MetadataListener {
         /**
-         * Called when the media metadata changes.
+         * Called when the media metadata changed.
          */
         void onMediaMetadataChanged(MediaMetadata metadata);
     }
 
+    /**
+     * A listener interface to receive notification on changes to the media session token.
+     */
+    public interface TokenListener {
+        /**
+         * Called when the media session token changed.
+         */
+        void onMediaSessionTokenChanged(MediaSession.Token token);
+    }
+
     private final Context mContext;
     private final Handler mMainHandler;
     private final HandlerExecutor mHandlerExecutor;
@@ -133,6 +144,7 @@
 
     private final ArrayList<ActionListener> mActionListeners = new ArrayList<>();
     private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>();
+    private final ArrayList<TokenListener> mTokenListeners = new ArrayList<>();
 
     public PipMediaController(Context context, Handler mainHandler) {
         mContext = context;
@@ -204,6 +216,31 @@
         mMetadataListeners.remove(listener);
     }
 
+    /**
+     * Adds a new token listener.
+     */
+    public void addTokenListener(TokenListener listener) {
+        if (!mTokenListeners.contains(listener)) {
+            mTokenListeners.add(listener);
+            listener.onMediaSessionTokenChanged(getToken());
+        }
+    }
+
+    /**
+     * Removes a token listener.
+     */
+    public void removeTokenListener(TokenListener listener) {
+        listener.onMediaSessionTokenChanged(null);
+        mTokenListeners.remove(listener);
+    }
+
+    private MediaSession.Token getToken() {
+        if (mMediaController == null) {
+            return null;
+        }
+        return mMediaController.getSessionToken();
+    }
+
     private MediaMetadata getMediaMetadata() {
         return mMediaController != null ? mMediaController.getMetadata() : null;
     }
@@ -294,6 +331,7 @@
             }
             notifyActionsChanged();
             notifyMetadataChanged(getMediaMetadata());
+            notifyTokenChanged(getToken());
 
             // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
         }
@@ -317,4 +355,10 @@
             mMetadataListeners.forEach(l -> l.onMediaMetadataChanged(metadata));
         }
     }
+
+    private void notifyTokenChanged(MediaSession.Token token) {
+        if (!mTokenListeners.isEmpty()) {
+            mTokenListeners.forEach(l -> l.onMediaSessionTokenChanged(token));
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 7df42e0..42ceb42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -76,6 +76,7 @@
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index e9b6bab..5a21e07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -44,6 +44,7 @@
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 8326588..7667794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -45,6 +45,7 @@
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
@@ -97,6 +98,7 @@
 
     private final TvPipBoundsState mTvPipBoundsState;
     private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
+    private final PipAppOpsListener mAppOpsListener;
     private final PipTaskOrganizer mPipTaskOrganizer;
     private final PipMediaController mPipMediaController;
     private final TvPipNotificationController mPipNotificationController;
@@ -121,6 +123,7 @@
             Context context,
             TvPipBoundsState tvPipBoundsState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
+            PipAppOpsListener pipAppOpsListener,
             PipTaskOrganizer pipTaskOrganizer,
             PipTransitionController pipTransitionController,
             TvPipMenuController tvPipMenuController,
@@ -136,6 +139,7 @@
                 context,
                 tvPipBoundsState,
                 tvPipBoundsAlgorithm,
+                pipAppOpsListener,
                 pipTaskOrganizer,
                 pipTransitionController,
                 tvPipMenuController,
@@ -153,6 +157,7 @@
             Context context,
             TvPipBoundsState tvPipBoundsState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
+            PipAppOpsListener pipAppOpsListener,
             PipTaskOrganizer pipTaskOrganizer,
             PipTransitionController pipTransitionController,
             TvPipMenuController tvPipMenuController,
@@ -181,6 +186,7 @@
         mTvPipMenuController = tvPipMenuController;
         mTvPipMenuController.setDelegate(this);
 
+        mAppOpsListener = pipAppOpsListener;
         mPipTaskOrganizer = pipTaskOrganizer;
         pipTransitionController.registerPipTransitionCallback(this);
 
@@ -287,6 +293,8 @@
         }
         mTvPipBoundsState.setTvPipManuallyCollapsed(!expanding);
         mTvPipBoundsState.setTvPipExpanded(expanding);
+        mPipNotificationController.updateExpansionState();
+
         updatePinnedStackBounds();
     }
 
@@ -521,6 +529,12 @@
             @Override
             public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
                 checkIfPinnedTaskAppeared();
+                mAppOpsListener.onActivityPinned(packageName);
+            }
+
+            @Override
+            public void onActivityUnpinned() {
+                mAppOpsListener.onActivityUnpinned();
             }
 
             @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 868e456..320c05c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -494,6 +494,14 @@
         setFrameHighlighted(false);
     }
 
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        super.onWindowFocusChanged(hasWindowFocus);
+        if (!hasWindowFocus) {
+            hideAllUserControls();
+        }
+    }
+
     private void animateAlphaTo(float alpha, View view) {
         if (view.getAlpha() == alpha) {
             return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 4033f03..61a609d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -16,36 +16,47 @@
 
 package com.android.wm.shell.pip.tv;
 
+import static android.app.Notification.Action.SEMANTIC_ACTION_DELETE;
+import static android.app.Notification.Action.SEMANTIC_ACTION_NONE;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.media.MediaMetadata;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.Bundle;
 import android.os.Handler;
 import android.text.TextUtils;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ImageUtils;
 import com.android.wm.shell.R;
 import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
+import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
-import java.util.Objects;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * A notification that informs users that PIP is running and also provides PIP controls.
- * <p>Once it's created, it will manage the PIP notification UI by itself except for handling
- * configuration changes.
+ * A notification that informs users that PiP is running and also provides PiP controls.
+ * <p>Once it's created, it will manage the PiP notification UI by itself except for handling
+ * configuration changes and user initiated expanded PiP toggling.
  */
 public class TvPipNotificationController {
     private static final String TAG = "TvPipNotification";
-    private static final boolean DEBUG = TvPipController.DEBUG;
 
     // Referenced in com.android.systemui.util.NotificationChannels.
     public static final String NOTIFICATION_CHANNEL = "TVPIP";
@@ -60,6 +71,8 @@
             "com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
     private static final String ACTION_TOGGLE_EXPANDED_PIP =
             "com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
+    private static final String ACTION_FULLSCREEN =
+            "com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
 
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -68,44 +81,88 @@
     private final ActionBroadcastReceiver mActionBroadcastReceiver;
     private final Handler mMainHandler;
     private Delegate mDelegate;
+    private final TvPipBoundsState mTvPipBoundsState;
 
     private String mDefaultTitle;
 
+    private final List<RemoteAction> mCustomActions = new ArrayList<>();
+    private final List<RemoteAction> mMediaActions = new ArrayList<>();
+    private RemoteAction mCustomCloseAction;
+
+    private MediaSession.Token mMediaSessionToken;
+
     /** Package name for the application that owns PiP window. */
     private String mPackageName;
-    private boolean mNotified;
-    private String mMediaTitle;
-    private Bitmap mArt;
+
+    private boolean mIsNotificationShown;
+    private String mPipTitle;
+    private String mPipSubtitle;
+
+    private Bitmap mActivityIcon;
 
     public TvPipNotificationController(Context context, PipMediaController pipMediaController,
+            PipParamsChangedForwarder pipParamsChangedForwarder, TvPipBoundsState tvPipBoundsState,
             Handler mainHandler) {
         mContext = context;
         mPackageManager = context.getPackageManager();
         mNotificationManager = context.getSystemService(NotificationManager.class);
         mMainHandler = mainHandler;
+        mTvPipBoundsState = tvPipBoundsState;
 
         mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL)
                 .setLocalOnly(true)
-                .setOngoing(false)
+                .setOngoing(true)
                 .setCategory(Notification.CATEGORY_SYSTEM)
                 .setShowWhen(true)
                 .setSmallIcon(R.drawable.pip_icon)
+                .setAllowSystemGeneratedContextualActions(false)
+                .setContentIntent(createPendingIntent(context, ACTION_FULLSCREEN))
+                .setDeleteIntent(getCloseAction().actionIntent)
                 .extend(new Notification.TvExtender()
                         .setContentIntent(createPendingIntent(context, ACTION_SHOW_PIP_MENU))
                         .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE_PIP)));
 
         mActionBroadcastReceiver = new ActionBroadcastReceiver();
 
-        pipMediaController.addMetadataListener(this::onMediaMetadataChanged);
+        pipMediaController.addActionListener(this::onMediaActionsChanged);
+        pipMediaController.addTokenListener(this::onMediaSessionTokenChanged);
+
+        pipParamsChangedForwarder.addListener(
+                new PipParamsChangedForwarder.PipParamsChangedCallback() {
+                    @Override
+                    public void onExpandedAspectRatioChanged(float ratio) {
+                        updateExpansionState();
+                    }
+
+                    @Override
+                    public void onActionsChanged(List<RemoteAction> actions,
+                            RemoteAction closeAction) {
+                        mCustomActions.clear();
+                        mCustomActions.addAll(actions);
+                        mCustomCloseAction = closeAction;
+                        updateNotificationContent();
+                    }
+
+                    @Override
+                    public void onTitleChanged(String title) {
+                        mPipTitle = title;
+                        updateNotificationContent();
+                    }
+
+                    @Override
+                    public void onSubtitleChanged(String subtitle) {
+                        mPipSubtitle = subtitle;
+                        updateNotificationContent();
+                    }
+                });
 
         onConfigurationChanged(context);
     }
 
     void setDelegate(Delegate delegate) {
-        if (DEBUG) {
-            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                    "%s: setDelegate(), delegate=%s", TAG, delegate);
-        }
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: setDelegate(), delegate=%s",
+                TAG, delegate);
+
         if (mDelegate != null) {
             throw new IllegalStateException(
                     "The delegate has already been set and should not change.");
@@ -118,90 +175,181 @@
     }
 
     void show(String packageName) {
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: show %s", TAG, packageName);
         if (mDelegate == null) {
             throw new IllegalStateException("Delegate is not set.");
         }
 
+        mIsNotificationShown = true;
         mPackageName = packageName;
-        update();
+        mActivityIcon = getActivityIcon();
         mActionBroadcastReceiver.register();
+
+        updateNotificationContent();
     }
 
     void dismiss() {
-        mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
-        mNotified = false;
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: dismiss()", TAG);
+
+        mIsNotificationShown = false;
         mPackageName = null;
         mActionBroadcastReceiver.unregister();
+
+        mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
     }
 
-    private void onMediaMetadataChanged(MediaMetadata metadata) {
-        if (updateMediaControllerMetadata(metadata) && mNotified) {
-            // update notification
-            update();
+    private Notification.Action getToggleAction(boolean expanded) {
+        if (expanded) {
+            return createSystemAction(R.drawable.pip_ic_collapse,
+                    R.string.pip_collapse, ACTION_TOGGLE_EXPANDED_PIP);
+        } else {
+            return createSystemAction(R.drawable.pip_ic_expand, R.string.pip_expand,
+                    ACTION_TOGGLE_EXPANDED_PIP);
         }
     }
 
+    private Notification.Action createSystemAction(int iconRes, int titleRes, String action) {
+        Notification.Action.Builder builder = new Notification.Action.Builder(
+                Icon.createWithResource(mContext, iconRes),
+                mContext.getString(titleRes),
+                createPendingIntent(mContext, action));
+        builder.setContextual(true);
+        return builder.build();
+    }
+
+    private void onMediaActionsChanged(List<RemoteAction> actions) {
+        mMediaActions.clear();
+        mMediaActions.addAll(actions);
+        if (mCustomActions.isEmpty()) {
+            updateNotificationContent();
+        }
+    }
+
+    private void onMediaSessionTokenChanged(MediaSession.Token token) {
+        mMediaSessionToken = token;
+        updateNotificationContent();
+    }
+
+    private Notification.Action remoteToNotificationAction(RemoteAction action) {
+        return remoteToNotificationAction(action, SEMANTIC_ACTION_NONE);
+    }
+
+    private Notification.Action remoteToNotificationAction(RemoteAction action,
+            int semanticAction) {
+        Notification.Action.Builder builder = new Notification.Action.Builder(action.getIcon(),
+                action.getTitle(),
+                action.getActionIntent());
+        if (action.getContentDescription() != null) {
+            Bundle extras = new Bundle();
+            extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
+                    action.getContentDescription());
+            builder.addExtras(extras);
+        }
+        builder.setSemanticAction(semanticAction);
+        builder.setContextual(true);
+        return builder.build();
+    }
+
+    private Notification.Action[] getNotificationActions() {
+        final List<Notification.Action> actions = new ArrayList<>();
+
+        // 1. Fullscreen
+        actions.add(getFullscreenAction());
+        // 2. Close
+        actions.add(getCloseAction());
+        // 3. App actions
+        final List<RemoteAction> appActions =
+                mCustomActions.isEmpty() ? mMediaActions : mCustomActions;
+        for (RemoteAction appAction : appActions) {
+            if (PipUtils.remoteActionsMatch(mCustomCloseAction, appAction)
+                    || !appAction.isEnabled()) {
+                continue;
+            }
+            actions.add(remoteToNotificationAction(appAction));
+        }
+        // 4. Move
+        actions.add(getMoveAction());
+        // 5. Toggle expansion (if expanded PiP enabled)
+        if (mTvPipBoundsState.getDesiredTvExpandedAspectRatio() > 0
+                && mTvPipBoundsState.isTvExpandedPipSupported()) {
+            actions.add(getToggleAction(mTvPipBoundsState.isTvPipExpanded()));
+        }
+        return actions.toArray(new Notification.Action[0]);
+    }
+
+    private Notification.Action getCloseAction() {
+        if (mCustomCloseAction == null) {
+            return createSystemAction(R.drawable.pip_ic_close_white, R.string.pip_close,
+                    ACTION_CLOSE_PIP);
+        } else {
+            return remoteToNotificationAction(mCustomCloseAction, SEMANTIC_ACTION_DELETE);
+        }
+    }
+
+    private Notification.Action getFullscreenAction() {
+        return createSystemAction(R.drawable.pip_ic_fullscreen_white,
+                R.string.pip_fullscreen, ACTION_FULLSCREEN);
+    }
+
+    private Notification.Action getMoveAction() {
+        return createSystemAction(R.drawable.pip_ic_move_white, R.string.pip_move,
+                ACTION_MOVE_PIP);
+    }
+
     /**
-     * Called by {@link PipController} when the configuration is changed.
+     * Called by {@link TvPipController} when the configuration is changed.
      */
     void onConfigurationChanged(Context context) {
         mDefaultTitle = context.getResources().getString(R.string.pip_notification_unknown_title);
-        if (mNotified) {
-            // Update the notification.
-            update();
-        }
+        updateNotificationContent();
     }
 
-    private void update() {
-        mNotified = true;
+    void updateExpansionState() {
+        updateNotificationContent();
+    }
+
+    private void updateNotificationContent() {
+        if (mPackageManager == null || !mIsNotificationShown) {
+            return;
+        }
+
+        Notification.Action[] actions = getNotificationActions();
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: update(), title: %s, subtitle: %s, mediaSessionToken: %s, #actions: %s", TAG,
+                getNotificationTitle(), mPipSubtitle, mMediaSessionToken, actions.length);
+        for (Notification.Action action : actions) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: action: %s", TAG,
+                    action.toString());
+        }
+
         mNotificationBuilder
                 .setWhen(System.currentTimeMillis())
-                .setContentTitle(getNotificationTitle());
-        if (mArt != null) {
-            mNotificationBuilder.setStyle(new Notification.BigPictureStyle()
-                    .bigPicture(mArt));
-        } else {
-            mNotificationBuilder.setStyle(null);
-        }
+                .setContentTitle(getNotificationTitle())
+                .setContentText(mPipSubtitle)
+                .setSubText(getApplicationLabel(mPackageName))
+                .setActions(actions);
+        setPipIcon();
+
+        Bundle extras = new Bundle();
+        extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, mMediaSessionToken);
+        mNotificationBuilder.setExtras(extras);
+
+        // TvExtender not recognized if not set last.
+        mNotificationBuilder.extend(new Notification.TvExtender()
+                .setContentIntent(createPendingIntent(mContext, ACTION_SHOW_PIP_MENU))
+                .setDeleteIntent(createPendingIntent(mContext, ACTION_CLOSE_PIP)));
         mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP,
                 mNotificationBuilder.build());
     }
 
-    private boolean updateMediaControllerMetadata(MediaMetadata metadata) {
-        String title = null;
-        Bitmap art = null;
-        if (metadata != null) {
-            title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
-            if (TextUtils.isEmpty(title)) {
-                title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
-            }
-            art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
-            if (art == null) {
-                art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
-            }
-        }
-
-        if (TextUtils.equals(title, mMediaTitle) && Objects.equals(art, mArt)) {
-            return false;
-        }
-
-        mMediaTitle = title;
-        mArt = art;
-
-        return true;
-    }
-
-
     private String getNotificationTitle() {
-        if (!TextUtils.isEmpty(mMediaTitle)) {
-            return mMediaTitle;
+        if (!TextUtils.isEmpty(mPipTitle)) {
+            return mPipTitle;
         }
-
         final String applicationTitle = getApplicationLabel(mPackageName);
         if (!TextUtils.isEmpty(applicationTitle)) {
             return applicationTitle;
         }
-
         return mDefaultTitle;
     }
 
@@ -214,10 +362,37 @@
         }
     }
 
+    private void setPipIcon() {
+        if (mActivityIcon != null) {
+            mNotificationBuilder.setLargeIcon(mActivityIcon);
+            return;
+        }
+        // Fallback: Picture-in-Picture icon
+        mNotificationBuilder.setLargeIcon(Icon.createWithResource(mContext, R.drawable.pip_icon));
+    }
+
+    private Bitmap getActivityIcon() {
+        if (mContext == null) return null;
+        ComponentName componentName = PipUtils.getTopPipActivity(mContext).first;
+        if (componentName == null) return null;
+
+        Drawable drawable;
+        try {
+            drawable = mPackageManager.getActivityIcon(componentName);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+        int width = mContext.getResources().getDimensionPixelSize(
+                android.R.dimen.notification_large_icon_width);
+        int height = mContext.getResources().getDimensionPixelSize(
+                android.R.dimen.notification_large_icon_height);
+        return ImageUtils.buildScaledBitmap(drawable, width, height, /* allowUpscaling */ true);
+    }
+
     private static PendingIntent createPendingIntent(Context context, String action) {
         return PendingIntent.getBroadcast(context, 0,
                 new Intent(action).setPackage(context.getPackageName()),
-                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 
     private class ActionBroadcastReceiver extends BroadcastReceiver {
@@ -228,6 +403,7 @@
             mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
             mIntentFilter.addAction(ACTION_MOVE_PIP);
             mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
+            mIntentFilter.addAction(ACTION_FULLSCREEN);
         }
         boolean mRegistered = false;
 
@@ -249,10 +425,8 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            if (DEBUG) {
-                ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                        "%s: on(Broadcast)Receive(), action=%s", TAG, action);
-            }
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "%s: on(Broadcast)Receive(), action=%s", TAG, action);
 
             if (ACTION_SHOW_PIP_MENU.equals(action)) {
                 mDelegate.showPictureInPictureMenu();
@@ -262,14 +436,21 @@
                 mDelegate.enterPipMovementMenu();
             } else if (ACTION_TOGGLE_EXPANDED_PIP.equals(action)) {
                 mDelegate.togglePipExpansion();
+            } else if (ACTION_FULLSCREEN.equals(action)) {
+                mDelegate.movePipToFullscreen();
             }
         }
     }
 
     interface Delegate {
         void showPictureInPictureMenu();
+
         void closePip();
+
         void enterPipMovementMenu();
+
         void togglePipExpansion();
+
+        void movePipToFullscreen();
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index cf4ea46..41cd31a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -37,7 +37,7 @@
         val displayBounds = WindowUtils.displayBounds
         val secondaryAppBounds = Region.from(0,
                 dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
-                displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
+                displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
         return secondaryAppBounds
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index a510d69..e2da1a4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -171,7 +171,7 @@
             val bottomAppBounds = Region.from(0,
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
-                displayBounds.bottom - WindowUtils.navigationBarHeight)
+                displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
             visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
                 .coversExactly(topAppBounds)
             visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
@@ -192,7 +192,7 @@
             val bottomAppBounds = Region.from(0,
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
-                displayBounds.bottom - WindowUtils.navigationBarHeight)
+                displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
 
             visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
                 .coversExactly(topAppBounds)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 6cf8829..42b1014 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -209,12 +209,11 @@
         createNavigationInfo(animationTarget, null, null,
                 BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
 
-        // Check that back start is dispatched.
         doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        verify(mIOnBackInvokedCallback).onBackStarted();
 
-        // Check that back progress is dispatched.
+        // Check that back start and progress is dispatched when first move.
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        verify(mIOnBackInvokedCallback).onBackStarted();
         ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
         verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture());
         assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index bf08261..df18133 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -45,6 +45,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index e7432ac..90c4440 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -136,24 +136,59 @@
         free(valueBuffer);
         return nullptr;
     }
+    mNumShadersCachedInRam++;
+    ATRACE_FORMAT("HWUI RAM cache: %d shaders", mNumShadersCachedInRam);
     return SkData::MakeFromMalloc(valueBuffer, valueSize);
 }
 
+namespace {
+// Helper for BlobCache::set to trace the result.
+void set(BlobCache* cache, const void* key, size_t keySize, const void* value, size_t valueSize) {
+    switch (cache->set(key, keySize, value, valueSize)) {
+        case BlobCache::InsertResult::kInserted:
+            // This is what we expect/hope. It means the cache is large enough.
+            return;
+        case BlobCache::InsertResult::kDidClean: {
+            ATRACE_FORMAT("ShaderCache: evicted an entry to fit {key: %lu value %lu}!", keySize,
+                          valueSize);
+            return;
+        }
+        case BlobCache::InsertResult::kNotEnoughSpace: {
+            ATRACE_FORMAT("ShaderCache: could not fit {key: %lu value %lu}!", keySize, valueSize);
+            return;
+        }
+        case BlobCache::InsertResult::kInvalidValueSize:
+        case BlobCache::InsertResult::kInvalidKeySize: {
+            ATRACE_FORMAT("ShaderCache: invalid size {key: %lu value %lu}!", keySize, valueSize);
+            return;
+        }
+        case BlobCache::InsertResult::kKeyTooBig:
+        case BlobCache::InsertResult::kValueTooBig:
+        case BlobCache::InsertResult::kCombinedTooBig: {
+            ATRACE_FORMAT("ShaderCache: entry too big: {key: %lu value %lu}!", keySize, valueSize);
+            return;
+        }
+    }
+}
+}  // namespace
+
 void ShaderCache::saveToDiskLocked() {
     ATRACE_NAME("ShaderCache::saveToDiskLocked");
     if (mInitialized && mBlobCache && mSavePending) {
         if (mIDHash.size()) {
             auto key = sIDKey;
-            mBlobCache->set(&key, sizeof(key), mIDHash.data(), mIDHash.size());
+            set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size());
         }
         mBlobCache->writeToFile();
     }
     mSavePending = false;
 }
 
-void ShaderCache::store(const SkData& key, const SkData& data) {
+void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
     ATRACE_NAME("ShaderCache::store");
     std::lock_guard<std::mutex> lock(mMutex);
+    mNumShadersCachedInRam++;
+    ATRACE_FORMAT("HWUI RAM cache: %d shaders", mNumShadersCachedInRam);
 
     if (!mInitialized) {
         return;
@@ -187,7 +222,7 @@
         mNewPipelineCacheSize = -1;
         mTryToStorePipelineCache = true;
     }
-    bc->set(key.data(), keySize, value, valueSize);
+    set(bc, key.data(), keySize, value, valueSize);
 
     if (!mSavePending && mDeferredSaveDelay > 0) {
         mSavePending = true;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 4dcc9fb..3e0fd51 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -73,7 +73,7 @@
      * "store" attempts to insert a new key/value blob pair into the cache.
      * This will be called by Skia after it compiled a new SKSL shader
      */
-    void store(const SkData& key, const SkData& data) override;
+    void store(const SkData& key, const SkData& data, const SkString& description) override;
 
     /**
      * "onVkFrameFlushed" tries to store Vulkan pipeline cache state.
@@ -210,6 +210,13 @@
      */
     static constexpr uint8_t sIDKey = 0;
 
+    /**
+     * Most of this class concerns persistent storage for shaders, but it's also
+     * interesting to keep track of how many shaders are stored in RAM. This
+     * class provides a convenient entry point for that.
+     */
+    int mNumShadersCachedInRam = 0;
+
     friend class ShaderCacheTestUtils;  // used for unit testing
 };
 
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 87981f1..974d85a 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -140,9 +140,9 @@
     // write to the in-memory cache without storing on disk and verify we read the same values
     sk_sp<SkData> inVS;
     setShader(inVS, "sassas");
-    ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
+    ShaderCache::get().store(GrProgramDescTest(100), *inVS.get(), SkString());
     setShader(inVS, "someVS");
-    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get(), SkString());
     ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
     ASSERT_TRUE(checkShader(outVS, "sassas"));
     ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
@@ -166,7 +166,7 @@
 
     // change data, store to disk, read back again and verify data has been changed
     setShader(inVS, "ewData1");
-    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get(), SkString());
     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
     ShaderCache::get().initShaderDiskCache();
     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
@@ -177,7 +177,7 @@
     std::vector<uint8_t> dataBuffer(dataSize);
     genRandomData(dataBuffer);
     setShader(inVS, dataBuffer);
-    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get(), SkString());
     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
     ShaderCache::get().initShaderDiskCache();
     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
@@ -225,7 +225,7 @@
         setShader(data, dataBuffer);
 
         blob = std::make_pair(key, data);
-        ShaderCache::get().store(*key.get(), *data.get());
+        ShaderCache::get().store(*key.get(), *data.get(), SkString());
     }
     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
 
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 1dc74e5..10ea651 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -106,6 +106,7 @@
 PointerController::~PointerController() {
     mDisplayInfoListener->onPointerControllerDestroyed();
     mUnregisterWindowInfosListener(mDisplayInfoListener);
+    mContext.getPolicy()->onPointerDisplayIdChanged(ADISPLAY_ID_NONE, 0, 0);
 }
 
 std::mutex& PointerController::getLock() const {
@@ -255,6 +256,12 @@
         getAdditionalMouseResources = true;
     }
     mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources);
+    if (viewport.displayId != mLocked.pointerDisplayId) {
+        float xPos, yPos;
+        mCursorController.getPosition(&xPos, &yPos);
+        mContext.getPolicy()->onPointerDisplayIdChanged(viewport.displayId, xPos, yPos);
+        mLocked.pointerDisplayId = viewport.displayId;
+    }
 }
 
 void PointerController::updatePointerIcon(int32_t iconId) {
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 2e6e851..eab030f 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -104,6 +104,7 @@
 
     struct Locked {
         Presentation presentation;
+        int32_t pointerDisplayId = ADISPLAY_ID_NONE;
 
         std::vector<gui::DisplayInfo> mDisplayInfos;
         std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
index 26a65a4..c2bc1e0 100644
--- a/libs/input/PointerControllerContext.h
+++ b/libs/input/PointerControllerContext.h
@@ -79,6 +79,7 @@
             std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
     virtual int32_t getDefaultPointerIconId() = 0;
     virtual int32_t getCustomPointerIconId() = 0;
+    virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) = 0;
 };
 
 /*
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index dae1fcc..f9752ed 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -56,9 +56,11 @@
             std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
     virtual int32_t getDefaultPointerIconId() override;
     virtual int32_t getCustomPointerIconId() override;
+    virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) override;
 
     bool allResourcesAreLoaded();
     bool noResourcesAreLoaded();
+    std::optional<int32_t> getLastReportedPointerDisplayId() { return latestPointerDisplayId; }
 
 private:
     void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
@@ -66,6 +68,7 @@
     bool pointerIconLoaded{false};
     bool pointerResourcesLoaded{false};
     bool additionalMouseResourcesLoaded{false};
+    std::optional<int32_t /*displayId*/> latestPointerDisplayId;
 };
 
 void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
@@ -126,12 +129,19 @@
     icon->hotSpotX = hotSpot.first;
     icon->hotSpotY = hotSpot.second;
 }
+
+void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t displayId,
+                                                                     float /*xPos*/,
+                                                                     float /*yPos*/) {
+    latestPointerDisplayId = displayId;
+}
+
 class PointerControllerTest : public Test {
 protected:
     PointerControllerTest();
     ~PointerControllerTest();
 
-    void ensureDisplayViewportIsSet();
+    void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
 
     sp<MockSprite> mPointerSprite;
     sp<MockPointerControllerPolicyInterface> mPolicy;
@@ -168,9 +178,9 @@
     mThread.join();
 }
 
-void PointerControllerTest::ensureDisplayViewportIsSet() {
+void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
     DisplayViewport viewport;
-    viewport.displayId = ADISPLAY_ID_DEFAULT;
+    viewport.displayId = displayId;
     viewport.logicalRight = 1600;
     viewport.logicalBottom = 1200;
     viewport.physicalRight = 800;
@@ -255,6 +265,30 @@
     ensureDisplayViewportIsSet();
 }
 
+TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
+    EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
+            << "A pointer display change does not occur when PointerController is created.";
+
+    ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
+
+    const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
+    ASSERT_TRUE(lastReportedPointerDisplayId)
+            << "The policy is notified of a pointer display change when the viewport is first set.";
+    EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
+            << "Incorrect pointer display notified.";
+
+    ensureDisplayViewportIsSet(42);
+
+    EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
+            << "The policy is notified when the pointer display changes.";
+
+    // Release the PointerController.
+    mPointerController = nullptr;
+
+    EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
+            << "The pointer display changes to invalid when PointerController is destroyed.";
+}
+
 class PointerControllerWindowInfoListenerTest : public Test {};
 
 class TestPointerController : public PointerController {
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 70d6810..472586b 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -643,6 +643,9 @@
 
     /**
      * <p>Return the frame to the ImageReader for reuse.</p>
+     *
+     * This method should only be called via {@link SurfaceImage#close} which ensures that image
+     * closing is atomic.
      */
     private void releaseImage(Image i) {
         if (! (i instanceof SurfaceImage) ) {
@@ -1125,6 +1128,8 @@
     }
 
     private class SurfaceImage extends android.media.Image {
+        private final Object mCloseLock = new Object();
+
         public SurfaceImage(int format) {
             mFormat = format;
             mHardwareBufferFormat = ImageReader.this.mHardwareBufferFormat;
@@ -1139,7 +1144,9 @@
 
         @Override
         public void close() {
-            ImageReader.this.releaseImage(this);
+            synchronized (this.mCloseLock) {
+                ImageReader.this.releaseImage(this);
+            }
         }
 
         public ImageReader getReader() {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 37cbf30..5e91864 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -484,6 +484,14 @@
         if (deviceFilterPairs.isEmpty()) return;
 
         mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
+        // No need to show user consent dialog if it is a singleDevice
+        // and isSkipPrompt(true) AssociationRequest.
+        // See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
+        if (mRequest.isSkipPrompt()) {
+            mSingleDeviceSpinner.setVisibility(View.GONE);
+            onUserSelectedDevice(mSelectedDevice);
+            return;
+        }
 
         final String deviceName = mSelectedDevice.getDisplayName();
         final Spanned title = getHtmlFromResources(
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml
index 25f0771..72b569f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml
@@ -25,7 +25,7 @@
         android:fitsSystemWindows="true"
         android:outlineAmbientShadowColor="@android:color/transparent"
         android:outlineSpotShadowColor="@android:color/transparent"
-        android:background="?android:attr/colorPrimary"
+        android:background="@android:color/transparent"
         android:theme="@style/Theme.CollapsingToolbar.Settings">
 
         <com.google.android.material.appbar.CollapsingToolbarLayout
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index 72383fe..dbb4b50 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -20,6 +20,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.os.Build;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -29,6 +30,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 
@@ -41,6 +43,7 @@
  * This widget is wrapping the collapsing toolbar and can be directly used by the
  * {@link AppCompatActivity}.
  */
+@RequiresApi(Build.VERSION_CODES.S)
 public class CollapsingCoordinatorLayout extends CoordinatorLayout {
     private static final String TAG = "CollapsingCoordinatorLayout";
     private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 284da73..2f30baa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -234,5 +234,10 @@
         if (!(mPreference instanceof RestrictedTopLevelPreference)) {
             mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
         }
+
+        if (mPreference instanceof PrimarySwitchPreference) {
+            ((PrimarySwitchPreference) mPreference)
+                    .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4ee2122..6919cf2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1376,7 +1376,7 @@
     /**
      * Store the member devices that are in the same coordinated set.
      */
-    public void setMemberDevice(CachedBluetoothDevice memberDevice) {
+    public void addMemberDevice(CachedBluetoothDevice memberDevice) {
         mMemberDevices.add(memberDevice);
     }
 
@@ -1393,24 +1393,24 @@
      * device and member devices.
      *
      * @param prevMainDevice the previous Main device, it will be added into the member device set.
-     * @param newMainDevie the new Main device, it will be removed from the member device set.
+     * @param newMainDevice the new Main device, it will be removed from the member device set.
      */
     public void switchMemberDeviceContent(CachedBluetoothDevice prevMainDevice,
-            CachedBluetoothDevice newMainDevie) {
+            CachedBluetoothDevice newMainDevice) {
         // Backup from main device
         final BluetoothDevice tmpDevice = mDevice;
         final short tmpRssi = mRssi;
         final boolean tmpJustDiscovered = mJustDiscovered;
         // Set main device from sub device
-        mDevice = newMainDevie.mDevice;
-        mRssi = newMainDevie.mRssi;
-        mJustDiscovered = newMainDevie.mJustDiscovered;
-        setMemberDevice(prevMainDevice);
-        mMemberDevices.remove(newMainDevie);
+        mDevice = newMainDevice.mDevice;
+        mRssi = newMainDevice.mRssi;
+        mJustDiscovered = newMainDevice.mJustDiscovered;
+        addMemberDevice(prevMainDevice);
+        mMemberDevices.remove(newMainDevice);
         // Set sub device from backup
-        newMainDevie.mDevice = tmpDevice;
-        newMainDevie.mRssi = tmpRssi;
-        newMainDevie.mJustDiscovered = tmpJustDiscovered;
+        newMainDevice.mDevice = tmpDevice;
+        newMainDevice.mRssi = tmpRssi;
+        newMainDevice.mJustDiscovered = tmpJustDiscovered;
         fetchActiveDevices();
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index cc56a21..89e10c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -85,7 +85,7 @@
             // Once there is other devices with the same groupId, to add new device as member
             // devices.
             if (CsipDevice != null) {
-                CsipDevice.setMemberDevice(newDevice);
+                CsipDevice.addMemberDevice(newDevice);
                 newDevice.setName(CsipDevice.getName());
                 return true;
             }
@@ -148,7 +148,7 @@
             log("onGroupIdChanged: removed from UI device =" + cachedDevice
                     + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex);
 
-            mainDevice.setMemberDevice(cachedDevice);
+            mainDevice.addMemberDevice(cachedDevice);
             mCachedDevices.remove(i);
             mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
             break;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 298ee90..bef1d9c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -40,7 +40,6 @@
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Map;
 
 @RunWith(RobolectricTestRunner.class)
@@ -503,8 +502,8 @@
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
         CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
-        cachedDevice1.setMemberDevice(cachedDevice2);
-        cachedDevice1.setMemberDevice(cachedDevice3);
+        cachedDevice1.addMemberDevice(cachedDevice2);
+        cachedDevice1.addMemberDevice(cachedDevice3);
 
         assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
         assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
@@ -524,7 +523,7 @@
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
         cachedDevice1.setGroupId(1);
         cachedDevice2.setGroupId(1);
-        cachedDevice1.setMemberDevice(cachedDevice2);
+        cachedDevice1.addMemberDevice(cachedDevice2);
 
         // Call onDeviceUnpaired for the one in mCachedDevices.
         mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
@@ -541,7 +540,7 @@
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
         cachedDevice1.setGroupId(1);
         cachedDevice2.setGroupId(1);
-        cachedDevice1.setMemberDevice(cachedDevice2);
+        cachedDevice1.addMemberDevice(cachedDevice2);
 
         // Call onDeviceUnpaired for the one in mCachedDevices.
         mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 3029781..5eaf553 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -120,6 +120,9 @@
         Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
         Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+        Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
+        Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
+        Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
         Settings.Secure.VR_DISPLAY_MODE,
         Settings.Secure.NOTIFICATION_BADGING,
         Settings.Secure.NOTIFICATION_DISMISS_RTL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index a4da497..9ee7b65 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -18,6 +18,7 @@
 
 import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR;
@@ -176,6 +177,10 @@
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
index 528f603..fd06238 100644
--- a/packages/SystemUI/res/layout/alert_dialog_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2022 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.
@@ -15,88 +14,83 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<androidx.core.widget.NestedScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
 
-    <com.android.internal.widget.AlertDialogLayout
-        android:id="@*android:id/parentPanel"
+<com.android.internal.widget.AlertDialogLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@*android:id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_horizontal|top"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/dialog_top_padding"
+>
+
+    <include layout="@layout/alert_dialog_title_systemui" />
+
+    <FrameLayout
+        android:id="@*android:id/contentPanel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:gravity="center_horizontal|top"
-        android:orientation="vertical"
-        android:paddingTop="@dimen/dialog_top_padding"
-        >
+        android:minHeight="48dp"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding"
+    >
 
-        <include layout="@layout/alert_dialog_title_systemui" />
-
-        <FrameLayout
-            android:id="@*android:id/contentPanel"
+        <ScrollView
+            android:id="@*android:id/scrollView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="48dp"
-            android:paddingStart="@dimen/dialog_side_padding"
-            android:paddingEnd="@dimen/dialog_side_padding"
-            >
+            android:clipToPadding="false">
 
-            <ScrollView
-                android:id="@*android:id/scrollView"
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:clipToPadding="false">
+                android:orientation="vertical">
 
-                <LinearLayout
+                <Space
+                    android:id="@*android:id/textSpacerNoTitle"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp" />
+
+                <TextView
+                    android:id="@*android:id/message"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:orientation="vertical">
+                    style="@style/TextAppearance.Dialog.Body.Message" />
 
-                    <Space
-                        android:id="@*android:id/textSpacerNoTitle"
-                        android:visibility="gone"
-                        android:layout_width="match_parent"
-                        android:layout_height="0dp" />
+                <Space
+                    android:id="@*android:id/textSpacerNoButtons"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="6dp" />
+            </LinearLayout>
+        </ScrollView>
+    </FrameLayout>
 
-                    <TextView
-                        android:id="@*android:id/message"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        style="@style/TextAppearance.Dialog.Body.Message" />
-
-                    <Space
-                        android:id="@*android:id/textSpacerNoButtons"
-                        android:visibility="gone"
-                        android:layout_width="match_parent"
-                        android:layout_height="6dp" />
-                </LinearLayout>
-            </ScrollView>
-        </FrameLayout>
+    <FrameLayout
+        android:id="@*android:id/customPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="48dp"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding"
+    >
 
         <FrameLayout
-            android:id="@*android:id/customPanel"
+            android:id="@*android:id/custom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding">
+        <include
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="48dp"
-            android:paddingStart="@dimen/dialog_side_padding"
-            android:paddingEnd="@dimen/dialog_side_padding"
-            >
-
-            <FrameLayout
-                android:id="@*android:id/custom"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-        </FrameLayout>
-
-        <FrameLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingStart="@dimen/dialog_side_padding"
-            android:paddingEnd="@dimen/dialog_side_padding">
-            <include
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                layout="@layout/alert_dialog_button_bar_systemui" />
-        </FrameLayout>
-    </com.android.internal.widget.AlertDialogLayout>
-
-</androidx.core.widget.NestedScrollView>
\ No newline at end of file
+            layout="@layout/alert_dialog_button_bar_systemui" />
+    </FrameLayout>
+</com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index b230438..10bb6cb 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -121,6 +121,30 @@
             android:adjustViewBounds="true"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
+        <TextView
+            android:id="@+id/hidden_text_preview"
+            android:visibility="gone"
+            android:textFontWeight="500"
+            android:padding="8dp"
+            android:gravity="center"
+            android:textSize="14sp"
+            android:text="@string/clipboard_text_hidden"
+            android:textColor="?attr/overlayButtonTextColor"
+            android:background="?androidprv:attr/colorAccentSecondary"
+            android:layout_width="@dimen/clipboard_preview_size"
+            android:layout_height="@dimen/clipboard_preview_size"/>
+        <TextView
+            android:id="@+id/hidden_image_preview"
+            android:visibility="gone"
+            android:textFontWeight="500"
+            android:padding="8dp"
+            android:gravity="center"
+            android:textSize="14sp"
+            android:text="@string/clipboard_text_hidden"
+            android:textColor="#ffffff"
+            android:background="#000000"
+            android:layout_width="@dimen/clipboard_preview_size"
+            android:layout_height="@dimen/clipboard_preview_size"/>
     </FrameLayout>
     <FrameLayout
         android:id="@+id/dismiss_button"
@@ -141,4 +165,4 @@
             android:layout_margin="@dimen/overlay_dismiss_button_margin"
             android:src="@drawable/overlay_cancel"/>
     </FrameLayout>
-</com.android.systemui.screenshot.DraggableConstraintLayout>
+</com.android.systemui.screenshot.DraggableConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/fgs_manager_app_item.xml b/packages/SystemUI/res/layout/fgs_manager_app_item.xml
index 30bce9d..a6f659d 100644
--- a/packages/SystemUI/res/layout/fgs_manager_app_item.xml
+++ b/packages/SystemUI/res/layout/fgs_manager_app_item.xml
@@ -50,7 +50,8 @@
   <Button
       android:id="@+id/fgs_manager_app_item_stop_button"
       android:layout_width="wrap_content"
-      android:layout_height="48dp"
+      android:layout_height="wrap_content"
+      android:minHeight="48dp"
       android:text="@string/fgs_manager_app_item_stop_button_label"
       android:layout_marginStart="12dp"
       style="?android:attr/buttonBarNeutralButtonStyle" />
diff --git a/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml
new file mode 100644
index 0000000..71bb938
--- /dev/null
+++ b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<androidx.core.widget.NestedScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <include layout="@layout/alert_dialog_systemui" />
+
+</androidx.core.widget.NestedScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 89b72dd..2426f01 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1211,7 +1211,7 @@
     <string name="wallet_app_button_label">Show all</string>
     <!-- Label of the button underneath the card carousel prompting user unlock device. [CHAR LIMIT=NONE] -->
     <!-- Secondary label of the quick access wallet tile if no card. [CHAR LIMIT=NONE] -->
-    <string name="wallet_secondary_label_no_card">Add a card</string>
+    <string name="wallet_secondary_label_no_card">Tap to open</string>
     <!-- Secondary label of the quick access wallet tile if wallet is still updating. [CHAR LIMIT=NONE] -->
     <string name="wallet_secondary_label_updating">Updating</string>
     <!-- Secondary label of the quick access wallet tile if device locked. [CHAR LIMIT=NONE] -->
@@ -2482,6 +2482,8 @@
     <string name="clipboard_edit_image_description">Edit copied image</string>
     <!-- Label for button to send copied content to a nearby device [CHAR LIMIT=NONE] -->
     <string name="clipboard_send_nearby_description">Send to nearby device</string>
+    <!-- Text informing user that copied content is hidden [CHAR LIMIT=NONE] -->
+    <string name="clipboard_text_hidden">Tap to view</string>
 
     <!-- Generic "add" string [CHAR LIMIT=NONE] -->
     <string name="add">Add</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index c93c065..0c25f54 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -380,7 +380,7 @@
         <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
         <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
         <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
-        <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+        <item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item>
         <item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
         <item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
     </style>
@@ -389,6 +389,10 @@
         <item name="android:layout">@layout/alert_dialog_systemui</item>
     </style>
 
+    <style name="ScrollableAlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
+        <item name="android:layout">@layout/scrollable_alert_dialog_systemui</item>
+    </style>
+
     <style name="ButtonBarStyle" parent="@androidprv:style/DeviceDefault.ButtonBar.AlertDialog">
         <item name="android:paddingTop">@dimen/dialog_button_bar_top_padding</item>
         <item name="android:paddingBottom">@dimen/dialog_bottom_padding</item>
@@ -994,6 +998,7 @@
 
     <style name="Theme.SystemUI.Dialog.Cast">
         <item name="android:textAppearanceMedium">@style/TextAppearance.CastItem</item>
+        <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
     </style>
     <!-- ************************************************************************************* -->
 
@@ -1111,8 +1116,9 @@
     </style>
 
     <style name="FgsManagerAppDuration">
-        <item name="android:fontFamily">?android:attr/textAppearanceSmall</item>
         <item name="android:textDirection">locale</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
 
     <style name="BroadcastDialog">
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index f195d20..38fa354 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -16,14 +16,20 @@
 
 package com.android.keyguard
 
+import android.annotation.IntDef
 import android.content.ContentResolver
 import android.database.ContentObserver
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT
 import android.net.Uri
 import android.os.Handler
 import android.os.UserHandle
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.util.Log
 import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
@@ -44,6 +50,20 @@
     dumpManager: DumpManager
 ) : Dumpable {
 
+    companion object {
+        const val TAG = "ActiveUnlockConfig"
+
+        const val BIOMETRIC_TYPE_NONE = 0
+        const val BIOMETRIC_TYPE_ANY_FACE = 1
+        const val BIOMETRIC_TYPE_ANY_FINGERPRINT = 2
+        const val BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT = 3
+    }
+
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(BIOMETRIC_TYPE_NONE, BIOMETRIC_TYPE_ANY_FACE, BIOMETRIC_TYPE_ANY_FINGERPRINT,
+            BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT)
+    annotation class BiometricType
+
     /**
      * Indicates the origin for an active unlock request.
      */
@@ -51,35 +71,50 @@
         WAKE, UNLOCK_INTENT, BIOMETRIC_FAIL, ASSISTANT
     }
 
+    var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
     private var requestActiveUnlockOnWakeup = false
     private var requestActiveUnlockOnUnlockIntent = false
     private var requestActiveUnlockOnBioFail = false
 
+    private var faceErrorsToTriggerBiometricFailOn = mutableSetOf(FACE_ERROR_TIMEOUT)
+    private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>()
+    private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>(BIOMETRIC_TYPE_NONE)
+
     private val settingsObserver = object : ContentObserver(handler) {
-        private val wakeUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
-        private val unlockIntentUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
-        private val bioFailUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
+        private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
+        private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
+        private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
+        private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)
+        private val faceAcquireInfoUri =
+                secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)
+        private val unlockIntentWhenBiometricEnrolledUri =
+                secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
 
         fun register() {
-            contentResolver.registerContentObserver(
-                    wakeUri,
-                    false,
-                    this,
-                    UserHandle.USER_ALL)
-            contentResolver.registerContentObserver(
-                    unlockIntentUri,
-                    false,
-                    this,
-                    UserHandle.USER_ALL)
-            contentResolver.registerContentObserver(
-                    bioFailUri,
-                    false,
-                    this,
-                    UserHandle.USER_ALL)
+            registerUri(
+                    listOf(
+                            wakeUri,
+                            unlockIntentUri,
+                            bioFailUri,
+                            faceErrorsUri,
+                            faceAcquireInfoUri,
+                            unlockIntentWhenBiometricEnrolledUri
+                    )
+            )
 
             onChange(true, ArrayList(), 0, getCurrentUser())
         }
 
+        private fun registerUri(uris: Collection<Uri>) {
+            for (uri in uris) {
+                contentResolver.registerContentObserver(
+                        uri,
+                        false,
+                        this,
+                        UserHandle.USER_ALL)
+            }
+        }
+
         override fun onChange(
             selfChange: Boolean,
             uris: Collection<Uri>,
@@ -104,6 +139,55 @@
                 requestActiveUnlockOnBioFail = secureSettings.getIntForUser(
                         ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1
             }
+
+            if (selfChange || uris.contains(faceErrorsUri)) {
+                processStringArray(
+                        secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS,
+                                getCurrentUser()),
+                        faceErrorsToTriggerBiometricFailOn,
+                        setOf(FACE_ERROR_TIMEOUT))
+            }
+
+            if (selfChange || uris.contains(faceAcquireInfoUri)) {
+                processStringArray(
+                        secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
+                                getCurrentUser()),
+                        faceAcquireInfoToTriggerBiometricFailOn,
+                        setOf<Int>())
+            }
+
+            if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) {
+                processStringArray(
+                        secureSettings.getStringForUser(
+                                ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                                getCurrentUser()),
+                        onUnlockIntentWhenBiometricEnrolled,
+                        setOf(BIOMETRIC_TYPE_NONE))
+            }
+        }
+
+        /**
+         * Convert a pipe-separated set of integers into a set of ints.
+         * @param stringSetting expected input are integers delineated by a pipe. For example,
+         * it may look something like this: "1|5|3".
+         * @param out updates the "out" Set will the integers between the pipes.
+         * @param default If stringSetting is null, "out" will be populated with values in "default"
+         */
+        private fun processStringArray(
+            stringSetting: String?,
+            out: MutableSet<Int>,
+            default: Set<Int>
+        ) {
+            out.clear()
+            stringSetting?.let {
+                for (code: String in stringSetting.split("|")) {
+                    try {
+                        out.add(code.toInt())
+                    } catch (e: NumberFormatException) {
+                        Log.e(TAG, "Passed an invalid setting=$code")
+                    }
+                }
+            } ?: out.addAll(default)
         }
     }
 
@@ -113,6 +197,30 @@
     }
 
     /**
+     * If any active unlock triggers are enabled.
+     */
+    fun isActiveUnlockEnabled(): Boolean {
+        return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
+                requestActiveUnlockOnBioFail
+    }
+
+    /**
+     * Whether the face error code from {@link BiometricFaceConstants} should trigger
+     * active unlock on biometric failure.
+     */
+    fun shouldRequestActiveUnlockOnFaceError(errorCode: Int): Boolean {
+        return faceErrorsToTriggerBiometricFailOn.contains(errorCode)
+    }
+
+    /**
+     * Whether the face acquireInfo from {@link BiometricFaceConstants} should trigger
+     * active unlock on biometric failure.
+     */
+    fun shouldRequestActiveUnlockOnFaceAcquireInfo(acquiredInfo: Int): Boolean {
+        return faceAcquireInfoToTriggerBiometricFailOn.contains(acquiredInfo)
+    }
+
+    /**
      * Whether to trigger active unlock based on where the request is coming from and
      * the current settings.
      */
@@ -121,7 +229,8 @@
             ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE -> requestActiveUnlockOnWakeup
 
             ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT ->
-                requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup
+                requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
+                        (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
 
             ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL ->
                 requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
@@ -131,17 +240,55 @@
         }
     }
 
-    /**
-     * If any active unlock triggers are enabled.
-     */
-    fun isActiveUnlockEnabled(): Boolean {
-        return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
-                requestActiveUnlockOnBioFail
+    private fun shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment(): Boolean {
+        if (!requestActiveUnlockOnBioFail) {
+            return false
+        }
+
+        keyguardUpdateMonitor?.let {
+            val anyFaceEnrolled = it.isFaceEnrolled
+            val anyFingerprintEnrolled =
+                    it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())
+            val udfpsEnrolled = it.isUdfpsEnrolled
+
+            if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
+                return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_NONE)
+            }
+
+            if (!anyFaceEnrolled && anyFingerprintEnrolled) {
+                return onUnlockIntentWhenBiometricEnrolled.contains(
+                        BIOMETRIC_TYPE_ANY_FINGERPRINT) ||
+                        (udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains(
+                                BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT))
+            }
+
+            if (!anyFingerprintEnrolled && anyFaceEnrolled) {
+                return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_ANY_FACE)
+            }
+        }
+
+        return false
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("Settings:")
         pw.println("   requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
         pw.println("   requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
         pw.println("   requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
+        pw.println("   requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
+                "$onUnlockIntentWhenBiometricEnrolled")
+        pw.println("   requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
+        pw.println("   requestActiveUnlockOnFaceAcquireInfo=" +
+                "$faceAcquireInfoToTriggerBiometricFailOn")
+
+        pw.println("Current state:")
+        keyguardUpdateMonitor?.let {
+            pw.println("   shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
+                    "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
+            pw.println("   faceEnrolled=${it.isFaceEnrolled}")
+            pw.println("   fpEnrolled=${
+                    it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}")
+            pw.println("   udfpsEnrolled=${it.isUdfpsEnrolled}")
+        } ?: pw.println("   keyguardUpdateMonitor is uninitialized")
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 41f9240..39c3949 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -282,7 +282,7 @@
         super.reloadColors();
         mMessageAreaController.reloadColors();
         int textColor = Utils.getColorAttr(mLockPatternView.getContext(),
-                android.R.attr.textColorPrimary).getDefaultColor();
+                android.R.attr.textColorSecondary).getDefaultColor();
         int errorColor = Utils.getColorError(mLockPatternView.getContext()).getDefaultColor();
         mLockPatternView.setColors(textColor, textColor, errorColor);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bbe9a362..121ac29 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -56,7 +56,6 @@
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.hardware.SensorPrivacyManager;
-import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
@@ -1615,7 +1614,7 @@
                         mKeyguardBypassController.setUserHasDeviceEntryIntent(false);
                     }
 
-                    if (errMsgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
+                    if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) {
                         requestActiveUnlock(
                                 ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
                                 "faceError-" + errMsgId);
@@ -1625,6 +1624,13 @@
                 @Override
                 public void onAuthenticationAcquired(int acquireInfo) {
                     handleFaceAcquired(acquireInfo);
+
+                    if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                            acquireInfo)) {
+                        requestActiveUnlock(
+                                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
+                                "faceAcquireInfo-" + acquireInfo);
+                    }
                 }
     };
 
@@ -1639,6 +1645,7 @@
     private boolean mFingerprintLockedOut;
     private boolean mFingerprintLockedOutPermanent;
     private boolean mFaceLockedOutPermanent;
+    private HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>();
     private TelephonyManager mTelephonyManager;
 
     /**
@@ -1889,6 +1896,7 @@
         dumpManager.registerDumpable(getClass().getName(), this);
         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
         mActiveUnlockConfig = activeUnlockConfiguration;
+        mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
 
         mHandler = new Handler(mainLooper) {
             @Override
@@ -2329,7 +2337,7 @@
         }
 
         if (shouldTriggerActiveUnlock()) {
-            if (DEBUG) {
+            if (DEBUG_ACTIVE_UNLOCK) {
                 Log.d("ActiveUnlock", "initiate active unlock triggerReason=" + reason);
             }
             mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser());
@@ -2359,7 +2367,7 @@
         }
 
         if (allowRequest && shouldTriggerActiveUnlock()) {
-            if (DEBUG) {
+            if (DEBUG_ACTIVE_UNLOCK) {
                 Log.d("ActiveUnlock", "reportUserRequestedUnlock"
                         + " origin=" + requestOrigin.name()
                         + " reason=" + reason
@@ -2777,8 +2785,17 @@
     }
 
     private boolean isUnlockWithFingerprintPossible(int userId) {
-        return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
-                && mFpm.hasEnrolledTemplates(userId);
+        mIsUnlockWithFingerprintPossible.put(userId, mFpm != null && mFpm.isHardwareDetected()
+                && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
+        return mIsUnlockWithFingerprintPossible.get(userId);
+    }
+
+    /**
+     * Cached value for whether fingerprint is enrolled and possible to use for authentication.
+     * Note: checking fingerprint enrollment directly with the AuthController requires an IPC.
+     */
+    public boolean getCachedIsUnlockWithFingerprintPossible(int userId) {
+        return mIsUnlockWithFingerprintPossible.get(userId);
     }
 
     private boolean isUnlockWithFacePossible(int userId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index e191365..fdde402 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -55,7 +55,7 @@
 
     @NonNull private final RectF mSensorRect;
     @NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
-    private int mRadius;
+    private float mRadius;
     private int mLockIconPadding;
 
     private ImageView mLockIcon;
@@ -126,7 +126,7 @@
      * Set the location of the lock icon.
      */
     @VisibleForTesting
-    public void setCenterLocation(@NonNull PointF center, int radius, int drawablePadding) {
+    public void setCenterLocation(@NonNull PointF center, float radius, int drawablePadding) {
         mLockIconCenter = center;
         mRadius = radius;
         mLockIconPadding = drawablePadding;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 2b217f0..d79b145 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -188,6 +188,7 @@
     protected void onViewAttached() {
         updateIsUdfpsEnrolled();
         updateConfiguration();
+        updateLockIconLocation();
         updateKeyguardShowing();
         mUserUnlockedWithBiometric = false;
 
@@ -340,20 +341,17 @@
         mHeightPixels = bounds.bottom;
         mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
 
-        final int defaultPaddingPx =
-                getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
-        mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor());
-
         mUnlockedLabel = mView.getContext().getResources().getString(
                 R.string.accessibility_unlock_button);
         mLockedLabel = mView.getContext()
                 .getResources().getString(R.string.accessibility_lock_icon);
-
-        updateLockIconLocation();
     }
 
     private void updateLockIconLocation() {
         if (mUdfpsSupported) {
+            final int defaultPaddingPx =
+                    getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
+            mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor());
             mView.setCenterLocation(mAuthController.getUdfpsLocation(),
                     mAuthController.getUdfpsRadius(), mScaledPaddingPx);
         } else {
@@ -362,8 +360,6 @@
                         mHeightPixels - mBottomPaddingPx - sLockIconRadiusPx),
                         sLockIconRadiusPx, mScaledPaddingPx);
         }
-
-        mView.getHitRect(mSensorTouchLocation);
     }
 
     @Override
@@ -386,6 +382,7 @@
         pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
         pw.println("  mStatusBarState: " + StatusBarState.toString(mStatusBarState));
         pw.println("  mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
+        pw.println("  mSensorTouchLocation: " + mSensorTouchLocation);
 
         if (mView != null) {
             mView.dump(pw, args);
@@ -672,6 +669,7 @@
     }
 
     private boolean inLockIconArea(MotionEvent event) {
+        mView.getHitRect(mSensorTouchLocation);
         return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
                 && mView.getVisibility() == View.VISIBLE;
     }
@@ -692,6 +690,7 @@
         mExecutor.execute(() -> {
             updateIsUdfpsEnrolled();
             updateConfiguration();
+            updateLockIconLocation();
         });
     }
 
@@ -705,6 +704,11 @@
         public void onEnrollmentsChanged() {
             updateUdfpsConfig();
         }
+
+        @Override
+        public void onUdfpsLocationChanged() {
+            updateLockIconLocation();
+        }
     };
 
     private final View.OnClickListener mA11yClickListener = v -> onLongPress();
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index cbce854..dd31218 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -47,6 +47,7 @@
 import android.hardware.graphics.common.DisplayDecorationSupport;
 import android.os.Handler;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.util.DisplayUtils;
@@ -1067,15 +1068,22 @@
     }
 
     private void updateLayoutParams() {
-        if (mOverlays == null) {
-            return;
+        //ToDo: We should skip unnecessary call to update view layout.
+        Trace.beginSection("ScreenDecorations#updateLayoutParams");
+        if (mScreenDecorHwcWindow != null) {
+            mWindowManager.updateViewLayout(mScreenDecorHwcWindow, getHwcWindowLayoutParams());
         }
-        for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
-            if (mOverlays[i] == null) {
-                continue;
+
+        if (mOverlays != null) {
+            for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
+                if (mOverlays[i] == null) {
+                    continue;
+                }
+                mWindowManager.updateViewLayout(
+                        mOverlays[i].getRootView(), getWindowLayoutParams(i));
             }
-            mWindowManager.updateViewLayout(mOverlays[i].getRootView(), getWindowLayoutParams(i));
         }
+        Trace.endSection();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 9324893..75339aa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -79,6 +79,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -446,11 +447,11 @@
     /**
      * @return the radius of UDFPS on the screen in pixels
      */
-    public int getUdfpsRadius() {
+    public float getUdfpsRadius() {
         if (mUdfpsController == null || mUdfpsBounds == null) {
             return -1;
         }
-        return mUdfpsBounds.height() / 2;
+        return mUdfpsBounds.height() / 2f;
     }
 
     /**
@@ -634,11 +635,17 @@
                     displayInfo.getNaturalHeight());
 
             final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
+            final Rect previousUdfpsBounds = mUdfpsBounds;
             mUdfpsBounds = udfpsProp.getLocation().getRect();
             mUdfpsBounds.scale(scaleFactor);
             mUdfpsController.updateOverlayParams(udfpsProp.sensorId,
                     new UdfpsOverlayParams(mUdfpsBounds, displayInfo.getNaturalWidth(),
                             displayInfo.getNaturalHeight(), scaleFactor, displayInfo.rotation));
+            if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) {
+                for (Callback cb : mCallbacks) {
+                    cb.onUdfpsLocationChanged();
+                }
+            }
         }
     }
 
@@ -1054,5 +1061,10 @@
          * Called when the biometric prompt is no longer showing.
          */
         default void onBiometricPromptDismissed() {}
+
+        /**
+         * The location in pixels can change due to resolution changes.
+         */
+        default void onUdfpsLocationChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index d9aa1ba..86e5016 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -335,15 +335,17 @@
                 updateSensorLocation()
             }
 
-            override fun onEnrollmentsChanged() {
+            override fun onUdfpsLocationChanged() {
+                updateUdfpsDependentParams()
+                updateSensorLocation()
             }
         }
 
     private fun updateUdfpsDependentParams() {
         authController.udfpsProps?.let {
             if (it.size > 0) {
-                udfpsRadius = it[0].location.sensorRadius.toFloat()
                 udfpsController = udfpsControllerProvider.get()
+                udfpsRadius = authController.udfpsRadius
 
                 if (mView.isAttachedToWindow) {
                     udfpsController?.addCallback(udfpsControllerCallback)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 2f09792..8de7213 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -154,7 +154,9 @@
 
         //re-calculate the height of description
         View description = mView.findViewById(R.id.description);
-        totalHeight += measureDescription(description, displayHeight, width, totalHeight);
+        if (description != null && description.getVisibility() != View.GONE) {
+            totalHeight += measureDescription(description, displayHeight, width, totalHeight);
+        }
 
         return new AuthDialog.LayoutParams(width, totalHeight);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 726d00c..ee8363f 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -38,6 +38,7 @@
 import android.app.RemoteAction;
 import android.content.BroadcastReceiver;
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -128,6 +129,8 @@
     private final View mClipboardPreview;
     private final ImageView mImagePreview;
     private final TextView mTextPreview;
+    private final TextView mHiddenTextPreview;
+    private final TextView mHiddenImagePreview;
     private final View mPreviewBorder;
     private final OverlayActionChip mEditChip;
     private final OverlayActionChip mRemoteCopyChip;
@@ -186,6 +189,8 @@
         mClipboardPreview = requireNonNull(mView.findViewById(R.id.clipboard_preview));
         mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview));
         mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
+        mHiddenTextPreview = requireNonNull(mView.findViewById(R.id.hidden_text_preview));
+        mHiddenImagePreview = requireNonNull(mView.findViewById(R.id.hidden_image_preview));
         mPreviewBorder = requireNonNull(mView.findViewById(R.id.preview_border));
         mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
         mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
@@ -270,21 +275,32 @@
             mExitAnimator.cancel();
         }
         reset();
+
+        boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
+                && clipData.getDescription().getExtras()
+                .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
         if (clipData == null || clipData.getItemCount() == 0) {
-            showTextPreview(mContext.getResources().getString(
-                    R.string.clipboard_overlay_text_copied));
+            showTextPreview(
+                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
+                    mTextPreview);
         } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
             ClipData.Item item = clipData.getItemAt(0);
             if (item.getTextLinks() != null) {
                 AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
             }
-            showEditableText(item.getText());
+            if (isSensitive) {
+                showEditableText(
+                        mContext.getResources().getString(R.string.clipboard_text_hidden), true);
+            } else {
+                showEditableText(item.getText(), false);
+            }
         } else if (clipData.getItemAt(0).getUri() != null) {
             // How to handle non-image URIs?
-            showEditableImage(clipData.getItemAt(0).getUri());
+            showEditableImage(clipData.getItemAt(0).getUri(), isSensitive);
         } else {
             showTextPreview(
-                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
+                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
+                    mTextPreview);
         }
         Intent remoteCopyIntent = getRemoteCopyIntent(clipData);
         // Only show remote copy if it's available.
@@ -406,15 +422,23 @@
         animateOut();
     }
 
-    private void showTextPreview(CharSequence text) {
-        mTextPreview.setVisibility(View.VISIBLE);
+    private void showSinglePreview(View v) {
+        mTextPreview.setVisibility(View.GONE);
         mImagePreview.setVisibility(View.GONE);
-        mTextPreview.setText(text.subSequence(0, Math.min(500, text.length())));
+        mHiddenTextPreview.setVisibility(View.GONE);
+        mHiddenImagePreview.setVisibility(View.GONE);
+        v.setVisibility(View.VISIBLE);
+    }
+
+    private void showTextPreview(CharSequence text, TextView textView) {
+        showSinglePreview(textView);
+        textView.setText(text.subSequence(0, Math.min(500, text.length())));
         mEditChip.setVisibility(View.GONE);
     }
 
-    private void showEditableText(CharSequence text) {
-        showTextPreview(text);
+    private void showEditableText(CharSequence text, boolean hidden) {
+        TextView textView = hidden ? mHiddenTextPreview : mTextPreview;
+        showTextPreview(text, textView);
         mEditChip.setVisibility(View.VISIBLE);
         mActionContainerBackground.setVisibility(View.VISIBLE);
         mEditChip.setAlpha(1f);
@@ -422,32 +446,36 @@
                 mContext.getString(R.string.clipboard_edit_text_description));
         View.OnClickListener listener = v -> editText();
         mEditChip.setOnClickListener(listener);
-        mTextPreview.setOnClickListener(listener);
+        textView.setOnClickListener(listener);
     }
 
-    private void showEditableImage(Uri uri) {
-        ContentResolver resolver = mContext.getContentResolver();
-        try {
-            int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
-            // The width of the view is capped, height maintains aspect ratio, so allow it to be
-            // taller if needed.
-            Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
-            mImagePreview.setImageBitmap(thumbnail);
-        } catch (IOException e) {
-            Log.e(TAG, "Thumbnail loading failed", e);
-            showTextPreview(
-                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
-            return;
-        }
-        mTextPreview.setVisibility(View.GONE);
-        mImagePreview.setVisibility(View.VISIBLE);
+    private void showEditableImage(Uri uri, boolean isSensitive) {
         mEditChip.setAlpha(1f);
         mActionContainerBackground.setVisibility(View.VISIBLE);
         View.OnClickListener listener = v -> editImage(uri);
+        if (isSensitive) {
+            showSinglePreview(mHiddenImagePreview);
+            mHiddenImagePreview.setOnClickListener(listener);
+        } else {
+            showSinglePreview(mImagePreview);
+            ContentResolver resolver = mContext.getContentResolver();
+            try {
+                int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
+                // The width of the view is capped, height maintains aspect ratio, so allow it to be
+                // taller if needed.
+                Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
+                mImagePreview.setImageBitmap(thumbnail);
+            } catch (IOException e) {
+                Log.e(TAG, "Thumbnail loading failed", e);
+                showTextPreview(
+                        mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
+                        mTextPreview);
+            }
+            mImagePreview.setOnClickListener(listener);
+        }
         mEditChip.setOnClickListener(listener);
         mEditChip.setContentDescription(
                 mContext.getString(R.string.clipboard_edit_image_description));
-        mImagePreview.setOnClickListener(listener);
     }
 
     private Intent getRemoteCopyIntent(ClipData clipData) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index 0d89879..b54b832 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -20,10 +20,12 @@
 
 import android.app.Activity;
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.ClipboardManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.util.Log;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
@@ -41,6 +43,7 @@
     private EditText mEditText;
     private ClipboardManager mClipboardManager;
     private TextView mAttribution;
+    private boolean mSensitive;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -72,6 +75,9 @@
         }
         mEditText.setText(clip.getItemAt(0).getText());
         mEditText.requestFocus();
+        mSensitive = clip.getDescription().getExtras() != null
+                && clip.getDescription().getExtras()
+                .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
         mClipboardManager.addPrimaryClipChangedListener(this);
     }
 
@@ -88,6 +94,9 @@
 
     private void saveToClipboard() {
         ClipData clip = ClipData.newPlainText("text", mEditText.getText());
+        PersistableBundle extras = new PersistableBundle();
+        extras.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, mSensitive);
+        clip.getDescription().setExtras(extras);
         mClipboardManager.setPrimaryClip(clip);
         hideImeAndFinish();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
rename to packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 5d154c3..4e48a52 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -38,7 +38,6 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -80,8 +79,19 @@
 import dagger.Provides;
 
 /**
- * A dagger module for injecting default implementations of components of System UI that may be
- * overridden by the System UI implementation.
+ * A dagger module for injecting default implementations of components of System UI.
+ *
+ * Variants of SystemUI should make a copy of this, include it in their component, and customize it
+ * as needed.
+ *
+ * This module might alternatively be named `AospSystemUIModule`, `PhoneSystemUIModule`,
+ * or `BasicSystemUIModule`.
+ *
+ * Nothing in the module should be strictly required. Each piece should either be swappable with
+ * a different implementation or entirely removable.
+ *
+ * This is different from {@link SystemUIModule} which should be used for pieces of required
+ * SystemUI code that variants of SystemUI _must_ include to function correctly.
  */
 @Module(includes = {
         MediaModule.class,
@@ -90,7 +100,7 @@
         StartCentralSurfacesModule.class,
         VolumeModule.class
 })
-public abstract class SystemUIDefaultModule {
+public abstract class ReferenceSystemUIModule {
 
     @SysUISingleton
     @Provides
@@ -101,9 +111,6 @@
     }
 
     @Binds
-    abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
-
-    @Binds
     abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
             NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
 
@@ -148,6 +155,7 @@
         return spC;
     }
 
+    /** */
     @Binds
     @SysUISingleton
     public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 701972a..5d34a69 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -73,7 +73,7 @@
         SystemUIBinder.class,
         SystemUIModule.class,
         SystemUICoreStartableModule.class,
-        SystemUIDefaultModule.class})
+        ReferenceSystemUIModule.class})
 public interface SysUIComponent {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index bbeb66c..535eff8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -100,8 +100,14 @@
 import dagger.Provides;
 
 /**
- * A dagger module for injecting components of System UI that are not overridden by the System UI
- * implementation.
+ * A dagger module for injecting components of System UI that are required by System UI.
+ *
+ * If your feature can be excluded, subclassed, or re-implemented by a variant of SystemUI, put
+ * your Dagger Module in {@link ReferenceSystemUIModule} and/or any variant modules that
+ * rely on the feature.
+ *
+ * Adding an entry in this file means that _all_ variants of SystemUI will receive that code. They
+ * may not appreciate that.
  */
 @Module(includes = {
             AppOpsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 74044e2..1cc5df5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -39,7 +39,6 @@
 import com.android.systemui.doze.DozeMachine.State;
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.Assert;
@@ -94,7 +93,6 @@
     private final AuthController mAuthController;
     private final DelayableExecutor mMainExecutor;
     private final KeyguardStateController mKeyguardStateController;
-    private final BatteryController mBatteryController;
     private final UiEventLogger mUiEventLogger;
     private final DevicePostureController mDevicePostureController;
 
@@ -186,8 +184,7 @@
             @Main DelayableExecutor mainExecutor,
             UiEventLogger uiEventLogger,
             KeyguardStateController keyguardStateController,
-            DevicePostureController devicePostureController,
-            BatteryController batteryController) {
+            DevicePostureController devicePostureController) {
         mContext = context;
         mDozeHost = dozeHost;
         mConfig = config;
@@ -208,7 +205,6 @@
         mMainExecutor = mainExecutor;
         mUiEventLogger = uiEventLogger;
         mKeyguardStateController = keyguardStateController;
-        mBatteryController = batteryController;
     }
     private final DevicePostureController.Callback mDevicePostureCallback =
             posture -> {
@@ -320,12 +316,7 @@
                     gentleWakeUp(pulseReason);
                 } else if (isPickup) {
                     if (shouldDropPickupEvent())  {
-                        mDozeLog.traceSensorEventDropped(
-                                pulseReason,
-                                "keyguardOccluded="
-                                        + mKeyguardStateController.isOccluded()
-                                        + " pluggedInWireless="
-                                        + mBatteryController.isPluggedInWireless());
+                        mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded");
                         return;
                     }
                     gentleWakeUp(pulseReason);
@@ -356,7 +347,7 @@
     }
 
     private boolean shouldDropPickupEvent() {
-        return mKeyguardStateController.isOccluded() || mBatteryController.isPluggedInWireless();
+        return mKeyguardStateController.isOccluded();
     }
 
     private void gentleWakeUp(int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 9aebb9d..14a7e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
 import com.android.systemui.shared.system.smartspace.SmartspaceState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
@@ -140,7 +141,8 @@
     keyguardViewMediator: Lazy<KeyguardViewMediator>,
     private val keyguardViewController: KeyguardViewController,
     private val featureFlags: FeatureFlags,
-    private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>
+    private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
+    private val statusBarStateController: SysuiStatusBarStateController
 ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() {
 
     interface KeyguardUnlockAnimationListener {
@@ -372,7 +374,8 @@
      * changed.
      */
     override fun onKeyguardGoingAwayChanged() {
-        if (keyguardStateController.isKeyguardGoingAway) {
+        if (keyguardStateController.isKeyguardGoingAway
+            && !statusBarStateController.leaveOpenOnKeyguardHide()) {
             prepareForInWindowLauncherAnimations()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7becc82..4d59f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -911,12 +911,12 @@
                         RemoteAnimationTarget[] wallpapers,
                         RemoteAnimationTarget[] nonApps,
                         IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+                    setOccluded(false /* isOccluded */, true /* animate */);
+
                     if (apps == null || apps.length == 0 || apps[0] == null) {
                         Log.d(TAG, "No apps provided to unocclude runner; "
                                 + "skipping animation and unoccluding.");
-
                         finishedCallback.onAnimationFinished();
-                        setOccluded(false /* isOccluded */, true /* animate */);
                         return;
                     }
 
@@ -961,7 +961,6 @@
                             @Override
                             public void onAnimationEnd(Animator animation) {
                                 try {
-                                    setOccluded(false /* isOccluded */, true /* animate */);
                                     finishedCallback.onAnimationFinished();
                                     mUnoccludeAnimator = null;
                                 } catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 2fec499..d659401 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -636,6 +636,10 @@
                     mIsArtworkBound = isArtworkBound;
                 }
 
+                // Scrim bounds are set manually so it scales as expected
+                albumView.getForeground().setBounds(0, 0,
+                        Math.max(width, height), Math.max(width, height));
+
                 // Transition Colors to current color scheme
                 mColorSchemeTransition.updateColorScheme(colorScheme, mIsArtworkBound);
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 1437c96..7eccb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -278,13 +278,14 @@
     /**
      * Apply squishFraction to a copy of viewState such that the cached version is untouched.
      */
-    private fun squishViewState(
+    @VisibleForTesting
+    internal fun squishViewState(
         viewState: TransitionViewState,
         squishFraction: Float
     ): TransitionViewState {
         val squishedViewState = viewState.copy()
         squishedViewState.height = (squishedViewState.height * squishFraction).toInt()
-        val albumArtViewState = viewState.widgetStates.get(R.id.album_art)
+        val albumArtViewState = squishedViewState.widgetStates.get(R.id.album_art)
         if (albumArtViewState != null) {
             albumArtViewState.height = squishedViewState.height
         }
@@ -317,6 +318,7 @@
         if (transitionLayout == null) {
             return null
         }
+
         // Not cached. Let's create a new measurement
         if (state.expansion == 0.0f || state.expansion == 1.0f) {
             result = transitionLayout!!.calculateViewState(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 3bb4e64..3c373f4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -70,6 +70,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.Region;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -91,6 +92,8 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
 import android.view.WindowManager;
@@ -121,6 +124,7 @@
 import com.android.systemui.navigationbar.buttons.DeadZone;
 import com.android.systemui.navigationbar.buttons.KeyButtonView;
 import com.android.systemui.navigationbar.buttons.RotationContextButton;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
@@ -153,6 +157,7 @@
 
 import java.io.PrintWriter;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Optional;
 import java.util.function.Consumer;
 
@@ -199,12 +204,14 @@
     private final Optional<Recents> mRecentsOptional;
     private final DeviceConfigProxy mDeviceConfigProxy;
     private final NavigationBarTransitions mNavigationBarTransitions;
+    private final EdgeBackGestureHandler mEdgeBackGestureHandler;
     private final Optional<BackAnimation> mBackAnimation;
     private final Handler mHandler;
     private final UiEventLogger mUiEventLogger;
     private final NavBarHelper mNavBarHelper;
     private final NotificationShadeDepthController mNotificationShadeDepthController;
     private final UserContextProvider mUserContextProvider;
+    private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
     private NavigationBarFrame mFrame;
 
     private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -516,6 +523,7 @@
             DeadZone deadZone,
             DeviceConfigProxy deviceConfigProxy,
             NavigationBarTransitions navigationBarTransitions,
+            EdgeBackGestureHandler edgeBackGestureHandler,
             Optional<BackAnimation> backAnimation,
             UserContextProvider userContextProvider) {
         super(navigationBarView);
@@ -542,6 +550,7 @@
         mDeadZone = deadZone;
         mDeviceConfigProxy = deviceConfigProxy;
         mNavigationBarTransitions = navigationBarTransitions;
+        mEdgeBackGestureHandler = edgeBackGestureHandler;
         mBackAnimation = backAnimation;
         mHandler = mainHandler;
         mUiEventLogger = uiEventLogger;
@@ -555,6 +564,29 @@
         mInputMethodManager = inputMethodManager;
         mUserContextProvider = userContextProvider;
 
+        mOnComputeInternalInsetsListener = info -> {
+            // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
+            // gestural mode, the entire nav bar should be touchable.
+            if (!mEdgeBackGestureHandler.isHandlingGestures()) {
+                // We're in 2/3 button mode OR back button force-shown in SUW
+                if (!mImeVisible) {
+                    // IME not showing, take all touches
+                    info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+                }
+                if (!mView.isImeRenderingNavButtons()) {
+                    // IME showing but not drawing any buttons, take all touches
+                    info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+                }
+            }
+
+            // When in gestural and the IME is showing, don't use the nearest region since it will
+            // take gesture space away from the IME
+            info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            info.touchableRegion.set(getButtonLocations(false /* includeFloatingButtons */,
+                    false /* inScreen */, false /* useNearestRegion */));
+        };
+
+        mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler);
         mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
     }
 
@@ -569,6 +601,7 @@
         mView.setBarTransitions(mNavigationBarTransitions);
         mView.setTouchHandler(mTouchHandler);
         mView.setNavBarMode(mNavBarMode);
+        mEdgeBackGestureHandler.setStateChangeCallback(mView::updateStates);
         mView.updateRotationButton();
 
         mView.setVisibility(
@@ -646,11 +679,14 @@
         mView.setNavBarMode(mNavBarMode);
         mView.setUpdateActiveTouchRegionsCallback(
                 () -> mOverviewProxyService.onActiveNavBarRegionChanges(
-                        mView.getButtonLocations(
+                        getButtonLocations(
                                 true /* includeFloatingButtons */,
                                 true /* inScreen */,
                                 true /* useNearestRegion */)));
 
+        mView.getViewTreeObserver().addOnComputeInternalInsetsListener(
+                mOnComputeInternalInsetsListener);
+
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
 
         mPipOptional.ifPresent(mView::addPipExclusionBoundsChangeListener);
@@ -721,6 +757,8 @@
             mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
                     mOrientationHandleGlobalLayoutListener);
         }
+        mView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
+                mOnComputeInternalInsetsListener);
         mHandler.removeCallbacks(mAutoDim);
         mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
         mHandler.removeCallbacks(mEnableLayoutTransitions);
@@ -1044,8 +1082,8 @@
     }
 
     private void handleTransientChanged() {
-        mView.onTransientStateChanged(mTransientShown,
-                mTransientShownFromGestureOnSystemBar);
+        mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTransientShown);
+
         final int transitionMode = transitionMode(mTransientShown, mAppearance);
         if (updateTransitionMode(transitionMode) && mLightBarController != null) {
             mLightBarController.onNavigationBarModeChanged(transitionMode);
@@ -1639,6 +1677,79 @@
         mNavigationIconHints = hints;
     }
 
+    /**
+     * @param includeFloatingButtons Whether to include the floating rotation and overlay button in
+     *                               the region for all the buttons
+     * @param inScreenSpace Whether to return values in screen space or window space
+     * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds
+     * @return
+     */
+    Region getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace,
+            boolean useNearestRegion) {
+        if (useNearestRegion && !inScreenSpace) {
+            // We currently don't support getting the nearest region in anything but screen space
+            useNearestRegion = false;
+        }
+        Region region = new Region();
+        Map<View, Rect> touchRegionCache = mView.getButtonTouchRegionCache();
+        updateButtonLocation(
+                region, touchRegionCache, mView.getBackButton(), inScreenSpace, useNearestRegion);
+        updateButtonLocation(
+                region, touchRegionCache, mView.getHomeButton(), inScreenSpace, useNearestRegion);
+        updateButtonLocation(region, touchRegionCache, mView.getRecentsButton(), inScreenSpace,
+                useNearestRegion);
+        updateButtonLocation(region, touchRegionCache, mView.getImeSwitchButton(), inScreenSpace,
+                useNearestRegion);
+        updateButtonLocation(
+                region, touchRegionCache, mView.getAccessibilityButton(), inScreenSpace,
+                useNearestRegion);
+        if (includeFloatingButtons && mView.getFloatingRotationButton().isVisible()) {
+            // Note: this button is floating so the nearest region doesn't apply
+            updateButtonLocation(
+                    region, mView.getFloatingRotationButton().getCurrentView(), inScreenSpace);
+        } else {
+            updateButtonLocation(region, touchRegionCache, mView.getRotateSuggestionButton(),
+                    inScreenSpace, useNearestRegion);
+        }
+        return region;
+    }
+
+    private void updateButtonLocation(
+            Region region,
+            Map<View, Rect> touchRegionCache,
+            ButtonDispatcher button,
+            boolean inScreenSpace,
+            boolean useNearestRegion) {
+        if (button == null) {
+            return;
+        }
+        View view = button.getCurrentView();
+        if (view == null || !button.isVisible()) {
+            return;
+        }
+        // If the button is tappable from perspective of NearestTouchFrame, then we'll
+        // include the regions where the tap is valid instead of just the button layout location
+        if (useNearestRegion && touchRegionCache.containsKey(view)) {
+            region.op(touchRegionCache.get(view), Region.Op.UNION);
+            return;
+        }
+        updateButtonLocation(region, view, inScreenSpace);
+    }
+
+    private void updateButtonLocation(Region region, View view, boolean inScreenSpace) {
+        Rect bounds = new Rect();
+        if (inScreenSpace) {
+            view.getBoundsOnScreen(bounds);
+        } else {
+            int[] location = new int[2];
+            view.getLocationInWindow(location);
+            bounds.set(location[0], location[1],
+                    location[0] + view.getWidth(),
+                    location[1] + view.getHeight());
+        }
+        region.op(bounds, Region.Op.UNION);
+    }
+
     private final ModeChangedListener mModeChangedListener = new ModeChangedListener() {
         @Override
         public void onNavigationModeChanged(int mode) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 93bf136..f6bfd6c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -24,6 +24,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 
 import dagger.Module;
 import dagger.Provides;
@@ -55,6 +56,14 @@
         return barView.findViewById(R.id.navigation_bar_view);
     }
 
+    /** */
+    @Provides
+    @NavigationBarScope
+    static EdgeBackGestureHandler provideEdgeBackGestureHandler(
+            EdgeBackGestureHandler.Factory factory, @DisplayId Context context) {
+        return factory.create(context);
+    }
+
     /** A WindowManager specific to the display's context. */
     @Provides
     @NavigationBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 6e00ebc..a13c199 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -41,8 +41,6 @@
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Region.Op;
 import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -53,8 +51,6 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver.InternalInsetsInfo;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.WindowInsets;
 import android.view.WindowInsetsController.Behavior;
 import android.view.WindowManager;
@@ -93,7 +89,6 @@
 import com.android.wm.shell.pip.Pip;
 
 import java.io.PrintWriter;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -123,11 +118,6 @@
     private int mNavBarMode;
     private boolean mImeDrawsImeNavBar;
 
-    private final Region mTmpRegion = new Region();
-    private final int[] mTmpPosition = new int[2];
-    private Rect mTmpBounds = new Rect();
-    private Map<View, Rect> mButtonFullTouchableRegions = new HashMap<>();
-
     private KeyButtonDrawable mBackIcon;
     private KeyButtonDrawable mHomeDefaultIcon;
     private KeyButtonDrawable mRecentIcon;
@@ -138,7 +128,6 @@
 
     private EdgeBackGestureHandler mEdgeBackGestureHandler;
     private final DeadZone mDeadZone;
-    private boolean mDeadZoneConsuming = false;
     private NavigationBarTransitions mBarTransitions;
     @Nullable
     private AutoHideController mAutoHideController;
@@ -152,7 +141,6 @@
     private boolean mUseCarModeUi = false;
     private boolean mInCarMode = false;
     private boolean mDockedStackExists;
-    private boolean mImeVisible;
     private boolean mScreenOn = true;
 
     private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
@@ -272,31 +260,6 @@
                 }
             };
 
-    private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
-        // When the nav bar is in 2-button or 3-button mode, or when the back button is force-shown
-        // while in gesture nav in SUW, the entire nav bar should be touchable.
-        if (!mEdgeBackGestureHandler.isHandlingGestures()) {
-            // We're in 2/3 button mode OR back button force-shown in SUW
-            if (!mImeVisible) {
-                // IME not showing, take all touches
-                info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
-                return;
-            }
-  
-            if (!isImeRenderingNavButtons()) {
-                // IME showing but not drawing any buttons, take all touches
-                info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
-                return;     
-            }
-        }
-
-        // When in gestural and the IME is showing, don't use the nearest region since it will take
-        // gesture space away from the IME
-        info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-        info.touchableRegion.set(getButtonLocations(false /* includeFloatingButtons */,
-                false /* inScreen */, false /* useNearestRegion */));
-    };
-
     private final RotationButtonUpdatesCallback mRotationButtonListener =
             new RotationButtonUpdatesCallback() {
                 @Override
@@ -315,13 +278,6 @@
                 }
             };
 
-    private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
-        if (visible && mAutoHideController != null) {
-            mAutoHideController.touchAutoHide();
-        }
-        notifyActiveTouchRegions();
-    };
-
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -380,9 +336,6 @@
 
         mNavColorSampleMargin = getResources()
                         .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
-        mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
-                .create(mContext);
-        mEdgeBackGestureHandler.setStateChangeCallback(this::updateStates);
         Executor backgroundExecutor = Dependency.get(Dependency.BACKGROUND_EXECUTOR);
         mRegionSamplingHelper = new RegionSamplingHelper(this,
                 new RegionSamplingHelper.SamplingCallback() {
@@ -409,6 +362,10 @@
                 }, backgroundExecutor);
     }
 
+    public void setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler) {
+        mEdgeBackGestureHandler = edgeBackGestureHandler;
+    }
+
     void setBarTransitions(NavigationBarTransitions navigationBarTransitions) {
         mBarTransitions = navigationBarTransitions;
     }
@@ -681,8 +638,7 @@
         if (!visible) {
             mTransitionListener.onBackAltCleared();
         }
-        mImeVisible = visible;
-        mRotationButtonController.getRotationButton().setCanShowRotationButton(!mImeVisible);
+        mRotationButtonController.getRotationButton().setCanShowRotationButton(!visible);
     }
 
     void setDisabledFlags(int disabledFlags, SysUiState sysUiState) {
@@ -774,7 +730,7 @@
     /**
      * Returns whether the IME is currently visible and drawing the nav buttons.
      */
-    private boolean isImeRenderingNavButtons() {
+    boolean isImeRenderingNavButtons() {
         return mImeDrawsImeNavBar
                 && mImeCanRenderGesturalNavButtons
                 && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
@@ -1003,75 +959,14 @@
         notifyActiveTouchRegions();
     }
 
-    private void updateButtonTouchRegionCache() {
+    Map<View, Rect> getButtonTouchRegionCache() {
         FrameLayout navBarLayout = mIsVertical
                 ? mNavigationInflaterView.mVertical
                 : mNavigationInflaterView.mHorizontal;
-        mButtonFullTouchableRegions = ((NearestTouchFrame) navBarLayout
+        return ((NearestTouchFrame) navBarLayout
                 .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions();
     }
 
-    /**
-     * @param includeFloatingButtons Whether to include the floating rotation and overlay button in
-     *                               the region for all the buttons
-     * @param inScreenSpace Whether to return values in screen space or window space
-     * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds
-     * @return
-     */
-    Region getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace,
-            boolean useNearestRegion) {
-        // TODO: move this method to NavigationBar.
-        // TODO: don't use member variables for temp storage like mTmpRegion.
-        if (useNearestRegion && !inScreenSpace) {
-            // We currently don't support getting the nearest region in anything but screen space
-            useNearestRegion = false;
-        }
-        mTmpRegion.setEmpty();
-        updateButtonTouchRegionCache();
-        updateButtonLocation(getBackButton(), inScreenSpace, useNearestRegion);
-        updateButtonLocation(getHomeButton(), inScreenSpace, useNearestRegion);
-        updateButtonLocation(getRecentsButton(), inScreenSpace, useNearestRegion);
-        updateButtonLocation(getImeSwitchButton(), inScreenSpace, useNearestRegion);
-        updateButtonLocation(getAccessibilityButton(), inScreenSpace, useNearestRegion);
-        if (includeFloatingButtons && mFloatingRotationButton.isVisible()) {
-            // Note: this button is floating so the nearest region doesn't apply
-            updateButtonLocation(mFloatingRotationButton.getCurrentView(), inScreenSpace);
-        } else {
-            updateButtonLocation(getRotateSuggestionButton(), inScreenSpace, useNearestRegion);
-        }
-        return mTmpRegion;
-    }
-
-    private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace,
-            boolean useNearestRegion) {
-        if (button == null) {
-            return;
-        }
-        View view = button.getCurrentView();
-        if (view == null || !button.isVisible()) {
-            return;
-        }
-        // If the button is tappable from perspective of NearestTouchFrame, then we'll
-        // include the regions where the tap is valid instead of just the button layout location
-        if (useNearestRegion && mButtonFullTouchableRegions.containsKey(view)) {
-            mTmpRegion.op(mButtonFullTouchableRegions.get(view), Op.UNION);
-            return;
-        }
-        updateButtonLocation(view, inScreenSpace);
-    }
-
-    private void updateButtonLocation(View view, boolean inScreenSpace) {
-        if (inScreenSpace) {
-            view.getBoundsOnScreen(mTmpBounds);
-        } else {
-            view.getLocationInWindow(mTmpPosition);
-            mTmpBounds.set(mTmpPosition[0], mTmpPosition[1],
-                    mTmpPosition[0] + view.getWidth(),
-                    mTmpPosition[1] + view.getHeight());
-        }
-        mTmpRegion.op(mTmpBounds, Op.UNION);
-    }
-
     private void updateOrientationViews() {
         mHorizontal = findViewById(R.id.horizontal);
         mVertical = findViewById(R.id.vertical);
@@ -1272,7 +1167,6 @@
             mRotationButtonController.registerListeners();
         }
 
-        getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
         updateNavButtonIcons();
     }
 
@@ -1288,8 +1182,6 @@
         }
 
         mEdgeBackGestureHandler.onNavBarDetached();
-        getViewTreeObserver().removeOnComputeInternalInsetsListener(
-                mOnComputeInternalInsetsListener);
     }
 
     public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 8b8941a..3709a86 100644
--- a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.power.dagger;
 
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 
@@ -28,5 +30,9 @@
 public interface PowerModule {
     /** */
     @Binds
+    EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
+
+    /** */
+    @Binds
     PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 772e9fa..248c78e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -147,16 +147,9 @@
         if (mController.getWalletClient().isWalletServiceAvailable()
                 && mController.getWalletClient().isWalletFeatureAvailable()) {
             if (mSelectedCard != null) {
-                if (isDeviceLocked) {
-                    state.state = Tile.STATE_INACTIVE;
-                    state.secondaryLabel =
-                            mContext.getString(R.string.wallet_secondary_label_device_locked);
-                    state.sideViewCustomDrawable = null;
-                } else {
-                    state.state = Tile.STATE_ACTIVE;
-                    state.secondaryLabel = mSelectedCard.getContentDescription();
-                    state.sideViewCustomDrawable = mCardViewDrawable;
-                }
+                state.state = isDeviceLocked ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE;
+                state.secondaryLabel = mSelectedCard.getContentDescription();
+                state.sideViewCustomDrawable = mCardViewDrawable;
             } else {
                 state.state = Tile.STATE_INACTIVE;
                 state.secondaryLabel =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index f4dd415..d99c1d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -92,15 +92,15 @@
 
     @Override
     protected void handleClick(@Nullable View view) {
-        if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) {
-            mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
-                mSensorPrivacyController.setSensorBlocked(QS_TILE, getSensorId(),
-                        !mSensorPrivacyController.isSensorBlocked(getSensorId()));
-            });
+        boolean blocked = mSensorPrivacyController.isSensorBlocked(getSensorId());
+        if (mSensorPrivacyController.requiresAuthentication()
+                && mKeyguard.isMethodSecure()
+                && mKeyguard.isShowing()) {
+            mActivityStarter.postQSRunnableDismissingKeyguard(() ->
+                    mSensorPrivacyController.setSensorBlocked(QS_TILE, getSensorId(), !blocked));
             return;
         }
-        mSensorPrivacyController.setSensorBlocked(QS_TILE, getSensorId(),
-                !mSensorPrivacyController.isSensorBlocked(getSensorId()));
+        mSensorPrivacyController.setSensorBlocked(QS_TILE, getSensorId(), !blocked);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index dae375a..2d1d8b7 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -134,7 +134,9 @@
     override fun onClick(dialog: DialogInterface?, which: Int) {
         when (which) {
             BUTTON_POSITIVE -> {
-                if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
+                if (sensorPrivacyController.requiresAuthentication() &&
+                        keyguardStateController.isMethodSecure &&
+                        keyguardStateController.isShowing) {
                     keyguardDismissUtil.executeWhenUnlocked({
                         bgHandler.postDelayed({
                             disableSensorPrivacy()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 83970dc..629aa03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -220,7 +220,7 @@
         }
     }
 
-    public float getMinStackScrollerPadding() {
+    public float getLockscreenMinStackScrollerPadding() {
         if (mBypassEnabled) {
             return mUnlockedStackScrollerPadding;
         } else if (mIsSplitShade) {
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 985df42..74b9c71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -24,6 +24,7 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
 import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -325,6 +326,11 @@
     private boolean mShouldUseSplitNotificationShade;
     // The bottom padding reserved for elements of the keyguard measuring notifications
     private float mKeyguardNotificationBottomPadding;
+    /**
+     * The top padding from where notification should start in lockscreen.
+     * Should be static also during animations and should match the Y of the first notification.
+     */
+    private float mKeyguardNotificationTopPadding;
     // Current max allowed keyguard notifications determined by measuring the panel
     private int mMaxAllowedKeyguardNotifications;
 
@@ -936,7 +942,7 @@
                                         // the launcher icons animation starts, so use that as our
                                         // duration.
                                         .setDuration(unlockAnimationStartDelay)
-                                        .setInterpolator(EMPHASIZED_DECELERATE)
+                                        .setInterpolator(EMPHASIZED_ACCELERATE)
                                         .withEndAction(() -> {
                                             instantCollapse();
                                             mView.setAlpha(1f);
@@ -1513,7 +1519,10 @@
      */
     @VisibleForTesting
     float getSpaceForLockscreenNotifications() {
-        float topPadding = mNotificationStackScrollLayoutController.getTopPadding();
+        float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
+                // getMinStackScrollerPadding is from the top of the screen,
+                // but we need it from the top of the NSSL.
+                - mNotificationStackScrollLayoutController.getTop();
 
         // Space between bottom of notifications and top of lock icon or udfps background.
         float lockIconPadding = mLockIconViewController.getTop();
@@ -1525,11 +1534,15 @@
 
         float bottomPadding = Math.max(lockIconPadding,
                 Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
-        mKeyguardNotificationBottomPadding = bottomPadding;
 
+        mKeyguardNotificationBottomPadding = bottomPadding;
+        mKeyguardNotificationTopPadding = staticTopPadding;
+
+        // To debug the available space, enable debug lines in this class. If you change how the
+        // available space is calculated, please also update those lines.
         float availableSpace =
                 mNotificationStackScrollLayoutController.getHeight()
-                        - topPadding
+                        - staticTopPadding
                         - bottomPadding;
         return availableSpace;
     }
@@ -4923,6 +4936,20 @@
             drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
                     "mLockIconViewController.getTop()");
 
+            if (mKeyguardShowing) {
+                // Notifications have the space between those two lines.
+                drawDebugInfo(canvas,
+                        mNotificationStackScrollLayoutController.getTop() +
+                                (int) mKeyguardNotificationTopPadding,
+                        Color.RED,
+                        "NSSL.getTop() + mKeyguardNotificationTopPadding");
+
+                drawDebugInfo(canvas, mNotificationStackScrollLayoutController.getBottom() -
+                                (int) mKeyguardNotificationBottomPadding,
+                        Color.RED,
+                        "NSSL.getBottom() - mKeyguardNotificationBottomPadding");
+            }
+
             mDebugPaint.setColor(Color.CYAN);
             canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
                     mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 01aa2ec..faae4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -333,7 +333,7 @@
     }
 
     private void adjustScreenOrientation(State state) {
-        if (state.isKeyguardShowingAndNotOccluded() || state.mDozing) {
+        if (state.mBouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.mDozing) {
             if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index 1e73d59..eb08f37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -37,6 +37,11 @@
 
     void suppressSensorPrivacyReminders(int sensor, boolean suppress);
 
+    /**
+     * @return whether lock screen authentication is required to change the toggle state
+     */
+    boolean requiresAuthentication();
+
     interface Callback {
         void onSensorBlockedChanged(@Sensor int sensor, boolean blocked);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index e4c444d..fffd839 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -37,6 +37,7 @@
     private final @NonNull SensorPrivacyManager mSensorPrivacyManager;
     private final SparseBooleanArray mSoftwareToggleState = new SparseBooleanArray();
     private final SparseBooleanArray mHardwareToggleState = new SparseBooleanArray();
+    private Boolean mRequiresAuthentication;
     private final Set<Callback> mCallbacks = new ArraySet<>();
 
     public IndividualSensorPrivacyControllerImpl(
@@ -96,6 +97,11 @@
     }
 
     @Override
+    public boolean requiresAuthentication() {
+        return mSensorPrivacyManager.requiresAuthentication();
+    }
+
+    @Override
     public void addCallback(@NonNull Callback listener) {
         mCallbacks.add(listener);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index b7f90a4..4685c14 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -38,7 +38,6 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -102,9 +101,6 @@
     }
 
     @Binds
-    abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
-
-    @Binds
     abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
             NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 5b5dca3..d54de3fa 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -21,9 +21,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.provider.Settings;
-import android.view.ContextThemeWrapper;
 import android.view.DisplayCutout;
 
 import com.android.internal.policy.SystemBarUtils;
@@ -35,6 +33,8 @@
 
 public class Utils {
 
+    private static Boolean sUseQsMediaPlayer = null;
+
     /**
      * Allows lambda iteration over a list. It is done in reverse order so it is safe
      * to add or remove items during the iteration.  Skips over null items.
@@ -81,9 +81,16 @@
      * Off by default, but can be disabled by setting to 0
      */
     public static boolean useQsMediaPlayer(Context context) {
-        int flag = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1);
-        return flag > 0;
+        // TODO(b/192412820): Replace SHOW_MEDIA_ON_QUICK_SETTINGS with compile-time value
+        // Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS can't be toggled at runtime, so simply
+        // cache the first result we fetch and use that going forward. Do this to avoid unnecessary
+        // binder calls which may happen on the critical path.
+        if (sUseQsMediaPlayer == null) {
+            int flag = Settings.Global.getInt(context.getContentResolver(),
+                    Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1);
+            sUseQsMediaPlayer = flag > 0;
+        }
+        return sUseQsMediaPlayer;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index acff871..ebdddbf 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -180,7 +180,7 @@
         int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
         GetWalletCardsRequest request =
                 new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1);
-        mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever);
+        mQuickAccessWalletClient.getWalletCards(mCallbackExecutor, request, cardsRetriever);
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 7476490..39cc34b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver
 import android.database.ContentObserver
+import android.hardware.biometrics.BiometricFaceConstants
 import android.net.Uri
 import android.os.Handler
 import android.os.UserHandle
@@ -44,18 +45,20 @@
     private val fakeWakeUri = Uri.Builder().appendPath("wake").build()
     private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build()
     private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build()
+    private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build()
+    private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build()
+    private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build()
 
     @Mock
     private lateinit var secureSettings: SecureSettings
-
     @Mock
     private lateinit var contentResolver: ContentResolver
-
     @Mock
     private lateinit var handler: Handler
-
     @Mock
     private lateinit var dumpManager: DumpManager
+    @Mock
+    private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
     @Captor
     private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
@@ -72,6 +75,13 @@
                 .thenReturn(fakeUnlockIntentUri)
         `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
                 .thenReturn(fakeBioFailUri)
+        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS))
+                .thenReturn(fakeFaceErrorsUri)
+        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
+                .thenReturn(fakeFaceAcquiredUri)
+        `when`(secureSettings.getUriFor(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED))
+                .thenReturn(fakeUnlockIntentBioEnroll)
 
         activeUnlockConfig = ActiveUnlockConfig(
                 handler,
@@ -99,12 +109,7 @@
         // WHEN unlock on wake is allowed
         `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
                 0, 0)).thenReturn(1)
-        settingsObserverCaptor.value.onChange(
-                false,
-                listOf(fakeWakeUri),
-                0,
-                0
-        )
+        updateSetting(fakeWakeUri)
 
         // THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
         assertTrue(
@@ -134,12 +139,7 @@
         // WHEN unlock on biometric failed is allowed
         `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
                 0, 0)).thenReturn(1)
-        settingsObserverCaptor.value.onChange(
-                false,
-                listOf(fakeUnlockIntentUri),
-                0,
-                0
-        )
+        updateSetting(fakeUnlockIntentUri)
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -154,19 +154,19 @@
     fun testOnBioFailSettingChanged() {
         verifyRegisterSettingObserver()
 
-        // GIVEN no active unlock settings enabled
+        // GIVEN no active unlock settings enabled and triggering unlock intent on biometric
+        // enrollment setting is disabled (empty string is disabled, null would use the default)
+        `when`(secureSettings.getStringForUser(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                0)).thenReturn("")
+        updateSetting(fakeUnlockIntentBioEnroll)
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
                 ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
 
         // WHEN unlock on biometric failed is allowed
         `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
                 0, 0)).thenReturn(1)
-        settingsObserverCaptor.value.onChange(
-                false,
-                listOf(fakeBioFailUri),
-                0,
-                0
-        )
+        updateSetting(fakeBioFailUri)
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -177,21 +177,146 @@
                 ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
     }
 
+    @Test
+    fun testFaceErrorSettingsChanged() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // WHEN face error timeout (3), allow trigger active unlock
+        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
+                0)).thenReturn("3")
+        updateSetting(fakeFaceAcquiredUri)
+
+        // THEN active unlock triggers allowed on error TIMEOUT
+        assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+                BiometricFaceConstants.FACE_ERROR_TIMEOUT))
+
+        assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+                BiometricFaceConstants.FACE_ERROR_CANCELED))
+    }
+
+    @Test
+    fun testFaceAcquiredSettingsChanged() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
+        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
+                0)).thenReturn(
+                "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
+                        "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}")
+        updateSetting(fakeFaceAcquiredUri)
+
+        // THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
+        assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED))
+        assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED))
+
+        assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_GOOD))
+        assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED))
+    }
+
+    @Test
+    fun testTriggerOnUnlockIntentWhenBiometricEnrolledNone() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // GIVEN fingerprint and face are NOT enrolled
+        activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
+
+        // WHEN unlock intent is allowed when NO biometrics are enrolled (0)
+        `when`(secureSettings.getStringForUser(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                0)).thenReturn("${ActiveUnlockConfig.BIOMETRIC_TYPE_NONE}")
+        updateSetting(fakeUnlockIntentBioEnroll)
+
+        // THEN active unlock triggers allowed on unlock intent
+        assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+    }
+
+    @Test
+    fun testTriggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // GIVEN fingerprint and face are both enrolled
+        activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
+
+        // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
+        // are enrolled
+        `when`(secureSettings.getStringForUser(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                0)).thenReturn(
+                "${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FACE}" +
+                        "|${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FINGERPRINT}")
+        updateSetting(fakeUnlockIntentBioEnroll)
+
+        // THEN active unlock triggers NOT allowed on unlock intent
+        assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+
+        // WHEN fingerprint ONLY enrolled
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
+
+        // THEN active unlock triggers allowed on unlock intent
+        assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+
+        // WHEN face ONLY enrolled
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
+
+        // THEN active unlock triggers allowed on unlock intent
+        assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+    }
+
+    private fun updateSetting(uri: Uri) {
+        settingsObserverCaptor.value.onChange(
+                false,
+                listOf(uri),
+                0,
+                0 /* flags */
+        )
+    }
+
     private fun verifyRegisterSettingObserver() {
-        verify(contentResolver).registerContentObserver(
-                eq(fakeWakeUri),
-                eq(false),
-                capture(settingsObserverCaptor),
-                eq(UserHandle.USER_ALL))
+        verifyRegisterSettingObserver(fakeWakeUri)
+        verifyRegisterSettingObserver(fakeUnlockIntentUri)
+        verifyRegisterSettingObserver(fakeBioFailUri)
+        verifyRegisterSettingObserver(fakeFaceErrorsUri)
+        verifyRegisterSettingObserver(fakeFaceAcquiredUri)
+        verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll)
+    }
 
+    private fun verifyRegisterSettingObserver(uri: Uri) {
         verify(contentResolver).registerContentObserver(
-                eq(fakeUnlockIntentUri),
-                eq(false),
-                capture(settingsObserverCaptor),
-                eq(UserHandle.USER_ALL))
-
-        verify(contentResolver).registerContentObserver(
-                eq(fakeBioFailUri),
+                eq(uri),
                 eq(false),
                 capture(settingsObserverCaptor),
                 eq(UserHandle.USER_ALL))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index ae387e8..4eeb4ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,7 +45,6 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -91,8 +90,6 @@
     private KeyguardStateController mKeyguardStateController;
     @Mock
     private DevicePostureController mDevicePostureController;
-    @Mock
-    private BatteryController mBatteryController;
 
     private DozeTriggers mTriggers;
     private FakeSensorManager mSensors;
@@ -125,7 +122,7 @@
                 asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
                 mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
                 mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController,
-                mDevicePostureController, mBatteryController);
+                mDevicePostureController);
         mTriggers.setDozeMachine(mMachine);
         waitForSensorManager();
     }
@@ -233,9 +230,7 @@
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
 
         // WHEN the pick up gesture is triggered and keyguard isn't occluded
-        // and device isn't on a wireless charger
         when(mKeyguardStateController.isOccluded()).thenReturn(false);
-        when(mBatteryController.isPluggedInWireless()).thenReturn(false);
         mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
 
         // THEN wakeup
@@ -249,22 +244,6 @@
 
         // WHEN the pick up gesture is triggered and keyguard IS occluded
         when(mKeyguardStateController.isOccluded()).thenReturn(true);
-        when(mBatteryController.isPluggedInWireless()).thenReturn(false);
-        mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
-
-        // THEN never wakeup
-        verify(mMachine, never()).wakeUp();
-    }
-
-    @Test
-    public void testPickupGestureWirelessCharger() {
-        // GIVEN device is in doze (screen blank, but running doze sensors)
-        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
-
-        // WHEN the pick up gesture is triggered
-        // and device IS on a wireless charger
-        when(mKeyguardStateController.isOccluded()).thenReturn(false);
-        when(mBatteryController.isPluggedInWireless()).thenReturn(true);
         mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
 
         // THEN never wakeup
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 2d8c4d5..2abc666 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -14,6 +14,7 @@
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import junit.framework.Assert.assertEquals
@@ -49,6 +50,8 @@
     private lateinit var biometricUnlockController: BiometricUnlockController
     @Mock
     private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
+    @Mock
+    private lateinit var statusBarStateController: SysuiStatusBarStateController
 
     private lateinit var remoteAnimationTarget: RemoteAnimationTarget
 
@@ -57,7 +60,7 @@
         MockitoAnnotations.initMocks(this)
         keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
             context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
-            featureFlags, { biometricUnlockController }
+            featureFlags, { biometricUnlockController }, statusBarStateController
         )
 
         `when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 5ff316e..c532ed5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -34,8 +34,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedStateListDrawable;
 import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
@@ -77,9 +75,6 @@
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -182,7 +177,7 @@
     @Test
     public void testUpdateFingerprintLocationOnInit() {
         // GIVEN fp sensor location is available pre-attached
-        Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
+        Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
 
         // WHEN lock icon view controller is initialized and attached
         mLockIconViewController.init();
@@ -197,7 +192,7 @@
     @Test
     public void testUpdatePaddingBasedOnResolutionScale() {
         // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5
-        Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
+        Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
         when(mAuthController.getScaleFactor()).thenReturn(5f);
 
         // WHEN lock icon view controller is initialized and attached
@@ -215,20 +210,19 @@
         // GIVEN fp sensor location is not available pre-init
         when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
         when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
-        when(mAuthController.getUdfpsProps()).thenReturn(null);
         mLockIconViewController.init();
         captureAttachListener();
         mAttachListener.onViewAttachedToWindow(mLockIconView);
 
-        // GIVEN fp sensor location is available post-atttached
+        // GIVEN fp sensor location is available post-attached
         captureAuthControllerCallback();
-        Pair<Integer, PointF> udfps = setupUdfps();
+        Pair<Float, PointF> udfps = setupUdfps();
 
         // WHEN all authenticators are registered
         mAuthControllerCallback.onAllAuthenticatorsRegistered();
         mDelayableExecutor.runAllReady();
 
-        // THEN lock icon view location is updated with the same coordinates as fpProps
+        // THEN lock icon view location is updated with the same coordinates as auth controller vals
         verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
                 eq(PADDING));
     }
@@ -402,21 +396,10 @@
         verify(mLockIconView).setTranslationX(0);
 
     }
-    private Pair<Integer, PointF> setupUdfps() {
+    private Pair<Float, PointF> setupUdfps() {
         when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
         final PointF udfpsLocation = new PointF(50, 75);
-        final int radius = 33;
-        final FingerprintSensorPropertiesInternal fpProps =
-                new FingerprintSensorPropertiesInternal(
-                        /* sensorId */ 0,
-                        /* strength */ 0,
-                        /* max enrollments per user */ 5,
-                        /* component info */ new ArrayList<>(),
-                        /* sensorType */ 3,
-                        /* halControlsIllumination */ true,
-                        /* resetLockoutRequiresHwToken */ false,
-                        List.of(new SensorLocationInternal("" /* displayId */,
-                                (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
+        final float radius = 33f;
         when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
         when(mAuthController.getUdfpsRadius()).thenReturn(radius);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index f9fb865..0ed579f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -21,7 +21,6 @@
 import android.app.PendingIntent
 import android.app.smartspace.SmartspaceAction
 import android.content.Context
-import org.mockito.Mockito.`when` as whenever
 import android.content.Intent
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
@@ -67,8 +66,8 @@
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.KotlinArgumentCaptor
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.withArgCaptor
@@ -92,6 +91,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private const val KEY = "TEST_KEY"
 private const val PACKAGE = "PKG"
@@ -339,6 +339,7 @@
         whenever(viewHolder.player).thenReturn(view)
         whenever(viewHolder.appIcon).thenReturn(appIcon)
         whenever(viewHolder.albumView).thenReturn(albumView)
+        whenever(albumView.foreground).thenReturn(mock(Drawable::class.java))
         whenever(viewHolder.titleText).thenReturn(titleText)
         whenever(viewHolder.artistText).thenReturn(artistText)
         whenever(seamlessBackground.getDrawable(0)).thenReturn(mock(GradientDrawable::class.java))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
index 604e1f3..1817809 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
@@ -4,16 +4,22 @@
 import android.testing.TestableLooper
 import android.view.View
 import androidx.test.filters.SmallTest
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.animation.MeasurementInput
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.animation.TransitionViewState
+import com.android.systemui.util.animation.WidgetState
 import junit.framework.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 /**
  * Tests for {@link MediaViewController}.
@@ -31,6 +37,9 @@
     private lateinit var mediaViewController: MediaViewController
     private val mediaHostStateHolder = MediaHost.MediaHostStateHolder()
     private var transitionLayout = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
+    @Mock private lateinit var mockViewState: TransitionViewState
+    @Mock private lateinit var mockCopiedState: TransitionViewState
+    @Mock private lateinit var mockWidgetState: WidgetState
 
     @Before
     fun setUp() {
@@ -63,4 +72,15 @@
         mediaHostStateHolder.squishFraction = 0.5f
         assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 50)
     }
+
+    @Test
+    fun testSquish_DoesNotMutateViewState() {
+        whenever(mockViewState.copy()).thenReturn(mockCopiedState)
+        whenever(mockCopiedState.widgetStates)
+            .thenReturn(mutableMapOf(R.id.album_art to mockWidgetState))
+
+        mediaViewController.squishViewState(mockViewState, 0.5f)
+        verify(mockViewState, times(1)).copy()
+        verifyNoMoreInteractions(mockViewState)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
index 06d45de..a074475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
@@ -27,6 +27,7 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -136,6 +137,24 @@
     }
 
     @Test
+    fun testConnection_calledTwice_oldBrowserDisconnected() {
+        val oldBrowser = mock<MediaBrowser>()
+        whenever(browserFactory.create(any(), any(), any())).thenReturn(oldBrowser)
+
+        // When testConnection can connect to the service
+        setupBrowserConnection()
+        resumeBrowser.testConnection()
+
+        // And testConnection is called again
+        val newBrowser = mock<MediaBrowser>()
+        whenever(browserFactory.create(any(), any(), any())).thenReturn(newBrowser)
+        resumeBrowser.testConnection()
+
+        // Then we disconnect the old browser
+        verify(oldBrowser).disconnect()
+    }
+
+    @Test
     fun testFindRecentMedia_connectionFails_error() {
         // When findRecentMedia is called and we cannot connect
         setupBrowserFailed()
@@ -169,6 +188,24 @@
     }
 
     @Test
+    fun testFindRecentMedia_calledTwice_oldBrowserDisconnected() {
+        val oldBrowser = mock<MediaBrowser>()
+        whenever(browserFactory.create(any(), any(), any())).thenReturn(oldBrowser)
+
+        // When findRecentMedia is called and we connect
+        setupBrowserConnection()
+        resumeBrowser.findRecentMedia()
+
+        // And findRecentMedia is called again
+        val newBrowser = mock<MediaBrowser>()
+        whenever(browserFactory.create(any(), any(), any())).thenReturn(newBrowser)
+        resumeBrowser.findRecentMedia()
+
+        // Then we disconnect the old browser
+        verify(oldBrowser).disconnect()
+    }
+
+    @Test
     fun testFindRecentMedia_noChildren_error() {
         // When findRecentMedia is called and we connect, but do not get any results
         setupBrowserConnectionNoResults()
@@ -223,6 +260,24 @@
         verify(transportControls).play()
     }
 
+    @Test
+    fun testRestart_calledTwice_oldBrowserDisconnected() {
+        val oldBrowser = mock<MediaBrowser>()
+        whenever(browserFactory.create(any(), any(), any())).thenReturn(oldBrowser)
+
+        // When restart is called and we connect successfully
+        setupBrowserConnection()
+        resumeBrowser.restart()
+
+        // And restart is called again
+        val newBrowser = mock<MediaBrowser>()
+        whenever(browserFactory.create(any(), any(), any())).thenReturn(newBrowser)
+        resumeBrowser.restart()
+
+        // Then we disconnect the old browser
+        verify(oldBrowser).disconnect()
+    }
+
     /**
      * Helper function to mock a failed connection
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 7c30398..a526087 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -60,6 +60,7 @@
 import android.view.DisplayInfo;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
@@ -164,7 +165,7 @@
     @Mock
     private UiEventLogger mUiEventLogger;
     @Mock
-    EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
+    private ViewTreeObserver mViewTreeObserver;
     @Mock
     EdgeBackGestureHandler mEdgeBackGestureHandler;
     NavBarHelper mNavBarHelper;
@@ -199,8 +200,6 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        when(mEdgeBackGestureHandlerFactory.create(any(Context.class)))
-                .thenReturn(mEdgeBackGestureHandler);
         when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController);
         when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController);
         when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton);
@@ -213,6 +212,7 @@
         when(mNavigationBarTransitions.getLightTransitionsController())
                 .thenReturn(mLightBarTransitionsController);
         when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
+        when(mNavigationBarView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
         when(mUserContextProvider.createCurrentUserContext(any(Context.class)))
                 .thenReturn(mContext);
         setupSysuiDependency();
@@ -222,8 +222,6 @@
         mDependency.injectMockDependency(KeyguardStateController.class);
         mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
         mDependency.injectMockDependency(NavigationBarController.class);
-        mDependency.injectTestDependency(EdgeBackGestureHandler.Factory.class,
-                mEdgeBackGestureHandlerFactory);
         mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
         mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
         TestableLooper.get(this).runWithLooper(() -> {
@@ -463,6 +461,7 @@
                 mDeadZone,
                 mDeviceConfigProxyFake,
                 mNavigationBarTransitions,
+                mEdgeBackGestureHandler,
                 Optional.of(mock(BackAnimation.class)),
                 mUserContextProvider));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index c88ceac..4f6475f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -90,6 +90,7 @@
 
     private static final String CARD_ID = "card_id";
     private static final String LABEL = "QAW";
+    private static final String CARD_DESCRIPTION = "•••• 1234";
     private static final Icon CARD_IMAGE =
             Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888));
 
@@ -282,9 +283,7 @@
         mTile.handleUpdateState(state, null);
 
         assertEquals(Tile.STATE_ACTIVE, state.state);
-        assertEquals(
-                "•••• 1234",
-                state.secondaryLabel);
+        assertEquals(CARD_DESCRIPTION, state.secondaryLabel);
         assertNotNull(state.stateDescription);
         assertNotNull(state.sideViewCustomDrawable);
     }
@@ -298,11 +297,9 @@
         mTile.handleUpdateState(state, null);
 
         assertEquals(Tile.STATE_INACTIVE, state.state);
-        assertEquals(
-                mContext.getString(R.string.wallet_secondary_label_device_locked),
-                state.secondaryLabel);
+        assertEquals(CARD_DESCRIPTION, state.secondaryLabel);
         assertNotNull(state.stateDescription);
-        assertNull(state.sideViewCustomDrawable);
+        assertNotNull(state.sideViewCustomDrawable);
     }
 
     @Test
@@ -314,9 +311,7 @@
         mTile.handleUpdateState(state, null);
 
         assertEquals(Tile.STATE_ACTIVE, state.state);
-        assertEquals(
-                "•••• 1234",
-                state.secondaryLabel);
+        assertEquals(CARD_DESCRIPTION, state.secondaryLabel);
         assertNotNull(state.stateDescription);
         assertNotNull(state.sideViewCustomDrawable);
     }
@@ -426,6 +421,6 @@
     private WalletCard createWalletCard(Context context) {
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
-        return new WalletCard.Builder(CARD_ID, CARD_IMAGE, "•••• 1234", pendingIntent).build();
+        return new WalletCard.Builder(CARD_ID, CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index 540d291..1cce3b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -21,10 +21,13 @@
 import android.testing.AndroidTestingRunner
 
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.Utils
+import com.android.systemui.util.mockito.any
 
 import org.junit.After
 import org.junit.Assert.assertFalse
@@ -32,28 +35,33 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
     var manager: NotificationSectionsFeatureManager? = null
     val proxyFake = DeviceConfigProxyFake()
-    var originalQsMediaPlayer: Int = 0
+    private lateinit var staticMockSession: MockitoSession
 
     @Before
     public fun setup() {
         manager = NotificationSectionsFeatureManager(proxyFake, mContext)
         manager!!.clearCache()
-        originalQsMediaPlayer = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1)
+        staticMockSession = ExtendedMockito.mockitoSession()
+            .mockStatic<Utils>(Utils::class.java)
+            .strictness(Strictness.LENIENT)
+            .startMocking()
+        `when`(Utils.useQsMediaPlayer(any())).thenReturn(false)
         Settings.Global.putInt(context.getContentResolver(),
                 Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 0)
     }
 
     @After
     public fun teardown() {
-        Settings.Global.putInt(context.getContentResolver(),
-                Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, originalQsMediaPlayer)
+        staticMockSession.finishMocking()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index dc101f3..3a85972 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -233,7 +233,7 @@
 
     @Test
     public void hideSilentNotificationsPerUserSettingWithHighPriorityParent() {
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
         mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
         mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
         GroupEntry parent = new GroupEntryBuilder()
@@ -255,7 +255,7 @@
 
     @Test
     public void hideSilentNotificationsPerUserSetting() {
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
         mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
         mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
         mEntry = new NotificationEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 6d3a5fe..ec20271 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -336,7 +336,7 @@
         // WHEN the position algorithm is run
         positionClock();
         // THEN the padding DOESN'T adjust for keyguard status height.
-        assertThat(mClockPositionAlgorithm.getMinStackScrollerPadding())
+        assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding())
                 .isEqualTo(mKeyguardStatusBarHeaderHeight);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 94e6b9a..11f96ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -610,13 +610,13 @@
         when(mLockIconViewController.getTop()).thenReturn(80f);
         when(mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap)).thenReturn(5);
 
-        // Available space (100 - 10 - 15 = 75)
+        // Available space (100 - 0 - 15 = 85)
         when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(100);
-        when(mNotificationStackScrollLayoutController.getTopPadding()).thenReturn(10);
+        when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
         mNotificationPanelViewController.updateResources();
 
         assertThat(mNotificationPanelViewController.getSpaceForLockscreenNotifications())
-                .isEqualTo(75);
+                .isEqualTo(85);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index 26199d53..c402d2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -309,4 +310,17 @@
         });
         verify(mWindowManager).updateViewLayout(any(), any());
     }
+
+    @Test
+    public void bouncerShowing_OrientationNoSensor() {
+        mNotificationShadeWindowController.setKeyguardShowing(true);
+        mNotificationShadeWindowController.setKeyguardOccluded(true);
+        mNotificationShadeWindowController.setBouncerShowing(true);
+        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
+        mNotificationShadeWindowController.onConfigChanged(new Configuration());
+
+        verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
+        assertThat(mLayoutParameters.getValue().screenOrientation)
+                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 649328d..9b29bae 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -242,6 +242,9 @@
 
         int getCurrentUserIdLocked();
 
+        Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+                int windowId);
+
         boolean isAccessibilityButtonShown();
 
         /**
@@ -551,8 +554,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -576,11 +577,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -628,8 +629,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float [] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -653,11 +652,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -706,8 +705,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -731,11 +728,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -786,8 +783,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
                 return null;
@@ -811,11 +806,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -865,8 +860,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
                 return null;
@@ -889,12 +882,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -1672,21 +1664,10 @@
         mInvocationHandler.startInputLocked(connection, editorInfo, restarting);
     }
 
-
-
     @Nullable
-    Pair<float[], MagnificationSpec> getTransformMatrixAndSpecLocked(int resolvedWindowId) {
-        final WindowInfo windowInfo =
-                mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId);
-        if (windowInfo == null) {
-            Slog.w(LOG_TAG, "getTransformMatrixAndSpec, windowInfo is null window id = "
-                    + resolvedWindowId);
-            return new Pair<>(null, null);
-        }
-
-        final MagnificationSpec spec = new MagnificationSpec();
-        spec.setTo(windowInfo.mMagnificationSpec);
-        return new Pair<>(windowInfo.mTransformMatrix, spec);
+    private Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+            int resolvedWindowId) {
+        return mSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4806514..99c8495 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -68,6 +68,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -99,6 +100,7 @@
 import android.text.TextUtils.SimpleStringSplitter;
 import android.util.ArraySet;
 import android.util.IntArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -458,6 +460,41 @@
     }
 
     @Override
+    public Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+            int windowId) {
+        WindowInfo windowInfo;
+        synchronized (mLock) {
+            windowInfo = mA11yWindowManager.findWindowInfoByIdLocked(windowId);
+        }
+        if (windowInfo != null) {
+            final MagnificationSpec spec = new MagnificationSpec();
+            spec.setTo(windowInfo.mMagnificationSpec);
+            return new Pair<>(windowInfo.mTransformMatrix, spec);
+        } else {
+            // If the framework doesn't track windows, we fall back to get the pair of
+            // transformation matrix and MagnificationSpe from the WindowManagerService's
+            // WindowState.
+            IBinder token;
+            synchronized (mLock) {
+                token = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(mCurrentUserId,
+                        windowId);
+            }
+            Pair<Matrix, MagnificationSpec> pair =
+                    mWindowManagerService.getWindowTransformationMatrixAndMagnificationSpec(token);
+            final float[] outTransformationMatrix = new float[9];
+            final Matrix tmpMatrix = pair.first;
+            final MagnificationSpec spec = pair.second;
+            if (!spec.isNop()) {
+                tmpMatrix.postScale(spec.scale, spec.scale);
+                tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
+            }
+            tmpMatrix.getValues(outTransformationMatrix);
+
+            return new Pair<>(outTransformationMatrix, pair.second);
+        }
+    }
+
+    @Override
     public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
         mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
                 userState.mBoundServices);
@@ -3750,12 +3787,12 @@
                         boundsInScreenBeforeMagnification.centerY());
 
                 // Invert magnification if needed.
-                final WindowInfo windowInfo = mA11yWindowManager.findWindowInfoByIdLocked(
-                        focus.getWindowId());
+                final Pair<float[], MagnificationSpec> pair =
+                        getWindowTransformationMatrixAndMagnificationSpec(focus.getWindowId());
                 MagnificationSpec spec = null;
-                if (windowInfo != null) {
+                if (pair != null && pair.second != null) {
                     spec = new MagnificationSpec();
-                    spec.setTo(windowInfo.mMagnificationSpec);
+                    spec.setTo(pair.second);
                 }
 
                 if (spec != null && !spec.isNop()) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f18d13d..6d3620f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1559,9 +1559,18 @@
                 Slog.e(TAG, "Error sending input show up notification", e);
             }
         }
+    }
+
+    // AutoFillUiCallback
+    @Override
+    public void requestFallbackFromFillDialog() {
+        setFillDialogDisabled();
         synchronized (mLock) {
-            // stop to show fill dialog
-            mSessionFlags.mFillDialogDisabled = true;
+            if (mCurrentViewId == null) {
+                return;
+            }
+            final ViewState currentView = mViewStates.get(mCurrentViewId);
+            currentView.maybeCallOnFillReady(mFlags);
         }
     }
 
@@ -3208,16 +3217,24 @@
             return;
         }
 
-        if (requestShowFillDialog(response, filledId, filterText, flags)) {
-            synchronized (mLock) {
-                final ViewState currentView = mViewStates.get(mCurrentViewId);
-                currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
-                mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG);
+        final AutofillId[] ids = response.getFillDialogTriggerIds();
+        if (ids != null && ArrayUtils.contains(ids, filledId)) {
+            if (requestShowFillDialog(response, filledId, filterText, flags)) {
+                synchronized (mLock) {
+                    final ViewState currentView = mViewStates.get(mCurrentViewId);
+                    currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
+                    mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG);
+                }
+                // Just show fill dialog once, so disabled after shown.
+                // Note: Cannot disable before requestShowFillDialog() because the method
+                //       need to check whether fill dialog enabled.
+                setFillDialogDisabled();
+                return;
+            } else {
+                setFillDialogDisabled();
             }
-            return;
-        }
 
-        setFillDialogDisabled();
+        }
 
         if (response.supportsInlineSuggestions()) {
             synchronized (mLock) {
@@ -3324,15 +3341,11 @@
             return false;
         }
 
-        final AutofillId[] ids = response.getFillDialogTriggerIds();
-        if (ids == null || !ArrayUtils.contains(ids, filledId)) {
-            return false;
-        }
-
         final Drawable serviceIcon = getServiceIcon();
 
         getUiForShowing().showFillDialog(filledId, response, filterText,
-                mService.getServicePackageName(), mComponentName, serviceIcon, this);
+                mService.getServicePackageName(), mComponentName, serviceIcon, this,
+                id, mCompatMode);
         return true;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 57768ef..3ab873d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -97,6 +97,7 @@
         void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent);
         void cancelSession();
         void requestShowSoftInput(AutofillId id);
+        void requestFallbackFromFillDialog();
     }
 
     public AutoFillUI(@NonNull Context context) {
@@ -388,13 +389,19 @@
     public void showFillDialog(@NonNull AutofillId focusedId, @NonNull FillResponse response,
             @Nullable String filterText, @Nullable String servicePackageName,
             @NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
-            @NonNull AutoFillUiCallback callback) {
+            @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode) {
         if (sVerbose) {
             Slog.v(TAG, "showFillDialog for "
                     + componentName.toShortString() + ": " + response);
         }
 
-        // TODO: enable LogMaker
+        final LogMaker log = Helper
+                .newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, componentName, servicePackageName,
+                        sessionId, compatMode)
+                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
+                        filterText == null ? 0 : filterText.length())
+                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+                        response.getDatasets() == null ? 0 : response.getDatasets().size());
 
         mHandler.post(() -> {
             if (callback != mCallback) {
@@ -406,6 +413,7 @@
                     mUiModeMgr.isNightMode(), new DialogFillUi.UiCallback() {
                         @Override
                         public void onResponsePicked(FillResponse response) {
+                            log(MetricsEvent.TYPE_DETAIL);
                             hideFillDialogUiThread(callback);
                             if (mCallback != null) {
                                 mCallback.authenticate(response.getRequestId(),
@@ -417,6 +425,7 @@
 
                         @Override
                         public void onDatasetPicked(Dataset dataset) {
+                            log(MetricsEvent.TYPE_ACTION);
                             hideFillDialogUiThread(callback);
                             if (mCallback != null) {
                                 final int datasetIndex = response.getDatasets().indexOf(dataset);
@@ -426,15 +435,29 @@
                         }
 
                         @Override
-                        public void onCanceled() {
+                        public void onDismissed() {
+                            log(MetricsEvent.TYPE_DISMISS);
                             hideFillDialogUiThread(callback);
                             callback.requestShowSoftInput(focusedId);
                         }
 
                         @Override
+                        public void onCanceled() {
+                            log(MetricsEvent.TYPE_CLOSE);
+                            hideFillDialogUiThread(callback);
+                            callback.requestShowSoftInput(focusedId);
+                            callback.requestFallbackFromFillDialog();
+                        }
+
+                        @Override
                         public void startIntentSender(IntentSender intentSender) {
                             mCallback.startIntentSenderAndFinishSession(intentSender);
                         }
+
+                        private void log(int type) {
+                            log.setType(type);
+                            mMetricsLogger.write(log);
+                        }
                     });
         });
     }
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index f9f5289..5a1a1ae 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -81,6 +81,7 @@
     interface UiCallback {
         void onResponsePicked(@NonNull FillResponse response);
         void onDatasetPicked(@NonNull Dataset dataset);
+        void onDismissed();
         void onCanceled();
         void startIntentSender(IntentSender intentSender);
     }
@@ -144,6 +145,7 @@
         mDialog = new Dialog(mContext, mThemeId);
         mDialog.setContentView(decor);
         setDialogParamsAsBottomSheet();
+        mDialog.setOnCancelListener((d) -> mCallback.onCanceled());
 
         show();
     }
@@ -220,7 +222,7 @@
         final TextView noButton = decor.findViewById(R.id.autofill_dialog_no);
         // set "No thinks" by default
         noButton.setText(R.string.autofill_save_no);
-        noButton.setOnClickListener((v) -> mCallback.onCanceled());
+        noButton.setOnClickListener((v) -> mCallback.onDismissed());
     }
 
     private void setContinueButton(View decor, View.OnClickListener listener) {
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 9d4b50b..80182d2 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -22,6 +22,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
 import android.hardware.input.VirtualKeyEvent;
@@ -29,11 +30,13 @@
 import android.hardware.input.VirtualMouseRelativeEvent;
 import android.hardware.input.VirtualMouseScrollEvent;
 import android.hardware.input.VirtualTouchEvent;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.Display;
+import android.view.InputDevice;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -44,7 +47,11 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
 
 /** Controls virtual input devices, including device lifecycle and event dispatch. */
 class InputController {
@@ -72,20 +79,27 @@
     @GuardedBy("mLock")
     final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>();
 
+    private final Handler mHandler;
     private final NativeWrapper mNativeWrapper;
     private final DisplayManagerInternal mDisplayManagerInternal;
     private final InputManagerInternal mInputManagerInternal;
+    private final DeviceCreationThreadVerifier mThreadVerifier;
 
-    InputController(@NonNull Object lock) {
-        this(lock, new NativeWrapper());
+    InputController(@NonNull Object lock, @NonNull Handler handler) {
+        this(lock, new NativeWrapper(), handler,
+                // Verify that virtual devices are not created on the handler thread.
+                () -> !handler.getLooper().isCurrentThread());
     }
 
     @VisibleForTesting
-    InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) {
+    InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper,
+            @NonNull Handler handler, @NonNull DeviceCreationThreadVerifier threadVerifier) {
         mLock = lock;
+        mHandler = handler;
         mNativeWrapper = nativeWrapper;
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+        mThreadVerifier = threadVerifier;
     }
 
     void close() {
@@ -108,23 +122,13 @@
             @NonNull IBinder deviceToken,
             int displayId) {
         final String phys = createPhys(PHYS_TYPE_KEYBOARD);
-        setUniqueIdAssociation(displayId, phys);
-        final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys);
-        if (fd < 0) {
-            throw new RuntimeException(
-                    "A native error occurred when creating keyboard: " + -fd);
-        }
-        final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
-        synchronized (mLock) {
-            mInputDeviceDescriptors.put(deviceToken,
-                    new InputDeviceDescriptor(fd, binderDeathRecipient,
-                            InputDeviceDescriptor.TYPE_KEYBOARD, displayId, phys));
-        }
         try {
-            deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
-        } catch (RemoteException e) {
-            // TODO(b/215608394): remove and close InputDeviceDescriptor
-            throw new RuntimeException("Could not create virtual keyboard", e);
+            createDeviceInternal(InputDeviceDescriptor.TYPE_KEYBOARD, deviceName, vendorId,
+                    productId, deviceToken, displayId, phys,
+                    () -> mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys));
+        } catch (DeviceCreationException e) {
+            throw new RuntimeException(
+                    "Failed to create virtual keyboard device '" + deviceName + "'.", e);
         }
     }
 
@@ -134,25 +138,15 @@
             @NonNull IBinder deviceToken,
             int displayId) {
         final String phys = createPhys(PHYS_TYPE_MOUSE);
-        setUniqueIdAssociation(displayId, phys);
-        final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys);
-        if (fd < 0) {
-            throw new RuntimeException(
-                    "A native error occurred when creating mouse: " + -fd);
-        }
-        final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
-        synchronized (mLock) {
-            mInputDeviceDescriptors.put(deviceToken,
-                    new InputDeviceDescriptor(fd, binderDeathRecipient,
-                            InputDeviceDescriptor.TYPE_MOUSE, displayId, phys));
-            mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
-        }
         try {
-            deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
-        } catch (RemoteException e) {
-            // TODO(b/215608394): remove and close InputDeviceDescriptor
-            throw new RuntimeException("Could not create virtual mouse", e);
+            createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId,
+                    deviceToken, displayId, phys,
+                    () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys));
+        } catch (DeviceCreationException e) {
+            throw new RuntimeException(
+                    "Failed to create virtual mouse device: '" + deviceName + "'.", e);
         }
+        mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
     }
 
     void createTouchscreen(@NonNull String deviceName,
@@ -162,24 +156,14 @@
             int displayId,
             @NonNull Point screenSize) {
         final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN);
-        setUniqueIdAssociation(displayId, phys);
-        final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys,
-                screenSize.y, screenSize.x);
-        if (fd < 0) {
-            throw new RuntimeException(
-                    "A native error occurred when creating touchscreen: " + -fd);
-        }
-        final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
-        synchronized (mLock) {
-            mInputDeviceDescriptors.put(deviceToken,
-                    new InputDeviceDescriptor(fd, binderDeathRecipient,
-                            InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId, phys));
-        }
         try {
-            deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
-        } catch (RemoteException e) {
-            // TODO(b/215608394): remove and close InputDeviceDescriptor
-            throw new RuntimeException("Could not create virtual touchscreen", e);
+            createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId,
+                    productId, deviceToken, displayId, phys,
+                    () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
+                            phys, screenSize.y, screenSize.x));
+        } catch (DeviceCreationException e) {
+            throw new RuntimeException(
+                    "Failed to create virtual touchscreen device '" + deviceName + "'.", e);
         }
     }
 
@@ -510,4 +494,133 @@
             unregisterInputDevice(mDeviceToken);
         }
     }
+
+    /** A helper class used to wait for an input device to be registered. */
+    private class WaitForDevice implements AutoCloseable {
+        private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1);
+        private final InputManager.InputDeviceListener mListener;
+
+        WaitForDevice(String deviceName, int vendorId, int productId) {
+            mListener = new InputManager.InputDeviceListener() {
+                @Override
+                public void onInputDeviceAdded(int deviceId) {
+                    final InputDevice device = InputManager.getInstance().getInputDevice(
+                            deviceId);
+                    Objects.requireNonNull(device, "Newly added input device was null.");
+                    if (!device.getName().equals(deviceName)) {
+                        return;
+                    }
+                    final InputDeviceIdentifier id = device.getIdentifier();
+                    if (id.getVendorId() != vendorId || id.getProductId() != productId) {
+                        return;
+                    }
+                    mDeviceAddedLatch.countDown();
+                }
+
+                @Override
+                public void onInputDeviceRemoved(int deviceId) {
+
+                }
+
+                @Override
+                public void onInputDeviceChanged(int deviceId) {
+
+                }
+            };
+            InputManager.getInstance().registerInputDeviceListener(mListener, mHandler);
+        }
+
+        /** Note: This must not be called from {@link #mHandler}'s thread. */
+        void waitForDeviceCreation() throws DeviceCreationException {
+            try {
+                if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) {
+                    throw new DeviceCreationException(
+                            "Timed out waiting for virtual device to be created.");
+                }
+            } catch (InterruptedException e) {
+                throw new DeviceCreationException(
+                        "Interrupted while waiting for virtual device to be created.", e);
+            }
+        }
+
+        @Override
+        public void close() {
+            InputManager.getInstance().unregisterInputDeviceListener(mListener);
+        }
+    }
+
+    /** An internal exception that is thrown to indicate an error when opening a virtual device. */
+    private static class DeviceCreationException extends Exception {
+        DeviceCreationException(String message) {
+            super(message);
+        }
+        DeviceCreationException(String message, Exception cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * Creates a virtual input device synchronously, and waits for the notification that the device
+     * was added.
+     *
+     * Note: Input device creation is expected to happen on a binder thread, and the calling thread
+     * will be blocked until the input device creation is successful. This should not be called on
+     * the handler's thread.
+     *
+     * @throws DeviceCreationException Throws this exception if anything unexpected happens in the
+     *                                 process of creating the device. This method will take care
+     *                                 to restore the state of the system in the event of any
+     *                                 unexpected behavior.
+     */
+    private void createDeviceInternal(@InputDeviceDescriptor.Type int type, String deviceName,
+            int vendorId, int productId, IBinder deviceToken, int displayId, String phys,
+            Supplier<Integer> deviceOpener)
+            throws DeviceCreationException {
+        if (!mThreadVerifier.isValidThread()) {
+            throw new IllegalStateException(
+                    "Virtual device creation should happen on an auxiliary thread (e.g. binder "
+                            + "thread) and not from the handler's thread.");
+        }
+
+        final int fd;
+        final BinderDeathRecipient binderDeathRecipient;
+
+        setUniqueIdAssociation(displayId, phys);
+        try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) {
+            fd = deviceOpener.get();
+            if (fd < 0) {
+                throw new DeviceCreationException(
+                        "A native error occurred when creating touchscreen: " + -fd);
+            }
+            // The fd is valid from here, so ensure that all failures close the fd after this point.
+            try {
+                waiter.waitForDeviceCreation();
+
+                binderDeathRecipient = new BinderDeathRecipient(deviceToken);
+                try {
+                    deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
+                } catch (RemoteException e) {
+                    throw new DeviceCreationException(
+                            "Client died before virtual device could be created.", e);
+                }
+            } catch (DeviceCreationException e) {
+                mNativeWrapper.closeUinput(fd);
+                throw e;
+            }
+        } catch (DeviceCreationException e) {
+            InputManager.getInstance().removeUniqueIdAssociation(phys);
+            throw e;
+        }
+
+        synchronized (mLock) {
+            mInputDeviceDescriptors.put(deviceToken,
+                    new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys));
+        }
+    }
+
+    @VisibleForTesting
+    interface DeviceCreationThreadVerifier {
+        /** Returns true if the calling thread is a valid thread for device creation. */
+        boolean isValidThread();
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index de14ef6..9802b97 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -166,7 +166,8 @@
         mAppToken = token;
         mParams = params;
         if (inputController == null) {
-            mInputController = new InputController(mVirtualDeviceLock);
+            mInputController = new InputController(
+                    mVirtualDeviceLock, context.getMainThreadHandler());
         } else {
             mInputController = inputController;
         }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8f37823..bc40170 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3055,19 +3055,7 @@
             return true;
         }
 
-        if (packageName == null) {
-            return false;
-        }
-
-        final int packageUid = mPmInternal.getPackageUid(packageName,
-                PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid));
-
-        if (DEBUG_OBB) {
-            Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
-                    packageUid + ", callerUid = " + callerUid);
-        }
-
-        return callerUid == packageUid;
+        return mPmInternal.isSameApp(packageName, callerUid, UserHandle.getUserId(callerUid));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 6e28d8f..5a234f5 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -56,7 +56,6 @@
 import android.os.BatteryStatsInternal;
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
-import android.os.Build;
 import android.os.PowerExemptionManager;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.SystemClock;
@@ -97,7 +96,7 @@
     static final boolean DEBUG_BACKGROUND_BATTERY_TRACKER = false;
 
     static final boolean DEBUG_BACKGROUND_BATTERY_TRACKER_VERBOSE =
-            DEBUG_BACKGROUND_BATTERY_TRACKER | Build.IS_DEBUGGABLE;
+            DEBUG_BACKGROUND_BATTERY_TRACKER | false;
 
     // As we don't support realtime per-UID battery usage stats yet, we're polling the stats
     // in a regular time basis.
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index fc73a59..cceacd8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2792,6 +2792,15 @@
         }
 
         int N = procs.size();
+        for (int i = 0; i < N; ++i) {
+            final ProcessRecord proc = procs.get(i).first;
+            try {
+                Process.setProcessFrozen(proc.getPid(), proc.uid, true);
+            } catch (Exception e) {
+                Slog.w(TAG, "Unable to freeze " + proc.getPid() + " " + proc.processName);
+            }
+        }
+
         for (int i=0; i<N; i++) {
             final Pair<ProcessRecord, Boolean> proc = procs.get(i);
             removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3e97b91..36afb36 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4545,8 +4545,9 @@
      * @return The restriction matching the package
      */
     private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
-        return new RestrictionBypass(pkg.isPrivileged(), mContext.checkPermission(
-                android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
+        return new RestrictionBypass(pkg.getUid() == Process.SYSTEM_UID, pkg.isPrivileged(),
+                mContext.checkPermission(android.Manifest.permission
+                        .EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
                 == PackageManager.PERMISSION_GRANTED);
     }
 
@@ -4853,6 +4854,9 @@
                 if (opBypass != null) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
+                        if (opBypass.isSystemUid && appBypass != null && appBypass.isSystemUid) {
+                            return false;
+                        }
                         if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
                             return false;
                         }
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 122a950..d8aa9aa 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -486,8 +486,7 @@
 
         for (SADeviceState deviceState : mSADevices) {
             if (deviceType == deviceState.mDeviceType
-                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
-                    || !wireless) {
+                    && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
                 isInList = true;
                 if (forceEnable) {
                     deviceState.mEnabled = true;
@@ -511,8 +510,7 @@
 
         for (SADeviceState deviceState : mSADevices) {
             if (deviceType == deviceState.mDeviceType
-                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
-                    || !wireless) {
+                    && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
                 deviceState.mEnabled = false;
                 break;
             }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 8ab0b93..e4e9d1d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -164,6 +164,7 @@
     private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
     private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6;
+    private static final int MSG_POINTER_DISPLAY_ID_CHANGED = 7;
 
     private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
 
@@ -276,11 +277,24 @@
     @GuardedBy("mAssociationLock")
     private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
 
+    // Guards per-display input properties and properties relating to the mouse pointer.
+    // Threads can wait on this lock to be notified the next time the display on which the mouse
+    // pointer is shown has changed.
     private final Object mAdditionalDisplayInputPropertiesLock = new Object();
 
-    // Forces the MouseCursorController to target a specific display id.
+    // Forces the PointerController to target a specific display id.
     @GuardedBy("mAdditionalDisplayInputPropertiesLock")
     private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
+
+    // PointerController is the source of truth of the pointer display. This is the value of the
+    // latest pointer display id reported by PointerController.
+    @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+    private int mAcknowledgedPointerDisplayId = Display.INVALID_DISPLAY;
+    // This is the latest display id that IMS has requested PointerController to use. If there are
+    // no devices that can control the pointer, PointerController may end up disregarding this
+    // value.
+    @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+    private int mRequestedPointerDisplayId = Display.INVALID_DISPLAY;
     @GuardedBy("mAdditionalDisplayInputPropertiesLock")
     private final SparseArray<AdditionalDisplayInputProperties> mAdditionalDisplayInputProperties =
             new SparseArray<>();
@@ -289,7 +303,6 @@
     @GuardedBy("mAdditionalDisplayInputPropertiesLock")
     private PointerIcon mIcon;
 
-
     // Holds all the registered gesture monitors that are implemented as spy windows. The spy
     // windows are mapped by their InputChannel tokens.
     @GuardedBy("mInputMonitors")
@@ -383,6 +396,10 @@
         NativeInputManagerService getNativeService(InputManagerService service) {
             return new NativeInputManagerService.NativeImpl(service, mContext, mLooper.getQueue());
         }
+
+        void registerLocalService(InputManagerInternal localService) {
+            LocalServices.addService(InputManagerInternal.class, localService);
+        }
     }
 
     public InputManagerService(Context context) {
@@ -391,11 +408,14 @@
 
     @VisibleForTesting
     InputManagerService(Injector injector) {
+        // The static association map is accessed by both java and native code, so it must be
+        // initialized before initializing the native service.
+        mStaticAssociations = loadStaticInputPortAssociations();
+
         mContext = injector.getContext();
         mHandler = new InputManagerHandler(injector.getLooper());
         mNative = injector.getNativeService(this);
 
-        mStaticAssociations = loadStaticInputPortAssociations();
         mUseDevInputEventForAudioJack =
                 mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
         Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
@@ -406,7 +426,7 @@
         mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
             new File(doubleTouchGestureEnablePath);
 
-        LocalServices.addService(InputManagerInternal.class, new LocalService());
+        injector.registerLocalService(new LocalService());
     }
 
     public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
@@ -556,6 +576,8 @@
                 vArray[i] = viewports.get(i);
             }
             mNative.setDisplayViewports(vArray);
+            // Always attempt to update the pointer display when viewports change.
+            updatePointerDisplayId();
 
             if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
                 final AdditionalDisplayInputProperties properties =
@@ -1690,6 +1712,13 @@
             mPointerIconDisplayContext = null;
         }
 
+        synchronized (mAdditionalDisplayInputPropertiesLock) {
+            setPointerIconVisible(AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE,
+                    displayId);
+            setPointerAcceleration(AdditionalDisplayInputProperties.DEFAULT_POINTER_ACCELERATION,
+                    displayId);
+        }
+
         mNative.displayRemoved(displayId);
     }
 
@@ -1961,10 +1990,43 @@
         return result;
     }
 
-    private void setVirtualMousePointerDisplayId(int displayId) {
+    /**
+     * Update the display on which the mouse pointer is shown.
+     * If there is an overridden display for the mouse pointer, use that. Otherwise, query
+     * WindowManager for the pointer display.
+     *
+     * @return true if the pointer displayId changed, false otherwise.
+     */
+    private boolean updatePointerDisplayId() {
+        synchronized (mAdditionalDisplayInputPropertiesLock) {
+            final int pointerDisplayId = mOverriddenPointerDisplayId != Display.INVALID_DISPLAY
+                    ? mOverriddenPointerDisplayId : mWindowManagerCallbacks.getPointerDisplayId();
+            if (mRequestedPointerDisplayId == pointerDisplayId) {
+                return false;
+            }
+            mRequestedPointerDisplayId = pointerDisplayId;
+            mNative.setPointerDisplayId(pointerDisplayId);
+            return true;
+        }
+    }
+
+    private void handlePointerDisplayIdChanged(PointerDisplayIdChangedArgs args) {
+        synchronized (mAdditionalDisplayInputPropertiesLock) {
+            mAcknowledgedPointerDisplayId = args.mPointerDisplayId;
+            // Notify waiting threads that the display of the mouse pointer has changed.
+            mAdditionalDisplayInputPropertiesLock.notifyAll();
+        }
+        mWindowManagerCallbacks.notifyPointerDisplayIdChanged(
+                args.mPointerDisplayId, args.mXPosition, args.mYPosition);
+    }
+
+    private boolean setVirtualMousePointerDisplayIdBlocking(int displayId) {
+        // Indicates whether this request is for removing the override.
+        final boolean removingOverride = displayId == Display.INVALID_DISPLAY;
+
         synchronized (mAdditionalDisplayInputPropertiesLock) {
             mOverriddenPointerDisplayId = displayId;
-            if (displayId != Display.INVALID_DISPLAY) {
+            if (!removingOverride) {
                 final AdditionalDisplayInputProperties properties =
                         mAdditionalDisplayInputProperties.get(displayId);
                 if (properties != null) {
@@ -1972,9 +2034,30 @@
                     updatePointerIconVisibleLocked(properties.pointerIconVisible);
                 }
             }
+            if (!updatePointerDisplayId() && mAcknowledgedPointerDisplayId == displayId) {
+                // The requested pointer display is already set.
+                return true;
+            }
+            if (removingOverride && mAcknowledgedPointerDisplayId == Display.INVALID_DISPLAY) {
+                // The pointer display override is being removed, but the current pointer display
+                // is already invalid. This can happen when the PointerController is destroyed as a
+                // result of the removal of all input devices that can control the pointer.
+                return true;
+            }
+            try {
+                // The pointer display changed, so wait until the change has propagated.
+                mAdditionalDisplayInputPropertiesLock.wait(5_000 /*mills*/);
+            } catch (InterruptedException ignored) {
+            }
+            // This request succeeds in two cases:
+            // - This request was to remove the override, in which case the new pointer display
+            //   could be anything that WM has set.
+            // - We are setting a new override, in which case the request only succeeds if the
+            //   reported new displayId is the one we requested. This check ensures that if two
+            //   competing overrides are requested in succession, the caller can be notified if one
+            //   of them fails.
+            return  removingOverride || mAcknowledgedPointerDisplayId == displayId;
         }
-        // TODO(b/215597605): trigger MousePositionTracker update
-        mNative.notifyPointerDisplayIdChanged();
     }
 
     private int getVirtualMousePointerDisplayId() {
@@ -3156,18 +3239,6 @@
 
     // Native callback.
     @SuppressWarnings("unused")
-    private int getPointerDisplayId() {
-        synchronized (mAdditionalDisplayInputPropertiesLock) {
-            // Prefer the override to all other displays.
-            if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
-                return mOverriddenPointerDisplayId;
-            }
-        }
-        return mWindowManagerCallbacks.getPointerDisplayId();
-    }
-
-    // Native callback.
-    @SuppressWarnings("unused")
     private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
         if (!mSystemReady) {
             return null;
@@ -3206,6 +3277,26 @@
         return null;
     }
 
+    private static class PointerDisplayIdChangedArgs {
+        final int mPointerDisplayId;
+        final float mXPosition;
+        final float mYPosition;
+        PointerDisplayIdChangedArgs(int pointerDisplayId, float xPosition, float yPosition) {
+            mPointerDisplayId = pointerDisplayId;
+            mXPosition = xPosition;
+            mYPosition = yPosition;
+        }
+    }
+
+    // Native callback.
+    @SuppressWarnings("unused")
+    @VisibleForTesting
+    void onPointerDisplayIdChanged(int pointerDisplayId, float xPosition, float yPosition) {
+        mHandler.obtainMessage(MSG_POINTER_DISPLAY_ID_CHANGED,
+                new PointerDisplayIdChangedArgs(pointerDisplayId, xPosition,
+                        yPosition)).sendToTarget();
+    }
+
     /**
      * Callback interface implemented by the Window Manager.
      */
@@ -3329,6 +3420,14 @@
          */
         @Nullable
         SurfaceControl createSurfaceForGestureMonitor(String name, int displayId);
+
+        /**
+         * Notify WindowManagerService when the display of the mouse pointer changes.
+         * @param displayId The display on which the mouse pointer is shown.
+         * @param x The x coordinate of the mouse pointer.
+         * @param y The y coordinate of the mouse pointer.
+         */
+        void notifyPointerDisplayIdChanged(int displayId, float x, float y);
     }
 
     /**
@@ -3381,6 +3480,9 @@
                     boolean inTabletMode = (boolean) args.arg1;
                     deliverTabletModeChanged(whenNanos, inTabletMode);
                     break;
+                case MSG_POINTER_DISPLAY_ID_CHANGED:
+                    handlePointerDisplayIdChanged((PointerDisplayIdChangedArgs) msg.obj);
+                    break;
             }
         }
     }
@@ -3631,8 +3733,9 @@
         }
 
         @Override
-        public void setVirtualMousePointerDisplayId(int pointerDisplayId) {
-            InputManagerService.this.setVirtualMousePointerDisplayId(pointerDisplayId);
+        public boolean setVirtualMousePointerDisplayId(int pointerDisplayId) {
+            return InputManagerService.this
+                    .setVirtualMousePointerDisplayIdBlocking(pointerDisplayId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 2169155..81882d2 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -176,6 +176,9 @@
 
     void cancelCurrentTouch();
 
+    /** Set the displayId on which the mouse cursor should be shown. */
+    void setPointerDisplayId(int displayId);
+
     /** The native implementation of InputManagerService methods. */
     class NativeImpl implements NativeInputManagerService {
         /** Pointer to native input manager service object, used by native code. */
@@ -388,5 +391,8 @@
 
         @Override
         public native void cancelCurrentTouch();
+
+        @Override
+        public native void setPointerDisplayId(int displayId);
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index e62c5c1..1703310 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -30,6 +30,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -148,10 +149,11 @@
 
     @AnyThread
     void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
-            boolean restarting, @InputMethodNavButtonFlags int navButtonFlags) {
+            boolean restarting, @InputMethodNavButtonFlags int navButtonFlags,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         try {
             mTarget.startInput(startInputToken, inputContext, attribute, restarting,
-                    navButtonFlags);
+                    navButtonFlags, imeDispatcher);
         } catch (RemoteException e) {
             logRemoteException(e);
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6af00b3..c759c64 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -150,6 +150,7 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -616,6 +617,12 @@
     IInputContext mCurInputContext;
 
     /**
+     * The {@link ImeOnBackInvokedDispatcher} last provided by the current client to
+     * receive {@link android.window.OnBackInvokedCallback}s forwarded from IME.
+     */
+    ImeOnBackInvokedDispatcher mCurImeDispatcher;
+
+    /**
      * The {@link IRemoteAccessibilityInputConnection} last provided by the current client.
      */
     @Nullable IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection;
@@ -2623,7 +2630,7 @@
         final SessionState session = mCurClient.curSession;
         setEnabledSessionLocked(session);
         session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
-                navButtonFlags);
+                navButtonFlags, mCurImeDispatcher);
         if (mShowRequested) {
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
             showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2733,7 +2740,8 @@
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
             @StartInputReason int startInputReason,
-            int unverifiedTargetSdkVersion) {
+            int unverifiedTargetSdkVersion,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         // If no method is currently selected, do nothing.
         final String selectedMethodId = getSelectedMethodIdLocked();
         if (selectedMethodId == null) {
@@ -2777,6 +2785,7 @@
         mCurClient = cs;
         mCurInputContext = inputContext;
         mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection;
+        mCurImeDispatcher = imeDispatcher;
         mCurVirtualDisplayToScreenMatrix =
                 getVirtualDisplayToScreenMatrixLocked(cs.selfReportedDisplayId,
                         mDisplayIdToShowIme);
@@ -3780,10 +3789,12 @@
             @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion) {
+            int unverifiedTargetSdkVersion,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
                 startInputFlags, softInputMode, windowFlags, attribute, inputContext,
-                remoteAccessibilityInputConnection, unverifiedTargetSdkVersion);
+                remoteAccessibilityInputConnection, unverifiedTargetSdkVersion,
+                imeDispatcher);
     }
 
     @NonNull
@@ -3792,7 +3803,8 @@
             @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion) {
+            int unverifiedTargetSdkVersion,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (windowToken == null) {
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
@@ -3829,7 +3841,7 @@
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
                             client, windowToken, startInputFlags, softInputMode, windowFlags,
                             attribute, inputContext, remoteAccessibilityInputConnection,
-                            unverifiedTargetSdkVersion, userId);
+                            unverifiedTargetSdkVersion, userId, imeDispatcher);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -3857,7 +3869,8 @@
             @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
             IInputContext inputContext,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion, @UserIdInt int userId) {
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (DEBUG) {
             Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
                     + InputMethodDebug.startInputReasonToString(startInputReason)
@@ -3868,7 +3881,8 @@
                     + InputMethodDebug.startInputFlagsToString(startInputFlags)
                     + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                     + " windowFlags=#" + Integer.toHexString(windowFlags)
-                    + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
+                    + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
+                    + " imeDispatcher=" + imeDispatcher);
         }
 
         final ClientState cs = mClients.get(client.asBinder());
@@ -3952,7 +3966,7 @@
             if (attribute != null) {
                 return startInputUncheckedLocked(cs, inputContext,
                         remoteAccessibilityInputConnection, attribute, startInputFlags,
-                        startInputReason, unverifiedTargetSdkVersion);
+                        startInputReason, unverifiedTargetSdkVersion, imeDispatcher);
             }
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -3993,7 +4007,8 @@
         if (isTextEditor && attribute != null
                 && shouldRestoreImeVisibility(windowToken, softInputMode)) {
             res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
-                    attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+                    attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion,
+                    imeDispatcher);
             showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
                     SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
             return res;
@@ -4033,7 +4048,8 @@
                     if (attribute != null) {
                         res = startInputUncheckedLocked(cs, inputContext,
                                 remoteAccessibilityInputConnection, attribute, startInputFlags,
-                                startInputReason, unverifiedTargetSdkVersion);
+                                startInputReason, unverifiedTargetSdkVersion,
+                                imeDispatcher);
                         didStart = true;
                     }
                     showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4065,7 +4081,8 @@
                         if (attribute != null) {
                             res = startInputUncheckedLocked(cs, inputContext,
                                     remoteAccessibilityInputConnection, attribute, startInputFlags,
-                                    startInputReason, unverifiedTargetSdkVersion);
+                                    startInputReason, unverifiedTargetSdkVersion,
+                                    imeDispatcher);
                             didStart = true;
                         }
                         showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4085,7 +4102,8 @@
                         if (attribute != null) {
                             res = startInputUncheckedLocked(cs, inputContext,
                                     remoteAccessibilityInputConnection, attribute, startInputFlags,
-                                    startInputReason, unverifiedTargetSdkVersion);
+                                    startInputReason, unverifiedTargetSdkVersion,
+                                    imeDispatcher);
                             didStart = true;
                         }
                         showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4115,7 +4133,8 @@
                 }
                 res = startInputUncheckedLocked(cs, inputContext,
                         remoteAccessibilityInputConnection, attribute, startInputFlags,
-                        startInputReason, unverifiedTargetSdkVersion);
+                        startInputReason, unverifiedTargetSdkVersion,
+                        imeDispatcher);
             } else {
                 res = InputBindResult.NULL_EDITOR_INFO;
             }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 92703ec..604e8f3 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -60,8 +60,8 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.NoSuchElementException;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * This is the system implementation of a Session. Apps will interact with the
@@ -1159,6 +1159,9 @@
         public void sendCommand(String packageName, int pid, int uid, String command, Bundle args,
                 ResultReceiver cb) {
             try {
+                final String reason = TAG + ":" + command;
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onCommand(packageName, pid, uid, command, args, cb);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in sendCommand.", e);
@@ -1168,6 +1171,9 @@
         public void sendCustomAction(String packageName, int pid, int uid, String action,
                 Bundle args) {
             try {
+                final String reason = TAG + ":custom-" + action;
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onCustomAction(packageName, pid, uid, action, args);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in sendCustomAction.", e);
@@ -1176,6 +1182,9 @@
 
         public void prepare(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":prepare";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepare(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepare.", e);
@@ -1185,6 +1194,9 @@
         public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":prepareFromMediaId";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepareFromMediaId.", e);
@@ -1194,6 +1206,9 @@
         public void prepareFromSearch(String packageName, int pid, int uid, String query,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":prepareFromSearch";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepareFromSearch.", e);
@@ -1202,6 +1217,9 @@
 
         public void prepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             try {
+                final String reason = TAG + ":prepareFromUri";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepareFromUri.", e);
@@ -1210,6 +1228,9 @@
 
         public void play(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":play";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlay(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in play.", e);
@@ -1219,6 +1240,9 @@
         public void playFromMediaId(String packageName, int pid, int uid, String mediaId,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":playFromMediaId";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in playFromMediaId.", e);
@@ -1228,6 +1252,9 @@
         public void playFromSearch(String packageName, int pid, int uid, String query,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":playFromSearch";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in playFromSearch.", e);
@@ -1236,6 +1263,9 @@
 
         public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             try {
+                final String reason = TAG + ":playFromUri";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in playFromUri.", e);
@@ -1244,6 +1274,9 @@
 
         public void skipToTrack(String packageName, int pid, int uid, long id) {
             try {
+                final String reason = TAG + ":skipToTrack";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSkipToTrack(packageName, pid, uid, id);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in skipToTrack", e);
@@ -1252,6 +1285,9 @@
 
         public void pause(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":pause";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPause(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in pause.", e);
@@ -1260,6 +1296,9 @@
 
         public void stop(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":stop";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onStop(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in stop.", e);
@@ -1268,6 +1307,9 @@
 
         public void next(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":next";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onNext(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in next.", e);
@@ -1276,6 +1318,9 @@
 
         public void previous(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":previous";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrevious(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in previous.", e);
@@ -1284,6 +1329,9 @@
 
         public void fastForward(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":fastForward";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onFastForward(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in fastForward.", e);
@@ -1292,6 +1340,9 @@
 
         public void rewind(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":rewind";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onRewind(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in rewind.", e);
@@ -1300,6 +1351,9 @@
 
         public void seekTo(String packageName, int pid, int uid, long pos) {
             try {
+                final String reason = TAG + ":seekTo";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSeekTo(packageName, pid, uid, pos);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in seekTo.", e);
@@ -1308,6 +1362,9 @@
 
         public void rate(String packageName, int pid, int uid, Rating rating) {
             try {
+                final String reason = TAG + ":rate";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onRate(packageName, pid, uid, rating);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in rate.", e);
@@ -1316,6 +1373,9 @@
 
         public void setPlaybackSpeed(String packageName, int pid, int uid, float speed) {
             try {
+                final String reason = TAG + ":setPlaybackSpeed";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSetPlaybackSpeed(packageName, pid, uid, speed);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in setPlaybackSpeed.", e);
@@ -1325,6 +1385,9 @@
         public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService,
                 int direction) {
             try {
+                final String reason = TAG + ":adjustVolume";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 if (asSystemService) {
                     mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(),
                             Process.SYSTEM_UID, direction);
@@ -1338,6 +1401,9 @@
 
         public void setVolumeTo(String packageName, int pid, int uid, int value) {
             try {
+                final String reason = TAG + ":setVolumeTo";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSetVolumeTo(packageName, pid, uid, value);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in setVolumeTo.", e);
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index a09aa7c..b4230c1 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -55,10 +55,8 @@
     private final PermissionManagerServiceInternal mPmi;
     private final IPackageManager mPackageManager;
     private final IPermissionManager mPermManager;
-    // TODO (b/194833441): Remove this boolean (but keep the isMigrationEnabled() method)
-    //  when the migration is enabled
+    // TODO (b/194833441): Remove when the migration is enabled
     private final boolean mMigrationEnabled;
-    private final boolean mIsTv;
     private final boolean mForceUserSetOnUpgrade;
 
     public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager,
@@ -69,17 +67,10 @@
         mPermManager = permManager;
         mMigrationEnabled = migrationEnabled;
         mForceUserSetOnUpgrade = forceUserSetOnUpgrade;
-        boolean isTv;
-        try {
-            isTv = mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0);
-        } catch (RemoteException e) {
-            isTv = false;
-        }
-        mIsTv = isTv;
     }
 
     public boolean isMigrationEnabled() {
-        return mMigrationEnabled && !mIsTv;
+        return mMigrationEnabled;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 2824585..3911994 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -34,13 +34,14 @@
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
 import android.content.pm.IOnChecksumsReadyListener;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -63,7 +64,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.security.VerityUtils;
-import com.android.server.LocalServices;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.ByteArrayOutputStream;
@@ -637,9 +637,18 @@
         return null;
     }
 
+    private static boolean containsFile(File dir, String filePath) {
+        if (dir == null) {
+            return false;
+        }
+        return FileUtils.contains(dir.getAbsolutePath(), filePath);
+    }
+
     private static ApkChecksum extractHashFromFS(String split, String filePath) {
         // verity first
-        {
+        // Skip /product folder.
+        // TODO(b/231354111): remove this hack once we are allowed to change SELinux rules.
+        if (!containsFile(Environment.getProductDirectory(), filePath)) {
             byte[] hash = VerityUtils.getFsverityRootHash(filePath);
             if (hash != null) {
                 return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 320b06f..32f0f10 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -119,7 +119,7 @@
             IInstalld.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
 
     private final boolean mIsolated;
-
+    private volatile boolean mDeferSetFirstBoot;
     private volatile IInstalld mInstalld;
     private volatile Object mWarnIfHeld;
 
@@ -171,6 +171,7 @@
             mInstalld = IInstalld.Stub.asInterface(binder);
             try {
                 invalidateMounts();
+                executeDeferredActions();
             } catch (InstallerException ignored) {
             }
         } else {
@@ -180,6 +181,15 @@
     }
 
     /**
+     * Perform any deferred actions on mInstalld while the connection could not be made.
+     */
+    private void executeDeferredActions() throws InstallerException {
+        if (mDeferSetFirstBoot) {
+            setFirstBoot();
+        }
+    }
+
+    /**
      * Do several pre-flight checks before making a remote call.
      *
      * @return if the remote call should continue.
@@ -291,8 +301,15 @@
             return;
         }
         try {
-            mInstalld.setFirstBoot();
-        } catch (RemoteException e) {
+            // mInstalld might be null if the connection could not be established.
+            if (mInstalld != null) {
+                mInstalld.setFirstBoot();
+            } else {
+                // if it is null while trying to set the first boot, set a flag to try and set the
+                // first boot when the connection is eventually established
+                mDeferSetFirstBoot = true;
+            }
+        } catch (Exception e) {
             throw InstallerException.from(e);
         }
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ba4d09f..6400502 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4579,7 +4579,7 @@
                 pw.print(" (override=true)");
             }
             pw.println();
-            if (ps.getPkg().getQueriesPackages().isEmpty()) {
+            if (!ps.getPkg().getQueriesPackages().isEmpty()) {
                 pw.append(prefix).append("  queriesPackages=")
                         .println(ps.getPkg().getQueriesPackages());
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 4df54b7..fa0c6c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -166,18 +166,19 @@
      * An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
      */
     @GuardedBy("mLock")
-    final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
+    private final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
 
     /**
      * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on
      * IDs.
      */
     @GuardedBy("mLock")
-    private ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
+    private final ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
 
     /**
      * All the share targets from the package
      */
+    @GuardedBy("mLock")
     private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
 
     /**
@@ -231,7 +232,9 @@
     }
 
     public int getShortcutCount() {
-        return mShortcuts.size();
+        synchronized (mLock) {
+            return mShortcuts.size();
+        }
     }
 
     @Override
@@ -272,7 +275,9 @@
     @Nullable
     public ShortcutInfo findShortcutById(@Nullable final String id) {
         if (id == null) return null;
-        return mShortcuts.get(id);
+        synchronized (mLock) {
+            return mShortcuts.get(id);
+        }
     }
 
     public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
@@ -347,11 +352,14 @@
      * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
      */
     private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
-        final ShortcutInfo shortcut = mShortcuts.remove(id);
-        if (shortcut != null) {
-            removeIcon(shortcut);
-            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
-                    | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
+        final ShortcutInfo shortcut;
+        synchronized (mLock) {
+            shortcut = mShortcuts.remove(id);
+            if (shortcut != null) {
+                removeIcon(shortcut);
+                shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
+                        | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
+            }
         }
         return shortcut;
     }
@@ -524,14 +532,16 @@
     public List<ShortcutInfo> deleteAllDynamicShortcuts() {
         final long now = mShortcutUser.mService.injectCurrentTimeMillis();
         boolean changed = false;
-        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-            ShortcutInfo si = mShortcuts.valueAt(i);
-            if (si.isDynamic() && si.isVisibleToPublisher()) {
-                changed = true;
+        synchronized (mLock) {
+            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+                ShortcutInfo si = mShortcuts.valueAt(i);
+                if (si.isDynamic() && si.isVisibleToPublisher()) {
+                    changed = true;
 
-                si.setTimestamp(now);
-                si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
-                si.setRank(0); // It may still be pinned, so clear the rank.
+                    si.setTimestamp(now);
+                    si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+                    si.setRank(0); // It may still be pinned, so clear the rank.
+                }
             }
         }
         removeAllShortcutsAsync();
@@ -874,59 +884,63 @@
      */
     public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
             @NonNull IntentFilter filter) {
-        final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
-        for (int i = 0; i < mShareTargets.size(); i++) {
-            final ShareTargetInfo target = mShareTargets.get(i);
-            for (ShareTargetInfo.TargetData data : target.mTargetData) {
-                if (filter.hasDataType(data.mMimeType)) {
-                    // Matched at least with one data type
-                    matchedTargets.add(target);
-                    break;
-                }
-            }
-        }
-
-        if (matchedTargets.isEmpty()) {
-            return new ArrayList<>();
-        }
-
-        // Get the list of all dynamic shortcuts in this package.
-        final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
-        // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
-        // included in the result
-        findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
-                ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
-                mShortcutUser.mService.mContext.getPackageName(),
-                0, /*getPinnedByAnyLauncher=*/ false);
-
-        final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
-        for (int i = 0; i < shortcuts.size(); i++) {
-            final Set<String> categories = shortcuts.get(i).getCategories();
-            if (categories == null || categories.isEmpty()) {
-                continue;
-            }
-            for (int j = 0; j < matchedTargets.size(); j++) {
-                // Shortcut must have all of share target categories
-                boolean hasAllCategories = true;
-                final ShareTargetInfo target = matchedTargets.get(j);
-                for (int q = 0; q < target.mCategories.length; q++) {
-                    if (!categories.contains(target.mCategories[q])) {
-                        hasAllCategories = false;
+        synchronized (mLock) {
+            final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
+            for (int i = 0; i < mShareTargets.size(); i++) {
+                final ShareTargetInfo target = mShareTargets.get(i);
+                for (ShareTargetInfo.TargetData data : target.mTargetData) {
+                    if (filter.hasDataType(data.mMimeType)) {
+                        // Matched at least with one data type
+                        matchedTargets.add(target);
                         break;
                     }
                 }
-                if (hasAllCategories) {
-                    result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i),
-                            new ComponentName(getPackageName(), target.mTargetClass)));
-                    break;
+            }
+
+            if (matchedTargets.isEmpty()) {
+                return new ArrayList<>();
+            }
+
+            // Get the list of all dynamic shortcuts in this package.
+            final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+            // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
+            // included in the result
+            findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+                    ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
+                    mShortcutUser.mService.mContext.getPackageName(),
+                    0, /*getPinnedByAnyLauncher=*/ false);
+
+            final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
+            for (int i = 0; i < shortcuts.size(); i++) {
+                final Set<String> categories = shortcuts.get(i).getCategories();
+                if (categories == null || categories.isEmpty()) {
+                    continue;
+                }
+                for (int j = 0; j < matchedTargets.size(); j++) {
+                    // Shortcut must have all of share target categories
+                    boolean hasAllCategories = true;
+                    final ShareTargetInfo target = matchedTargets.get(j);
+                    for (int q = 0; q < target.mCategories.length; q++) {
+                        if (!categories.contains(target.mCategories[q])) {
+                            hasAllCategories = false;
+                            break;
+                        }
+                    }
+                    if (hasAllCategories) {
+                        result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i),
+                                new ComponentName(getPackageName(), target.mTargetClass)));
+                        break;
+                    }
                 }
             }
+            return result;
         }
-        return result;
     }
 
     public boolean hasShareTargets() {
-        return !mShareTargets.isEmpty();
+        synchronized (mLock) {
+            return !mShareTargets.isEmpty();
+        }
     }
 
     /**
@@ -935,38 +949,40 @@
      * the app's Xml resource.
      */
     int getSharingShortcutCount() {
-        if (mShareTargets.isEmpty()) {
-            return 0;
-        }
-
-        // Get the list of all dynamic shortcuts in this package
-        final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
-        findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
-                ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
-
-        int sharingShortcutCount = 0;
-        for (int i = 0; i < shortcuts.size(); i++) {
-            final Set<String> categories = shortcuts.get(i).getCategories();
-            if (categories == null || categories.isEmpty()) {
-                continue;
+        synchronized (mLock) {
+            if (mShareTargets.isEmpty()) {
+                return 0;
             }
-            for (int j = 0; j < mShareTargets.size(); j++) {
-                // A SharingShortcut must have all of share target categories
-                boolean hasAllCategories = true;
-                final ShareTargetInfo target = mShareTargets.get(j);
-                for (int q = 0; q < target.mCategories.length; q++) {
-                    if (!categories.contains(target.mCategories[q])) {
-                        hasAllCategories = false;
+
+            // Get the list of all dynamic shortcuts in this package
+            final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+            findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+                    ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+            int sharingShortcutCount = 0;
+            for (int i = 0; i < shortcuts.size(); i++) {
+                final Set<String> categories = shortcuts.get(i).getCategories();
+                if (categories == null || categories.isEmpty()) {
+                    continue;
+                }
+                for (int j = 0; j < mShareTargets.size(); j++) {
+                    // A SharingShortcut must have all of share target categories
+                    boolean hasAllCategories = true;
+                    final ShareTargetInfo target = mShareTargets.get(j);
+                    for (int q = 0; q < target.mCategories.length; q++) {
+                        if (!categories.contains(target.mCategories[q])) {
+                            hasAllCategories = false;
+                            break;
+                        }
+                    }
+                    if (hasAllCategories) {
+                        sharingShortcutCount++;
                         break;
                     }
                 }
-                if (hasAllCategories) {
-                    sharingShortcutCount++;
-                    break;
-                }
             }
+            return sharingShortcutCount;
         }
-        return sharingShortcutCount;
     }
 
     /**
@@ -1090,19 +1106,25 @@
 
         // Now prepare to publish manifest shortcuts.
         List<ShortcutInfo> newManifestShortcutList = null;
-        try {
-            newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
-                    getPackageName(), getPackageUserId(), mShareTargets);
-        } catch (IOException|XmlPullParserException e) {
-            Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+        final int shareTargetSize;
+        synchronized (mLock) {
+            try {
+                shareTargetSize = mShareTargets.size();
+                newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
+                        getPackageName(), getPackageUserId(), mShareTargets);
+            } catch (IOException | XmlPullParserException e) {
+                Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+            }
         }
         final int manifestShortcutSize = newManifestShortcutList == null ? 0
                 : newManifestShortcutList.size();
         if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
             Slog.d(TAG,
-                    String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
-                            getPackageName(), manifestShortcutSize, mShareTargets.size()));
+                    String.format(
+                            "Package %s has %d manifest shortcut(s), and %d share target(s)",
+                            getPackageName(), manifestShortcutSize, shareTargetSize));
         }
+
         if (isNewApp && (manifestShortcutSize == 0)) {
             // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
 
@@ -1701,37 +1723,38 @@
     @Override
     public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
-        final int size = mShortcuts.size();
-        final int shareTargetSize = mShareTargets.size();
+        synchronized (mLock) {
+            final int size = mShortcuts.size();
+            final int shareTargetSize = mShareTargets.size();
 
-        if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) {
-            return; // nothing to write.
-        }
+            if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) {
+                return; // nothing to write.
+            }
 
-        out.startTag(null, TAG_ROOT);
+            out.startTag(null, TAG_ROOT);
 
-        ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
-        ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
-        ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
-        if (!forBackup) {
-            synchronized (mLock) {
-                ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate)
+            ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
+            ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
+            ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
+            if (!forBackup) {
+                ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, mIsAppSearchSchemaUpToDate
                         ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
             }
-        }
-        getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
+            getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
 
-        for (int j = 0; j < size; j++) {
-            saveShortcut(out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
-        }
-
-        if (!forBackup) {
-            for (int j = 0; j < shareTargetSize; j++) {
-                mShareTargets.get(j).saveToXml(out);
+            for (int j = 0; j < size; j++) {
+                saveShortcut(
+                        out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
             }
-        }
 
-        out.endTag(null, TAG_ROOT);
+            if (!forBackup) {
+                for (int j = 0; j < shareTargetSize; j++) {
+                    mShareTargets.get(j).saveToXml(out);
+                }
+            }
+
+            out.endTag(null, TAG_ROOT);
+        }
     }
 
     private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup,
@@ -1917,38 +1940,38 @@
         synchronized (ret.mLock) {
             ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
                     parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
-        }
-        ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
-        ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
 
+            ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
+            ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
 
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-            final int depth = parser.getDepth();
-            final String tag = parser.getName();
-            if (depth == outerDepth + 1) {
-                switch (tag) {
-                    case ShortcutPackageInfo.TAG_ROOT:
-                        ret.getPackageInfo().loadFromXml(parser, fromBackup);
-
-                        continue;
-                    case TAG_SHORTCUT:
-                        final ShortcutInfo si = parseShortcut(parser, packageName,
-                                shortcutUser.getUserId(), fromBackup);
-                        // Don't use addShortcut(), we don't need to save the icon.
-                        ret.mShortcuts.put(si.getId(), si);
-                        continue;
-                    case TAG_SHARE_TARGET:
-                        ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser));
-                        continue;
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
                 }
+                final int depth = parser.getDepth();
+                final String tag = parser.getName();
+                if (depth == outerDepth + 1) {
+                    switch (tag) {
+                        case ShortcutPackageInfo.TAG_ROOT:
+                            ret.getPackageInfo().loadFromXml(parser, fromBackup);
+
+                            continue;
+                        case TAG_SHORTCUT:
+                            final ShortcutInfo si = parseShortcut(parser, packageName,
+                                    shortcutUser.getUserId(), fromBackup);
+                            // Don't use addShortcut(), we don't need to save the icon.
+                            ret.mShortcuts.put(si.getId(), si);
+                            continue;
+                        case TAG_SHARE_TARGET:
+                            ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser));
+                            continue;
+                    }
+                }
+                ShortcutService.warnForInvalidTag(depth, tag);
             }
-            ShortcutService.warnForInvalidTag(depth, tag);
         }
         return ret;
     }
@@ -2152,7 +2175,9 @@
 
     @VisibleForTesting
     List<ShareTargetInfo> getAllShareTargetsForTest() {
-        return new ArrayList<>(mShareTargets);
+        synchronized (mLock) {
+            return new ArrayList<>(mShareTargets);
+        }
     }
 
     @Override
@@ -2291,15 +2316,19 @@
 
     private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
         Objects.requireNonNull(shortcuts);
-        for (ShortcutInfo si : shortcuts) {
-            mShortcuts.put(si.getId(), si);
+        synchronized (mLock) {
+            for (ShortcutInfo si : shortcuts) {
+                mShortcuts.put(si.getId(), si);
+            }
         }
     }
 
     @Nullable
     List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) {
-        return ids.stream().map(mShortcuts::get)
-                .filter(Objects::nonNull).collect(Collectors.toList());
+        synchronized (mLock) {
+            return ids.stream().map(mShortcuts::get)
+                    .filter(Objects::nonNull).collect(Collectors.toList());
+        }
     }
 
     private void forEachShortcut(@NonNull final Consumer<ShortcutInfo> cb) {
@@ -2318,10 +2347,12 @@
 
     private void forEachShortcutStopWhen(
             @NonNull final Function<ShortcutInfo, Boolean> cb) {
-        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-            final ShortcutInfo si = mShortcuts.valueAt(i);
-            if (cb.apply(si)) {
-                return;
+        synchronized (mLock) {
+            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+                final ShortcutInfo si = mShortcuts.valueAt(i);
+                if (cb.apply(si)) {
+                    return;
+                }
             }
         }
     }
@@ -2461,6 +2492,7 @@
                         })));
     }
 
+    @GuardedBy("mLock")
     @Override
     void scheduleSaveToAppSearchLocked() {
         final Map<String, ShortcutInfo> copy = new ArrayMap<>(mShortcuts);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b142141..d8e7fbe 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -27,7 +27,6 @@
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
 import static android.os.Build.VERSION_CODES.M;
 import static android.os.Build.VERSION_CODES.O;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
@@ -130,7 +129,6 @@
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.session.MediaSessionLegacyHelper;
-import android.os.BatteryManagerInternal;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DeviceIdleManager;
@@ -396,7 +394,6 @@
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
     StatusBarManagerInternal mStatusBarManagerInternal;
-    BatteryManagerInternal mBatteryManagerInternal;
     AudioManagerInternal mAudioManagerInternal;
     DisplayManager mDisplayManager;
     DisplayManagerInternal mDisplayManagerInternal;
@@ -791,8 +788,7 @@
         @Override
         public void onWakeUp() {
             synchronized (mLock) {
-                if (shouldEnableWakeGestureLp()
-                        && getBatteryManagerInternal().getPlugType() != BATTERY_PLUGGED_WIRELESS) {
+                if (shouldEnableWakeGestureLp()) {
                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                             "Wake Up");
                     wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
@@ -849,15 +845,6 @@
         }
     }
 
-    BatteryManagerInternal getBatteryManagerInternal() {
-        synchronized (mServiceAcquireLock) {
-            if (mBatteryManagerInternal == null) {
-                mBatteryManagerInternal =
-                        LocalServices.getService(BatteryManagerInternal.class);
-            }
-            return mBatteryManagerInternal;
-        }
-    }
 
     // returns true if the key was handled and should not be passed to the user
     private boolean backKeyPress() {
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 0834417..3c779f3 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -727,7 +727,8 @@
                 return false;
             }
 
-            if (mKeyguardManager != null && mKeyguardManager.isDeviceLocked(userId)) {
+            if (requiresAuthentication() && mKeyguardManager != null
+                    && mKeyguardManager.isDeviceLocked(userId)) {
                 Log.i(TAG, "Can't change mic/cam toggle while device is locked");
                 return false;
             }
@@ -993,6 +994,13 @@
         }
 
         @Override
+        public boolean requiresAuthentication() {
+            enforceObserveSensorPrivacyPermission();
+            return mContext.getResources()
+                    .getBoolean(R.bool.config_sensorPrivacyRequiresAuthentication);
+        }
+
+        @Override
         public void showSensorUseDialog(int sensor) {
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                 throw new SecurityException("Can only be called by the system uid");
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index d374814..4b8c7c1 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -122,16 +122,9 @@
             if (!TrustManagerService.ENABLE_ACTIVE_UNLOCK_FLAG) {
                 return;
             }
-            if (!mWaitingForTrustableDowngrade) {
-                return;
-            }
             // are these the broadcasts we want to listen to
-            if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())
-                    || Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
-                mTrusted = false;
-                mTrustable = true;
-                mWaitingForTrustableDowngrade = false;
-                mTrustManagerService.updateTrust(mUserId, 0);
+            if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                downgradeToTrustable();
             }
         }
     };
@@ -480,8 +473,7 @@
         final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
         alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
 
-        IntentFilter trustableFilter = new IntentFilter(Intent.ACTION_USER_PRESENT);
-        trustableFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        IntentFilter trustableFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
 
         // Schedules a restart for when connecting times out. If the connection succeeds,
         // the restart is canceled in mCallback's onConnected.
@@ -668,6 +660,19 @@
         mTrustable = false;
     }
 
+    /**
+     * Downgrades the trustagent to trustable as a result of a keyguard or screen related event, and
+     * then updates the trust state of the phone to reflect the change.
+     */
+    public void downgradeToTrustable() {
+        if (mWaitingForTrustableDowngrade) {
+            mWaitingForTrustableDowngrade = false;
+            mTrusted = false;
+            mTrustable = true;
+            mTrustManagerService.updateTrust(mUserId, 0);
+        }
+    }
+
     public boolean isManagingTrust() {
         return mManagingTrust && !mTrustDisabledByDpm;
     }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 8f4ddea..80ce70d 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1184,6 +1184,22 @@
         return false;
     }
 
+    /**
+     * We downgrade to trustable whenever keyguard changes its showing value.
+     *  - becomes showing: something has caused the device to show keyguard which happens due to
+     *  user intent to lock the device either through direct action or a timeout
+     *  - becomes not showing: keyguard was dismissed and we no longer need to keep the device
+     *  unlocked
+     *  */
+    private void dispatchTrustableDowngrade() {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.userId == mCurrentUser) {
+                info.agent.downgradeToTrustable();
+            }
+        }
+    }
+
     private List<String> getTrustGrantedMessages(int userId) {
         if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
             return new ArrayList<>();
@@ -1752,6 +1768,7 @@
                     refreshDeviceLockedForUser(UserHandle.USER_ALL);
                     break;
                 case MSG_KEYGUARD_SHOWING_CHANGED:
+                    dispatchTrustableDowngrade();
                     refreshDeviceLockedForUser(mCurrentUser);
                     break;
                 case MSG_START_USER:
diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java
index 6059f96..75e39eb 100644
--- a/services/core/java/com/android/server/utils/WatchedArrayList.java
+++ b/services/core/java/com/android/server/utils/WatchedArrayList.java
@@ -416,7 +416,7 @@
         dst.mStorage.ensureCapacity(end);
         for (int i = 0; i < end; i++) {
             final E val = Snapshots.maybeSnapshot(src.get(i));
-            dst.add(i, val);
+            dst.add(val);
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 63619e5..f1dbad61 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -446,6 +446,43 @@
         // Not relevant for the window observer.
     }
 
+    public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+            IBinder token) {
+        synchronized (mService.mGlobalLock) {
+            final Matrix transformationMatrix = new Matrix();
+            final MagnificationSpec magnificationSpec = new MagnificationSpec();
+
+            final WindowState windowState = mService.mWindowMap.get(token);
+            if (windowState != null) {
+                windowState.getTransformationMatrix(new float[9], transformationMatrix);
+
+                if (hasCallbacks()) {
+                    final MagnificationSpec otherMagnificationSpec =
+                            getMagnificationSpecForWindow(windowState);
+                    if (otherMagnificationSpec != null && !otherMagnificationSpec.isNop()) {
+                        magnificationSpec.setTo(otherMagnificationSpec);
+                    }
+                }
+            }
+
+            return new Pair<>(transformationMatrix, magnificationSpec);
+        }
+    }
+
+    MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
+                    FLAGS_MAGNIFICATION_CALLBACK,
+                    "windowState={" + windowState + "}");
+        }
+        final int displayId = windowState.getDisplayId();
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            return displayMagnifier.getMagnificationSpecForWindow(windowState);
+        }
+        return null;
+    }
+
     boolean hasCallbacks() {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b97ee7e..b2f27eb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2732,8 +2732,8 @@
         intentActivity.getTaskFragment().clearLastPausedActivity();
         Task intentTask = intentActivity.getTask();
 
-        // Only update the target-root-task when it is not indicated.
         if (mTargetRootTask == null) {
+            // Update launch target task when it is not indicated.
             if (mSourceRecord != null && mSourceRecord.mLaunchRootTask != null) {
                 // Inherit the target-root-task from source to ensure trampoline activities will be
                 // launched into the same root task.
@@ -2742,6 +2742,17 @@
                 mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, intentTask,
                         mOptions);
             }
+        } else {
+            // If a launch target indicated, and the matching task is already in the adjacent task
+            // of the launch target. Adjust to use the adjacent task as its launch target. So the
+            // existing task will be launched into the closer one and won't be reparent redundantly.
+            // TODO(b/231541706): Migrate the logic to wm-shell after having proper APIs to help
+            //  resolve target task without actually starting the activity.
+            final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null
+                    ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null;
+            if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) {
+                mTargetRootTask = adjacentTargetTask;
+            }
         }
 
         // If the target task is not in the front, then we need to bring it to the front...
@@ -2771,7 +2782,7 @@
                     intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
                 }
 
-                if (mTargetRootTask == intentActivity.getRootTask()) {
+                if (intentActivity.isDescendantOf(mTargetRootTask)) {
                     // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
                     //  tasks hierarchies.
                     if (mTargetRootTask != intentTask
@@ -2818,19 +2829,6 @@
         mTargetRootTask = intentActivity.getRootTask();
         mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
                 mRootWindowContainer.getDefaultTaskDisplayArea(), mTargetRootTask);
-
-        // We need to check if there is a launch root task in TDA for this target root task.
-        // If it exist, we need to reparent target root task from TDA to launch root task.
-        final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
-        final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
-                mTargetRootTask.getActivityType(), null /** options */, mSourceRootTask,
-                mLaunchFlags);
-        // If target root task is created by organizer, let organizer handle reparent itself.
-        if (!mTargetRootTask.mCreatedByOrganizer && launchRootTask != null
-                && launchRootTask != mTargetRootTask) {
-            mTargetRootTask.reparent(launchRootTask, POSITION_TOP);
-            mTargetRootTask = launchRootTask;
-        }
     }
 
     private void resumeTargetRootTaskIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 701fc94..cb65597 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -68,6 +68,7 @@
 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp;
 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -1142,13 +1143,17 @@
             if (activity == null) {
                 continue;
             }
+            if (activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
+                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                        "Delaying app transition for recents animation to finish");
+                return false;
+            }
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                     "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
                             + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
                     activity, activity.allDrawn, activity.startingDisplayed,
                     activity.startingMoved, activity.isRelaunching(),
                     activity.mStartingWindow);
-
             final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
             if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
                 return false;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index ee7d9a9..46ce433 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -153,10 +153,10 @@
             for (WindowContainer wc : mRootMembers) {
                 wc.waitForSyncTransactionCommit(wcAwaitingCommit);
             }
-            final Runnable callback = new Runnable() {
+            class CommitCallback implements Runnable {
                 // Can run a second time if the action completes after the timeout.
                 boolean ran = false;
-                public void run() {
+                public void onCommitted() {
                     synchronized (mWm.mGlobalLock) {
                         if (ran) {
                             return;
@@ -171,8 +171,23 @@
                         wcAwaitingCommit.clear();
                     }
                 }
+
+                // Called in timeout
+                @Override
+                public void run() {
+                    // Sometimes we get a trace, sometimes we get a bugreport without
+                    // a trace. Since these kind of ANRs can trigger such an issue,
+                    // try and ensure we will have some visibility in both cases.
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout");
+                    Slog.e(TAG, "WM sent Transaction to organized, but never received" +
+                           " commit callback. Application ANR likely to follow.");
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                    onCommitted();
+
+                }
             };
-            merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::run);
+            CommitCallback callback = new CommitCallback();
+            merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::onCommitted);
             mWm.mH.postDelayed(callback, BLAST_TIMEOUT_DURATION);
 
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b37f980..247117e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -124,8 +124,7 @@
                     LocalServices.getService(WindowManagerInternal.class);
             IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
 
-            window = wmService.windowForClientLocked(null, focusedWindowToken,
-                    false /* throwOnError */);
+            window = wmService.getFocusedWindowLocked();
 
             if (window == null) {
                 EmbeddedWindowController.EmbeddedWindow embeddedWindow =
@@ -147,12 +146,6 @@
             }
 
             if (window == null) {
-                window = wmService.getFocusedWindowLocked();
-                ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
-                        "Focused window found using wmService.getFocusedWindowLocked()");
-            }
-
-            if (window == null) {
                 // We don't have any focused window, fallback ont the top currentTask of the focused
                 // display.
                 ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
@@ -194,7 +187,6 @@
             if (backType == BackNavigationInfo.TYPE_CALLBACK
                     || currentActivity == null
                     || currentTask == null
-                    || currentTask.getDisplayContent().getImeContainer().isVisible()
                     || currentActivity.isActivityTypeHome()) {
                 return infoBuilder
                         .setType(backType)
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2975a95..d0baa23 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2922,7 +2922,7 @@
     }
 
     DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
-        if (mDisplayPolicy == null) {
+        if (mDisplayPolicy == null || mInitialDisplayCutout == null) {
             return null;
         }
         return DisplayCutout.fromResourcesRectApproximation(
@@ -2931,7 +2931,7 @@
     }
 
     RoundedCorners loadRoundedCorners(int displayWidth, int displayHeight) {
-        if (mDisplayPolicy == null) {
+        if (mDisplayPolicy == null || mInitialRoundedCorners == null) {
             return null;
         }
         return RoundedCorners.fromResources(
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 67dd89e..33cdd2e 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -270,6 +270,22 @@
         }
     }
 
+    @Override
+    public void notifyPointerDisplayIdChanged(int displayId, float x, float y) {
+        synchronized (mService.mGlobalLock) {
+            mService.setMousePointerDisplayId(displayId);
+            if (displayId == Display.INVALID_DISPLAY) return;
+
+            final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+            if (dc == null) {
+                Slog.wtf(TAG, "The mouse pointer was moved to display " + displayId
+                        + " that does not have a valid DisplayContent.");
+                return;
+            }
+            mService.restorePointerIconLocked(dc, x, y);
+        }
+    }
+
     /** Waits until the built-in input devices have been configured. */
     public boolean waitForInputDevicesReady(long timeoutMillis) {
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 65062e5..ea72e12 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -573,7 +573,7 @@
                             recentsAnimationController.getTargetAppDisplayArea();
                     if (targetDA != null) {
                         mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA);
-                        mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+                        mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 2);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
                 }
@@ -584,10 +584,14 @@
                     final Task rootTask = w.getTask().getRootTask();
                     mPipInputConsumer.mWindowHandle.replaceTouchableRegionWithCrop(
                             rootTask.getSurfaceControl());
+                    final DisplayArea targetDA = rootTask.getDisplayArea();
                     // We set the layer to z=MAX-1 so that it's always on top.
-                    mPipInputConsumer.reparent(mInputTransaction, rootTask);
-                    mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
-                    mAddPipInputConsumerHandle = false;
+                    if (targetDA != null) {
+                        mPipInputConsumer.layout(mInputTransaction, rootTask.getBounds());
+                        mPipInputConsumer.reparent(mInputTransaction, targetDA);
+                        mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+                        mAddPipInputConsumerHandle = false;
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 4068a97..c7f8a1e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -158,7 +158,7 @@
     void continueStartingAnimations() {
         synchronized (mLock) {
             mAnimationStartDeferred = false;
-            if (!mPendingAnimations.isEmpty()) {
+            if (!mPendingAnimations.isEmpty() && mPreProcessingAnimations.isEmpty()) {
                 mChoreographer.postFrameCallback(this::startAnimations);
             }
         }
@@ -204,7 +204,7 @@
 
                             mPreProcessingAnimations.remove(animationLeash);
                             mPendingAnimations.put(animationLeash, runningAnim);
-                            if (!mAnimationStartDeferred) {
+                            if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                                 mChoreographer.postFrameCallback(this::startAnimations);
                             }
                         }
@@ -214,7 +214,7 @@
 
             if (!requiresEdgeExtension) {
                 mPendingAnimations.put(animationLeash, runningAnim);
-                if (!mAnimationStartDeferred) {
+                if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                     mChoreographer.postFrameCallback(this::startAnimations);
                 }
 
@@ -330,6 +330,14 @@
 
     private void startAnimations(long frameTimeNanos) {
         synchronized (mLock) {
+            if (!mPreProcessingAnimations.isEmpty()) {
+                // We only want to start running animations once all mPreProcessingAnimations have
+                // been processed to ensure preprocessed animations start in sync.
+                // NOTE: This means we might delay running animations that require preprocessing if
+                // new animations that also require preprocessing are requested before the previous
+                // ones have finished (see b/227449117).
+                return;
+            }
             startPendingAnimationsLocked();
         }
         mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
@@ -553,4 +561,4 @@
             return mAnimationHandler;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index c7bc513..a480c37 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1584,6 +1584,14 @@
         return true;
     }
 
+    void forAllWindowContainers(Consumer<WindowContainer> callback) {
+        callback.accept(this);
+        final int count = mChildren.size();
+        for (int i = 0; i < count; i++) {
+            mChildren.get(i).forAllWindowContainers(callback);
+        }
+    }
+
     /**
      * For all windows at or below this container call the callback.
      * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
@@ -3741,8 +3749,16 @@
             }
             // Otherwise this is the "root" of a synced subtree, so continue on to preparation.
         }
+
         // This container's situation has changed so we need to restart its sync.
-        mSyncState = SYNC_STATE_NONE;
+        // We cannot reset the sync without a chance of a deadlock since it will request a new
+        // buffer from the app process. This could cause issues if the app has run out of buffers
+        // since the previous buffer was already synced and is still held in a transaction.
+        // Resetting syncState violates the policies outlined in BlastSyncEngine.md so for now
+        // disable this when shell transitions is disabled.
+        if (mTransitionController.isShellTransitionsEnabled()) {
+            mSyncState = SYNC_STATE_NONE;
+        }
         prepareSync();
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index f8bc26a..42d1842 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -23,11 +23,13 @@
 import android.annotation.Nullable;
 import android.content.ClipData;
 import android.content.Context;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.Pair;
 import android.view.Display;
 import android.view.IInputFilter;
 import android.view.IRemoteAnimationFinishedCallback;
@@ -459,6 +461,17 @@
     public abstract void getWindowFrame(IBinder token, Rect outBounds);
 
     /**
+     * Get the transformation matrix and MagnificationSpec given its token.
+     *
+     * @param token The token.
+     * @return The pair of the transformation matrix and magnification spec.
+     */
+    // TODO (b/231663133): Long term solution for tracking window when the
+    //                     FLAG_RETRIEVE_INTERACTIVE_WINDOWS is unset.
+    public abstract Pair<Matrix, MagnificationSpec>
+            getWindowTransformationMatrixAndMagnificationSpec(IBinder token);
+
+    /**
      * Opens the global actions dialog.
      */
     public abstract void showGlobalActions();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7b77fd0..5f079ac 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -118,6 +118,7 @@
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
@@ -172,6 +173,7 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -223,6 +225,7 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.MergedConfiguration;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
@@ -3050,13 +3053,22 @@
         }
     }
 
+
     void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
         if (mRecentsAnimationController != null) {
             final RecentsAnimationController controller = mRecentsAnimationController;
             mRecentsAnimationController = null;
             controller.cleanupAnimation(reorderMode);
-            // TODO(mult-display): currently only default display support recents animation.
-            getDefaultDisplayContentLocked().mAppTransition.updateBooster();
+            // TODO(multi-display): currently only default display support recents animation.
+            // Cancel any existing app transition animation running in the legacy transition
+            // framework.
+            final DisplayContent dc = getDefaultDisplayContentLocked();
+            dc.mAppTransition.freeze();
+            dc.forAllWindowContainers((wc) -> {
+                if (wc.isAnimating(TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) {
+                    wc.cancelAnimation();
+                }
+            });
         }
     }
 
@@ -7198,18 +7210,42 @@
         private float mLatestMouseX;
         private float mLatestMouseY;
 
-        void updatePosition(float x, float y) {
+        /**
+         * The display that the pointer (mouse cursor) is currently shown on. This is updated
+         * directly by InputManagerService when the pointer display changes.
+         */
+        private int mPointerDisplayId = INVALID_DISPLAY;
+
+        /**
+         * Update the mouse cursor position as a result of a mouse movement.
+         * @return true if the position was successfully updated, false otherwise.
+         */
+        boolean updatePosition(int displayId, float x, float y) {
             synchronized (this) {
                 mLatestEventWasMouse = true;
+
+                if (displayId != mPointerDisplayId) {
+                    // The display of the position update does not match the display on which the
+                    // mouse pointer is shown, so do not update the position.
+                    return false;
+                }
                 mLatestMouseX = x;
                 mLatestMouseY = y;
+                return true;
+            }
+        }
+
+        void setPointerDisplayId(int displayId) {
+            synchronized (this) {
+                mPointerDisplayId = displayId;
             }
         }
 
         @Override
         public void onPointerEvent(MotionEvent motionEvent) {
             if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) {
-                updatePosition(motionEvent.getRawX(), motionEvent.getRawY());
+                updatePosition(motionEvent.getDisplayId(), motionEvent.getRawX(),
+                        motionEvent.getRawY());
             } else {
                 synchronized (this) {
                     mLatestEventWasMouse = false;
@@ -7219,6 +7255,7 @@
     };
 
     void updatePointerIcon(IWindow client) {
+        int pointerDisplayId;
         float mouseX, mouseY;
 
         synchronized(mMousePositionTracker) {
@@ -7227,6 +7264,7 @@
             }
             mouseX = mMousePositionTracker.mLatestMouseX;
             mouseY = mMousePositionTracker.mLatestMouseY;
+            pointerDisplayId = mMousePositionTracker.mPointerDisplayId;
         }
 
         synchronized (mGlobalLock) {
@@ -7243,6 +7281,10 @@
             if (displayContent == null) {
                 return;
             }
+            if (pointerDisplayId != displayContent.getDisplayId()) {
+                // Do not let the pointer icon be updated by a window on a different display.
+                return;
+            }
             WindowState windowUnderPointer =
                     displayContent.getTouchableWinAtPointLocked(mouseX, mouseY);
             if (windowUnderPointer != callingWin) {
@@ -7260,7 +7302,11 @@
 
     void restorePointerIconLocked(DisplayContent displayContent, float latestX, float latestY) {
         // Mouse position tracker has not been getting updates while dragging, update it now.
-        mMousePositionTracker.updatePosition(latestX, latestY);
+        if (!mMousePositionTracker.updatePosition(
+                displayContent.getDisplayId(), latestX, latestY)) {
+            // The mouse position could not be updated, so ignore this request.
+            return;
+        }
 
         WindowState windowUnderPointer =
                 displayContent.getTouchableWinAtPointLocked(latestX, latestY);
@@ -7284,6 +7330,10 @@
         }
     }
 
+    void setMousePointerDisplayId(int displayId) {
+        mMousePositionTracker.setPointerDisplayId(displayId);
+    }
+
     /**
      * Update a tap exclude region in the window identified by the provided id. Touches down on this
      * region will not:
@@ -7721,6 +7771,13 @@
         }
 
         @Override
+        public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+                IBinder token) {
+            return mAccessibilityController
+                    .getWindowTransformationMatrixAndMagnificationSpec(token);
+        }
+
+        @Override
         public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
             final WindowContainer container = displayId == INVALID_DISPLAY
                     ? mRoot : mRoot.getDisplayContent(displayId);
@@ -8829,16 +8886,18 @@
                 WindowState newFocusTarget =  displayContent == null
                         ? null : displayContent.findFocusedWindow();
                 if (newFocusTarget == null) {
-                    ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for "
-                                    + "win=%s dropped since no candidate was found",
+                    t.setFocusedWindow(null, null, displayId).apply();
+                    ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s"
+                                    + " dropped focus so setting focus to null since no candidate"
+                                    + " was found",
                             embeddedWindow);
                     return;
                 }
-                t.requestFocusTransfer(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
-                        inputToken, embeddedWindow.toString(),
+                t.setFocusedWindow(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
                         displayId).apply();
+
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
-                        "Transfer focus request " + newFocusTarget,
+                        "Focus request " + newFocusTarget,
                         "reason=grantEmbeddedWindowFocus(false)");
             }
             ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index d2e56fa..ed9b2f0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -378,7 +378,7 @@
     SurfaceControl.Builder makeSurface() {
         final SurfaceControl.Builder builder = super.makeSurface();
         if (mRoundedCornerOverlay) {
-            builder.setParent(null);
+            builder.setParent(getDisplayContent().getSurfaceControl());
         }
         return builder;
     }
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 49a4021..93152f2 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -66,13 +66,13 @@
 // Defines the maximum amount of VMAs we can send per process_madvise syscall.
 // Currently this is set to UIO_MAXIOV which is the maximum segments allowed by
 // iovec implementation used by process_madvise syscall
-#define MAX_VMAS_PER_BATCH UIO_MAXIOV
+#define MAX_VMAS_PER_COMPACTION UIO_MAXIOV
 
 // Maximum bytes that we can send per process_madvise syscall once this limit
 // is reached we split the remaining VMAs into another syscall. The MAX_RW_COUNT
 // limit is imposed by iovec implementation. However, if you want to use a smaller
-// limit, it has to be a page aligned value.
-#define MAX_BYTES_PER_BATCH MAX_RW_COUNT
+// limit, it has to be a page aligned value, otherwise, compaction would fail.
+#define MAX_BYTES_PER_COMPACTION MAX_RW_COUNT
 
 // Selected a high enough number to avoid clashing with linux errno codes
 #define ERROR_COMPACTION_CANCELLED -1000
@@ -83,181 +83,6 @@
 // before starting next VMA batch
 static std::atomic<bool> cancelRunningCompaction;
 
-// A VmaBatch represents a set of VMAs that can be processed
-// as VMAs are processed by client code it is expected that the
-// VMAs get consumed which means they are discarded as they are
-// processed so that the first element always is the next element
-// to be sent
-struct VmaBatch {
-    struct iovec* vmas;
-    // total amount of VMAs to reach the end of iovec
-    int totalVmas;
-    // total amount of bytes that are remaining within iovec
-    uint64_t totalBytes;
-};
-
-// Advances the iterator by the specified amount of bytes.
-// This is used to remove already processed or no longer
-// needed parts of the batch.
-// Returns total bytes consumed
-int consumeBytes(VmaBatch& batch, uint64_t bytesToConsume) {
-    int index = 0;
-    if (CC_UNLIKELY(bytesToConsume) < 0) {
-        LOG(ERROR) << "Cannot consume negative bytes for VMA batch !";
-        return 0;
-    }
-
-    if (bytesToConsume > batch.totalBytes) {
-        // Avoid consuming more bytes than available
-        bytesToConsume = batch.totalBytes;
-    }
-
-    uint64_t bytesConsumed = 0;
-    while (bytesConsumed < bytesToConsume) {
-        if (CC_UNLIKELY(index >= batch.totalVmas)) {
-            // reach the end of the batch
-            return bytesConsumed;
-        }
-        if (CC_UNLIKELY(bytesConsumed + batch.vmas[index].iov_len > bytesToConsume)) {
-            // this is the whole VMA that will be consumed
-            break;
-        }
-        bytesConsumed += batch.vmas[index].iov_len;
-        batch.totalBytes -= batch.vmas[index].iov_len;
-        --batch.totalVmas;
-        ++index;
-    }
-
-    // Move pointer to consume all the whole VMAs
-    batch.vmas = batch.vmas + index;
-
-    // Consume the rest of the bytes partially at last VMA in batch
-    uint64_t bytesLeftToConsume = bytesToConsume - bytesConsumed;
-    bytesConsumed += bytesLeftToConsume;
-    if (batch.totalVmas > 0) {
-        batch.vmas[0].iov_base = (void*)((uint64_t)batch.vmas[0].iov_base + bytesLeftToConsume);
-    }
-
-    return bytesConsumed;
-}
-
-// given a source of vmas this class will act as a factory
-// of VmaBatch objects and it will allow generating batches
-// until there are no more left in the source vector.
-// Note: the class does not actually modify the given
-// vmas vector, instead it iterates on it until the end.
-class VmaBatchCreator {
-    const std::vector<Vma>* sourceVmas;
-    // This is the destination array where batched VMAs will be stored
-    // it gets encapsulated into a VmaBatch which is the object
-    // meant to be used by client code.
-    struct iovec* destVmas;
-
-    // Parameters to keep track of the iterator on the source vmas
-    int currentIndex_;
-    uint64_t currentOffset_;
-
-public:
-    VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec)
-          : sourceVmas(vmasToBatch), destVmas(destVmasVec), currentIndex_(0), currentOffset_(0) {}
-
-    int currentIndex() { return currentIndex_; }
-    uint64_t currentOffset() { return currentOffset_; }
-
-    // Generates a batch and moves the iterator on the source vmas
-    // past the last VMA in the batch.
-    // Returns true on success, false on failure
-    bool createNextBatch(VmaBatch& batch) {
-        if (currentIndex_ >= MAX_VMAS_PER_BATCH && currentIndex_ >= sourceVmas->size()) {
-            return false;
-        }
-
-        const std::vector<Vma>& vmas = *sourceVmas;
-        batch.vmas = destVmas;
-        uint64_t totalBytesInBatch = 0;
-        int indexInBatch = 0;
-
-        // Add VMAs to the batch up until we consumed all the VMAs or
-        // reached any imposed limit of VMAs per batch.
-        while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < vmas.size()) {
-            uint64_t vmaStart = vmas[currentIndex_].start + currentOffset_;
-            uint64_t vmaSize = vmas[currentIndex_].end - vmaStart;
-            if (CC_UNLIKELY(vmaSize == 0)) {
-                // No more bytes to batch for this VMA, move to next one
-                // this only happens if a batch partially consumed bytes
-                // and offset landed at exactly the end of a vma
-                continue;
-            }
-            batch.vmas[indexInBatch].iov_base = (void*)vmaStart;
-            uint64_t bytesAvailableInBatch = MAX_BYTES_PER_BATCH - totalBytesInBatch;
-
-            if (vmaSize >= bytesAvailableInBatch) {
-                // VMA would exceed the max available bytes in batch
-                // clamp with available bytes and finish batch.
-                vmaSize = bytesAvailableInBatch;
-                currentOffset_ += bytesAvailableInBatch;
-            }
-
-            batch.vmas[indexInBatch].iov_len = vmaSize;
-            totalBytesInBatch += vmaSize;
-
-            ++indexInBatch;
-            if (totalBytesInBatch >= MAX_BYTES_PER_BATCH) {
-                // Reached max bytes quota so this marks
-                // the end of the batch
-                break;
-            }
-
-            // Fully finished current VMA, move to next one
-            currentOffset_ = 0;
-            ++currentIndex_;
-        }
-        // Vmas where fully filled and we are past the last filled index.
-        batch.totalVmas = indexInBatch;
-        batch.totalBytes = totalBytesInBatch;
-        return true;
-    }
-};
-
-// Madvise a set of VMAs given in a batch for a specific process
-// The total number of bytes successfully madvised will be set on
-// outBytesProcessed.
-// Returns 0 on success and standard linux -errno code returned by
-// process_madvise on failure
-int madviseVmasFromBatch(unique_fd& pidfd, VmaBatch& batch, int madviseType,
-                         uint64_t* outBytesProcessed) {
-    if (batch.totalVmas == 0) {
-        // No VMAs in Batch, skip.
-        *outBytesProcessed = 0;
-        return 0;
-    }
-
-    ATRACE_BEGIN(StringPrintf("Madvise %d: %d VMAs", madviseType, batch.totalVmas).c_str());
-    uint64_t bytesProcessedInSend =
-            process_madvise(pidfd, batch.vmas, batch.totalVmas, madviseType, 0);
-    ATRACE_END();
-
-    if (CC_UNLIKELY(bytesProcessedInSend == -1)) {
-        bytesProcessedInSend = 0;
-        if (errno != EINVAL) {
-            // Forward irrecoverable errors and bail out compaction
-            *outBytesProcessed = 0;
-            return -errno;
-        }
-    }
-
-    if (bytesProcessedInSend < batch.totalBytes) {
-        // Did not process all the bytes requested
-        // skip last page which likely failed
-        bytesProcessedInSend += PAGE_SIZE;
-    }
-
-    bytesProcessedInSend = consumeBytes(batch, bytesProcessedInSend);
-
-    *outBytesProcessed = bytesProcessedInSend;
-    return 0;
-}
-
 // Legacy method for compacting processes, any new code should
 // use compactProcess instead.
 static inline void compactProcessProcfs(int pid, const std::string& compactionType) {
@@ -271,6 +96,8 @@
 // If any VMA fails compaction due to -EINVAL it will be skipped and continue.
 // However, if it fails for any other reason, it will bail out and forward the error
 static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
+    static struct iovec vmasToKernel[MAX_VMAS_PER_COMPACTION];
+
     if (vmas.empty()) {
         return 0;
     }
@@ -281,16 +108,13 @@
         return -errno;
     }
 
-    struct iovec destVmas[MAX_VMAS_PER_BATCH];
-
-    VmaBatch batch;
-    VmaBatchCreator batcher(&vmas, destVmas);
-
     int64_t totalBytesProcessed = 0;
-    while (batcher.createNextBatch(batch)) {
-        uint64_t bytesProcessedInSend;
 
-        do {
+    int64_t vmaOffset = 0;
+    for (int iVma = 0; iVma < vmas.size();) {
+        uint64_t bytesSentToCompact = 0;
+        int iVec = 0;
+        while (iVec < MAX_VMAS_PER_COMPACTION && iVma < vmas.size()) {
             if (CC_UNLIKELY(cancelRunningCompaction.load())) {
                 // There could be a significant delay between when a compaction
                 // is requested and when it is handled during this time our
@@ -300,13 +124,50 @@
                                          StringPrintf("Cancelled compaction for %d", pid).c_str());
                 return ERROR_COMPACTION_CANCELLED;
             }
-            int error = madviseVmasFromBatch(pidfd, batch, madviseType, &bytesProcessedInSend);
-            if (error < 0) {
-                // Returns standard linux errno code
-                return error;
+
+            uint64_t vmaStart = vmas[iVma].start + vmaOffset;
+            uint64_t vmaSize = vmas[iVma].end - vmaStart;
+            if (vmaSize == 0) {
+                goto next_vma;
             }
-            totalBytesProcessed += bytesProcessedInSend;
-        } while (batch.totalBytes > 0);
+            vmasToKernel[iVec].iov_base = (void*)vmaStart;
+            if (vmaSize > MAX_BYTES_PER_COMPACTION - bytesSentToCompact) {
+                // Exceeded the max bytes that could be sent, so clamp
+                // the end to avoid exceeding limit and issue compaction
+                vmaSize = MAX_BYTES_PER_COMPACTION - bytesSentToCompact;
+            }
+
+            vmasToKernel[iVec].iov_len = vmaSize;
+            bytesSentToCompact += vmaSize;
+            ++iVec;
+            if (bytesSentToCompact >= MAX_BYTES_PER_COMPACTION) {
+                // Ran out of bytes within iovec, dispatch compaction.
+                vmaOffset += vmaSize;
+                break;
+            }
+
+        next_vma:
+            // Finished current VMA, and have more bytes remaining
+            vmaOffset = 0;
+            ++iVma;
+        }
+
+        ATRACE_BEGIN(StringPrintf("Compact %d VMAs", iVec).c_str());
+        auto bytesProcessed = process_madvise(pidfd, vmasToKernel, iVec, madviseType, 0);
+        ATRACE_END();
+
+        if (CC_UNLIKELY(bytesProcessed == -1)) {
+            if (errno == EINVAL) {
+                // This error is somewhat common due to an unevictable VMA if this is
+                // the case silently skip the bad VMA and continue compacting the rest.
+                continue;
+            } else {
+                // Forward irrecoverable errors and bail out compaction
+                return -errno;
+            }
+        }
+
+        totalBytesProcessed += bytesProcessed;
     }
 
     return totalBytesProcessed;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3c5ebe7..32adac7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,6 +107,7 @@
     jmethodID interceptKeyBeforeDispatching;
     jmethodID dispatchUnhandledKey;
     jmethodID checkInjectEventsPermission;
+    jmethodID onPointerDisplayIdChanged;
     jmethodID onPointerDownOutsideFocus;
     jmethodID getVirtualKeyQuietTimeMillis;
     jmethodID getExcludedDeviceNames;
@@ -120,7 +121,6 @@
     jmethodID getLongPressTimeout;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
-    jmethodID getPointerDisplayId;
     jmethodID getKeyboardLayoutOverlay;
     jmethodID getDeviceAlias;
     jmethodID getTouchCalibrationForInputDevice;
@@ -277,6 +277,7 @@
     void setFocusedDisplay(int32_t displayId);
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiLightsOut(bool lightsOut);
+    void setPointerDisplayId(int32_t displayId);
     void setPointerSpeed(int32_t speed);
     void setPointerAcceleration(float acceleration);
     void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
@@ -288,7 +289,6 @@
     void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled);
     void setCustomPointerIcon(const SpriteIcon& icon);
     void setMotionClassifierEnabled(bool enabled);
-    void notifyPointerDisplayIdChanged();
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -346,6 +346,7 @@
             std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId);
     virtual int32_t getDefaultPointerIconId();
     virtual int32_t getCustomPointerIconId();
+    virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos);
 
 private:
     sp<InputManagerInterface> mInputManager;
@@ -394,7 +395,6 @@
     void updateInactivityTimeoutLocked();
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
     void ensureSpriteControllerLocked();
-    int32_t getPointerDisplayId();
     sp<SurfaceControl> getParentSurfaceForPointers(int displayId);
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
@@ -498,13 +498,9 @@
         }
     }
 
-    // Get the preferred pointer controller displayId.
-    int32_t pointerDisplayId = getPointerDisplayId();
-
     { // acquire lock
         AutoMutex _l(mLock);
         mLocked.viewports = viewports;
-        mLocked.pointerDisplayId = pointerDisplayId;
         std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
         if (controller != nullptr) {
             controller->onDisplayViewportsUpdated(mLocked.viewports);
@@ -666,15 +662,12 @@
     return controller;
 }
 
-int32_t NativeInputManager::getPointerDisplayId() {
+void NativeInputManager::onPointerDisplayIdChanged(int32_t pointerDisplayId, float xPos,
+                                                   float yPos) {
     JNIEnv* env = jniEnv();
-    jint pointerDisplayId = env->CallIntMethod(mServiceObj,
-            gServiceClassInfo.getPointerDisplayId);
-    if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
-        pointerDisplayId = ADISPLAY_ID_DEFAULT;
-    }
-
-    return pointerDisplayId;
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId,
+                        xPos, yPos);
+    checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
 }
 
 sp<SurfaceControl> NativeInputManager::getParentSurfaceForPointers(int displayId) {
@@ -1032,6 +1025,22 @@
                                                                : InactivityTimeout::NORMAL);
 }
 
+void NativeInputManager::setPointerDisplayId(int32_t displayId) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mLocked.pointerDisplayId == displayId) {
+            return;
+        }
+
+        ALOGI("Setting pointer display id to %d.", displayId);
+        mLocked.pointerDisplayId = displayId;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
 void NativeInputManager::setPointerSpeed(int32_t speed) {
     { // acquire lock
         AutoMutex _l(mLock);
@@ -1494,18 +1503,6 @@
     mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
 }
 
-void NativeInputManager::notifyPointerDisplayIdChanged() {
-    int32_t pointerDisplayId = getPointerDisplayId();
-
-    { // acquire lock
-        AutoMutex _l(mLock);
-        mLocked.pointerDisplayId = pointerDisplayId;
-    } // release lock
-
-    mInputManager->getReader().requestRefreshConfiguration(
-            InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-}
-
 // ----------------------------------------------------------------------------
 
 static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) {
@@ -2199,11 +2196,6 @@
             InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 }
 
-static void nativeNotifyPointerDisplayIdChanged(JNIEnv* env, jobject nativeImplObj) {
-    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
-    im->notifyPointerDisplayIdChanged();
-}
-
 static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jobject nativeImplObj,
                                                          jint displayId, jboolean isEligible) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2321,6 +2313,11 @@
     im->getInputManager()->getDispatcher().cancelCurrentTouch();
 }
 
+static void nativeSetPointerDisplayId(JNIEnv* env, jobject nativeImplObj, jint displayId) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    im->setPointerDisplayId(displayId);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -2393,7 +2390,6 @@
         {"canDispatchToDisplay", "(II)Z", (void*)nativeCanDispatchToDisplay},
         {"notifyPortAssociationsChanged", "()V", (void*)nativeNotifyPortAssociationsChanged},
         {"changeUniqueIdAssociation", "()V", (void*)nativeChangeUniqueIdAssociation},
-        {"notifyPointerDisplayIdChanged", "()V", (void*)nativeNotifyPointerDisplayIdChanged},
         {"setDisplayEligibilityForPointerCapture", "(IZ)V",
          (void*)nativeSetDisplayEligibilityForPointerCapture},
         {"setMotionClassifierEnabled", "(Z)V", (void*)nativeSetMotionClassifierEnabled},
@@ -2403,6 +2399,7 @@
         {"disableSensor", "(II)V", (void*)nativeDisableSensor},
         {"flushSensor", "(II)Z", (void*)nativeFlushSensor},
         {"cancelCurrentTouch", "()V", (void*)nativeCancelCurrentTouch},
+        {"setPointerDisplayId", "(I)V", (void*)nativeSetPointerDisplayId},
 };
 
 #define FIND_CLASS(var, className) \
@@ -2498,6 +2495,9 @@
     GET_METHOD_ID(gServiceClassInfo.checkInjectEventsPermission, clazz,
                   "checkInjectEventsPermission", "(II)Z");
 
+    GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
+                  "(IFF)V");
+
     GET_METHOD_ID(gServiceClassInfo.onPointerDownOutsideFocus, clazz,
             "onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V");
 
@@ -2537,9 +2537,6 @@
     GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
             "getPointerIcon", "(I)Landroid/view/PointerIcon;");
 
-    GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
-            "getPointerDisplayId", "()I");
-
     GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
             "getKeyboardLayoutOverlay",
             "(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8a7134e..7f466f3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8513,6 +8513,13 @@
         }
     }
 
+    private boolean isDeviceOwnerUserId(int userId) {
+        synchronized (getLockObject()) {
+            return mOwners.getDeviceOwnerComponent() != null
+                    && mOwners.getDeviceOwnerUserId() == userId;
+        }
+    }
+
     private boolean isDeviceOwnerPackage(String packageName, int userId) {
         synchronized (getLockObject()) {
             return mOwners.hasDeviceOwner()
@@ -12117,10 +12124,11 @@
             return;
         }
         final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isProfileOwner(caller)
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                        && isManagedProfile(caller.getUserId()))
                         || isDefaultDeviceOwner(caller),
-                "Caller is not profile owner or device owner;"
-                        + " only profile owner or device owner may control the preferential"
+                "Caller is not managed profile owner or device owner;"
+                        + " only managed profile owner or device owner may control the preferential"
                         + " network service");
         synchronized (getLockObject()) {
             final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
@@ -12147,11 +12155,12 @@
         }
 
         final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isProfileOwner(caller)
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                        && isManagedProfile(caller.getUserId()))
                         || isDefaultDeviceOwner(caller),
-                "Caller is not profile owner or device owner;"
-                        + " only profile owner or device owner may retrieve the preferential"
-                        + " network service configurations");
+                "Caller is not managed profile owner or device owner;"
+                        + " only managed profile owner or device owner may retrieve the "
+                        + "preferential network service configurations");
         synchronized (getLockObject()) {
             final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
                     caller.getUserId());
@@ -18266,7 +18275,7 @@
 
     private void updateNetworkPreferenceForUser(int userId,
             List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs) {
-        if (!isManagedProfile(userId)) {
+        if (!isManagedProfile(userId) && !isDeviceOwnerUserId(userId)) {
             return;
         }
         List<ProfileNetworkPreference> preferences = new ArrayList<>();
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 8aa9f60..994a767 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -238,7 +238,7 @@
             }
         }
 
-        // called from Device.close()
+        // called from Device.closeLocked()
         public void removeDeviceConnection(DeviceConnection connection) {
             mDeviceConnections.remove(connection.getToken());
             if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
@@ -294,12 +294,6 @@
             }
 
             for (DeviceConnection connection : mDeviceConnections.values()) {
-                if (connection.getDevice().getDeviceInfo().getType()
-                        == MidiDeviceInfo.TYPE_USB) {
-                    synchronized (mUsbMidiLock) {
-                        removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
-                    }
-                }
                 connection.getDevice().removeDeviceConnection(connection);
             }
         }
@@ -541,6 +535,13 @@
                 synchronized (mDeviceConnections) {
                     mDeviceConnections.remove(connection);
 
+                    if (connection.getDevice().getDeviceInfo().getType()
+                            == MidiDeviceInfo.TYPE_USB) {
+                        synchronized (mUsbMidiLock) {
+                            removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
+                        }
+                    }
+
                     if (mDeviceConnections.size() == 0 && mServiceConnection != null) {
                         mContext.unbindService(mServiceConnection);
                         mServiceConnection = null;
@@ -559,6 +560,12 @@
         public void closeLocked() {
             synchronized (mDeviceConnections) {
                 for (DeviceConnection connection : mDeviceConnections) {
+                    if (connection.getDevice().getDeviceInfo().getType()
+                            == MidiDeviceInfo.TYPE_USB) {
+                        synchronized (mUsbMidiLock) {
+                            removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
+                        }
+                    }
                     connection.getClient().removeDeviceConnection(connection);
                 }
                 mDeviceConnections.clear();
@@ -1401,6 +1408,8 @@
         String deviceName = extractUsbDeviceName(name);
         String tagName = extractUsbDeviceTag(name);
 
+        Log.i(TAG, "Checking " + deviceName + " " + tagName);
+
         // Only one MIDI 2.0 device can be used at once.
         // Multiple MIDI 1.0 devices can be used at once.
         if (mUsbMidiUniversalDeviceInUse.contains(deviceName)
@@ -1420,6 +1429,8 @@
         String deviceName = extractUsbDeviceName(name);
         String tagName = extractUsbDeviceTag(name);
 
+        Log.i(TAG, "Adding " + deviceName + " " + tagName);
+
         if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
             mUsbMidiUniversalDeviceInUse.add(deviceName);
         } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
@@ -1437,6 +1448,8 @@
         String deviceName = extractUsbDeviceName(name);
         String tagName = extractUsbDeviceTag(name);
 
+        Log.i(TAG, "Removing " + deviceName + " " + tagName);
+
         if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
             mUsbMidiUniversalDeviceInUse.remove(deviceName);
         } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
diff --git a/services/proguard.flags b/services/proguard.flags
index 425da6c..bad02b4 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -33,6 +33,11 @@
   public <init>(...);
 }
 
+# Accessed from com.android.compos APEX
+-keep,allowoptimization,allowaccessmodification class com.android.internal.art.ArtStatsLog {
+   public static void write(...);
+}
+
 # Binder interfaces
 -keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface
 -keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 08df438..19df5a2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -89,8 +89,10 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.util.Pair;
 import android.view.Display;
 import android.view.KeyEvent;
+import android.view.MagnificationSpec;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -145,6 +147,8 @@
     private static final int USER_ID = 1;
     private static final int USER_ID2 = 2;
     private static final int INTERACTION_ID = 199;
+    private static final Pair<float[], MagnificationSpec> FAKE_MATRIX_AND_MAG_SPEC =
+            new Pair<>(new float[9], new MagnificationSpec());
     private static final int PID = Process.myPid();
     private static final long TID = Process.myTid();
     private static final int UID = Process.myUid();
@@ -188,6 +192,8 @@
                 .thenReturn(mMockFingerprintGestureDispatcher);
         when(mMockSystemSupport.getMagnificationProcessor())
                 .thenReturn(mMockMagnificationProcessor);
+        when(mMockSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(anyInt()))
+                .thenReturn(FAKE_MATRIX_AND_MAG_SPEC);
 
         PowerManager powerManager =
                 new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index b4bb04d..77cbb3a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -19,22 +19,22 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.IInputManager;
-import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.view.Display;
 import android.view.DisplayInfo;
 
-import androidx.test.runner.AndroidJUnit4;
-
 import com.android.server.LocalServices;
 
 import org.junit.Before;
@@ -44,7 +44,8 @@
 import org.mockito.MockitoAnnotations;
 
 @Presubmit
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class InputControllerTest {
 
     @Mock
@@ -56,13 +57,16 @@
     @Mock
     private IInputManager mIInputManagerMock;
 
+    private InputManagerMockHelper mInputManagerMockHelper;
     private InputController mInputController;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mInputManagerMockHelper = new InputManagerMockHelper(
+                TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock);
 
-        doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+        doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
         LocalServices.removeServiceForTest(InputManagerInternal.class);
         LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
 
@@ -72,10 +76,10 @@
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
 
-        InputManager.resetInstance(mIInputManagerMock);
-        doNothing().when(mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
-        doNothing().when(mIInputManagerMock).removeUniqueIdAssociation(anyString());
-        mInputController = new InputController(new Object(), mNativeWrapperMock);
+        // Allow virtual devices to be created on the looper thread for testing.
+        final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
+        mInputController = new InputController(new Object(), mNativeWrapperMock,
+                new Handler(TestableLooper.get(this).getLooper()), threadVerifier);
     }
 
     @Test
@@ -83,6 +87,7 @@
         final IBinder deviceToken = new Binder();
         mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
                 /* displayId= */ 1);
+        verify(mNativeWrapperMock).openUinputMouse(eq("name"), eq(1), eq(1), anyString());
         verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
         doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
         mInputController.unregisterInputDevice(deviceToken);
@@ -95,10 +100,12 @@
         final IBinder deviceToken = new Binder();
         mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
                 /* displayId= */ 1);
+        verify(mNativeWrapperMock).openUinputMouse(eq("name"), eq(1), eq(1), anyString());
         verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
         final IBinder deviceToken2 = new Binder();
         mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken2,
                 /* displayId= */ 2);
+        verify(mNativeWrapperMock, times(2)).openUinputMouse(eq("name"), eq(1), eq(1), anyString());
         verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2));
         mInputController.unregisterInputDevice(deviceToken);
         verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
new file mode 100644
index 0000000..aa2d97e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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.companion.virtual;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import android.hardware.input.IInputDevicesChangedListener;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
+import android.os.RemoteException;
+import android.testing.TestableLooper;
+import android.view.InputDevice;
+
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+/**
+ * A test utility class used to share the logic for setting up {@link InputManager}'s callback for
+ * when a virtual input device being added.
+ */
+class InputManagerMockHelper {
+    private final TestableLooper mTestableLooper;
+    private final InputController.NativeWrapper mNativeWrapperMock;
+    private final IInputManager mIInputManagerMock;
+    private final List<InputDevice> mDevices = new ArrayList<>();
+    private IInputDevicesChangedListener mDevicesChangedListener;
+
+    InputManagerMockHelper(TestableLooper testableLooper,
+            InputController.NativeWrapper nativeWrapperMock, IInputManager iInputManagerMock)
+            throws Exception {
+        mTestableLooper = testableLooper;
+        mNativeWrapperMock = nativeWrapperMock;
+        mIInputManagerMock = iInputManagerMock;
+
+        doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputMouse(
+                anyString(), anyInt(), anyInt(), anyString());
+        doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputKeyboard(
+                anyString(), anyInt(), anyInt(), anyString());
+        doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputTouchscreen(
+                anyString(), anyInt(), anyInt(), anyString(), anyInt(), anyInt());
+
+        doAnswer(inv -> {
+            mDevicesChangedListener = inv.getArgument(0);
+            return null;
+        }).when(mIInputManagerMock).registerInputDevicesChangedListener(notNull());
+        when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
+        doAnswer(inv -> mDevices.get(inv.getArgument(0)))
+                .when(mIInputManagerMock).getInputDevice(anyInt());
+        doNothing().when(mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
+        doNothing().when(mIInputManagerMock).removeUniqueIdAssociation(anyString());
+
+        // Set a new instance of InputManager for testing that uses the IInputManager mock as the
+        // interface to the server.
+        InputManager.resetInstance(mIInputManagerMock);
+    }
+
+    private Void handleNativeOpenInputDevice(InvocationOnMock inv) {
+        Objects.requireNonNull(mDevicesChangedListener,
+                "InputController did not register an InputDevicesChangedListener.");
+        // We only use a subset of the fields of InputDevice in InputController.
+        final InputDevice device = new InputDevice(mDevices.size() /*id*/, 1 /*generation*/, 0,
+                inv.getArgument(0) /*name*/, inv.getArgument(1) /*vendorId*/,
+                inv.getArgument(2) /*productId*/, inv.getArgument(3) /*descriptor*/,
+                true /*isExternal*/, 0 /*sources*/, 0 /*keyboardType*/,
+                null /*keyCharacterMap*/, false /*hasVibrator*/, false /*hasMic*/,
+                false /*hasButtonUnderPad*/, false /*hasSensor*/, false /*hasBattery*/);
+        mDevices.add(device);
+        try {
+            mDevicesChangedListener.onInputDevicesChanged(
+                    mDevices.stream().flatMapToInt(
+                            d -> IntStream.of(d.getId(), d.getGeneration())).toArray());
+        } catch (RemoteException ignored) {
+        }
+        // Process the device added notification.
+        mTestableLooper.processAllMessages();
+        return null;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 808f8c2c..cbb9fd7 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -54,6 +54,7 @@
 import android.content.pm.ApplicationInfo;
 import android.graphics.Point;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.IInputManager;
 import android.hardware.input.InputManagerInternal;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
@@ -118,6 +119,7 @@
     private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000;
 
     private Context mContext;
+    private InputManagerMockHelper mInputManagerMockHelper;
     private VirtualDeviceImpl mDeviceImpl;
     private InputController mInputController;
     private AssociationInfo mAssociationInfo;
@@ -146,6 +148,8 @@
     private IAudioConfigChangedCallback mConfigChangedCallback;
     @Mock
     private ApplicationInfo mApplicationInfoMock;
+    @Mock
+    IInputManager mIInputManagerMock;
 
     private ArraySet<ComponentName> getBlockedActivities() {
         ArraySet<ComponentName> blockedActivities = new ArraySet<>();
@@ -170,13 +174,13 @@
     }
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
 
-        doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+        doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
         doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
         doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
         LocalServices.removeServiceForTest(InputManagerInternal.class);
@@ -199,7 +203,13 @@
                 new Handler(TestableLooper.get(this).getLooper()));
         when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
 
-        mInputController = new InputController(new Object(), mNativeWrapperMock);
+        mInputManagerMockHelper = new InputManagerMockHelper(
+                TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock);
+        // Allow virtual devices to be created on the looper thread for testing.
+        final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
+        mInputController = new InputController(new Object(), mNativeWrapperMock,
+                new Handler(TestableLooper.get(this).getLooper()), threadVerifier);
+
         mAssociationInfo = new AssociationInfo(1, 0, null,
                 MacAddress.BROADCAST_ADDRESS, "", null, true, false, 0, 0);
 
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
new file mode 100644
index 0000000..e78f0c7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 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.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.hardware.display.DisplayViewport
+import android.hardware.input.InputManagerInternal
+import android.os.IInputConstants
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.view.Display
+import android.view.PointerIcon
+import androidx.test.InstrumentationRegistry
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+/**
+ * Tests for {@link InputManagerService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:InputManagerServiceTests
+ */
+@Presubmit
+class InputManagerServiceTests {
+
+    @get:Rule
+    val rule = MockitoJUnit.rule()!!
+
+    @Mock
+    private lateinit var native: NativeInputManagerService
+
+    @Mock
+    private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
+
+    private lateinit var service: InputManagerService
+    private lateinit var localService: InputManagerInternal
+    private lateinit var context: Context
+    private lateinit var testLooper: TestLooper
+
+    @Before
+    fun setup() {
+        context = spy(ContextWrapper(InstrumentationRegistry.getContext()))
+        testLooper = TestLooper()
+        service =
+            InputManagerService(object : InputManagerService.Injector(context, testLooper.looper) {
+                override fun getNativeService(
+                    service: InputManagerService?
+                ): NativeInputManagerService {
+                    return native
+                }
+
+                override fun registerLocalService(service: InputManagerInternal?) {
+                    localService = service!!
+                }
+            })
+        assertTrue("Local service must be registered", this::localService.isInitialized)
+        service.setWindowManagerCallbacks(wmCallbacks)
+    }
+
+    @Test
+    fun testPointerDisplayUpdatesWhenDisplayViewportsChanged() {
+        val displayId = 123
+        `when`(wmCallbacks.pointerDisplayId).thenReturn(displayId)
+        val viewports = listOf<DisplayViewport>()
+        localService.setDisplayViewports(viewports)
+        verify(native).setDisplayViewports(any(Array<DisplayViewport>::class.java))
+        verify(native).setPointerDisplayId(displayId)
+
+        val x = 42f
+        val y = 314f
+        service.onPointerDisplayIdChanged(displayId, x, y)
+        testLooper.dispatchNext()
+        verify(wmCallbacks).notifyPointerDisplayIdChanged(displayId, x, y)
+    }
+
+    @Test
+    fun testSetVirtualMousePointerDisplayId() {
+        // Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
+        // until the native callback happens.
+        var countDownLatch = CountDownLatch(1)
+        val overrideDisplayId = 123
+        Thread {
+            assertTrue("Setting virtual pointer display should succeed",
+                localService.setVirtualMousePointerDisplayId(overrideDisplayId))
+            countDownLatch.countDown()
+        }.start()
+        assertFalse("Setting virtual pointer display should block",
+            countDownLatch.await(100, TimeUnit.MILLISECONDS))
+
+        val x = 42f
+        val y = 314f
+        service.onPointerDisplayIdChanged(overrideDisplayId, x, y)
+        testLooper.dispatchNext()
+        verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, x, y)
+        assertTrue("Native callback unblocks calling thread",
+            countDownLatch.await(100, TimeUnit.MILLISECONDS))
+        verify(native).setPointerDisplayId(overrideDisplayId)
+
+        // Ensure that setting the same override again succeeds immediately.
+        assertTrue("Setting the same virtual mouse pointer displayId again should succeed",
+            localService.setVirtualMousePointerDisplayId(overrideDisplayId))
+
+        // Ensure that we did not query WM for the pointerDisplayId when setting the override
+        verify(wmCallbacks, never()).pointerDisplayId
+
+        // Unset the virtual mouse pointer displayId, and ensure that we query WM for the new
+        // pointer displayId and the calling thread is blocked until the native callback happens.
+        countDownLatch = CountDownLatch(1)
+        val pointerDisplayId = 42
+        `when`(wmCallbacks.pointerDisplayId).thenReturn(pointerDisplayId)
+        Thread {
+            assertTrue("Unsetting virtual mouse pointer displayId should succeed",
+                localService.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY))
+            countDownLatch.countDown()
+        }.start()
+        assertFalse("Unsetting virtual mouse pointer displayId should block",
+            countDownLatch.await(100, TimeUnit.MILLISECONDS))
+
+        service.onPointerDisplayIdChanged(pointerDisplayId, x, y)
+        testLooper.dispatchNext()
+        verify(wmCallbacks).notifyPointerDisplayIdChanged(pointerDisplayId, x, y)
+        assertTrue("Native callback unblocks calling thread",
+            countDownLatch.await(100, TimeUnit.MILLISECONDS))
+        verify(native).setPointerDisplayId(pointerDisplayId)
+    }
+
+    @Test
+    fun testSetVirtualMousePointerDisplayId_unsuccessfulUpdate() {
+        // Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
+        // until the native callback happens.
+        val countDownLatch = CountDownLatch(1)
+        val overrideDisplayId = 123
+        Thread {
+            assertFalse("Setting virtual pointer display should be unsuccessful",
+                localService.setVirtualMousePointerDisplayId(overrideDisplayId))
+            countDownLatch.countDown()
+        }.start()
+        assertFalse("Setting virtual pointer display should block",
+            countDownLatch.await(100, TimeUnit.MILLISECONDS))
+
+        val x = 42f
+        val y = 314f
+        // Assume the native callback updates the pointerDisplayId to the incorrect value.
+        service.onPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y)
+        testLooper.dispatchNext()
+        verify(wmCallbacks).notifyPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y)
+        assertTrue("Native callback unblocks calling thread",
+            countDownLatch.await(100, TimeUnit.MILLISECONDS))
+        verify(native).setPointerDisplayId(overrideDisplayId)
+    }
+
+    @Test
+    fun testSetVirtualMousePointerDisplayId_competingRequests() {
+        val firstRequestSyncLatch = CountDownLatch(1)
+        doAnswer {
+            firstRequestSyncLatch.countDown()
+        }.`when`(native).setPointerDisplayId(anyInt())
+
+        val firstRequestLatch = CountDownLatch(1)
+        val firstOverride = 123
+        Thread {
+            assertFalse("Setting virtual pointer display from thread 1 should be unsuccessful",
+                localService.setVirtualMousePointerDisplayId(firstOverride))
+            firstRequestLatch.countDown()
+        }.start()
+        assertFalse("Setting virtual pointer display should block",
+            firstRequestLatch.await(100, TimeUnit.MILLISECONDS))
+
+        assertTrue("Wait for first thread's request should succeed",
+            firstRequestSyncLatch.await(100, TimeUnit.MILLISECONDS))
+
+        val secondRequestLatch = CountDownLatch(1)
+        val secondOverride = 42
+        Thread {
+            assertTrue("Setting virtual mouse pointer from thread 2 should be successful",
+                localService.setVirtualMousePointerDisplayId(secondOverride))
+            secondRequestLatch.countDown()
+        }.start()
+        assertFalse("Setting virtual mouse pointer should block",
+            secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
+
+        val x = 42f
+        val y = 314f
+        // Assume the native callback updates directly to the second request.
+        service.onPointerDisplayIdChanged(secondOverride, x, y)
+        testLooper.dispatchNext()
+        verify(wmCallbacks).notifyPointerDisplayIdChanged(secondOverride, x, y)
+        assertTrue("Native callback unblocks first thread",
+            firstRequestLatch.await(100, TimeUnit.MILLISECONDS))
+        assertTrue("Native callback unblocks second thread",
+            secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
+        verify(native, times(2)).setPointerDisplayId(anyInt())
+    }
+
+    @Test
+    fun onDisplayRemoved_resetAllAdditionalInputProperties() {
+        localService.setVirtualMousePointerDisplayId(10)
+        localService.setPointerIconVisible(false, 10)
+        verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+        localService.setPointerAcceleration(5f, 10)
+        verify(native).setPointerAcceleration(eq(5f))
+
+        service.onDisplayRemoved(10)
+        verify(native).displayRemoved(eq(10))
+        verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
+        verify(native).setPointerAcceleration(
+            eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat()))
+
+        localService.setVirtualMousePointerDisplayId(10)
+        verify(native).setPointerDisplayId(eq(10))
+        verifyNoMoreInteractions(native)
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index a227cd3..035249e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -70,6 +70,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -516,6 +517,7 @@
         }
     }
 
+    @Ignore("Causing breakages so ignoring to resolve, b/231667368")
     @Test
     public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception {
         int uid = Binder.getCallingUid();
@@ -539,6 +541,7 @@
                 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2());
     }
 
+    @Ignore("Causing breakages so ignoring to resolve, b/231667368")
     @Test
     public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception {
         int uid = Binder.getCallingUid();
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 20486b3..8167b44 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -454,14 +454,14 @@
                         + "    <library \n"
                         + "        name=\"foo\"\n"
                         + "        file=\"" + mFooJar + "\"\n"
-                        + "        on-bootclasspath-before=\"Q\"\n"
+                        + "        on-bootclasspath-before=\"A\"\n"
                         + "        on-bootclasspath-since=\"W\"\n"
                         + "     />\n\n"
                         + " </permissions>";
         parseSharedLibraries(contents);
         assertFooIsOnlySharedLibrary();
         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
-        assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
+        assertThat(entry.onBootclasspathBefore).isEqualTo("A");
         assertThat(entry.onBootclasspathSince).isEqualTo("W");
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f59ec42..b943275 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -785,6 +785,34 @@
     }
 
     /**
+     * This test ensures that {@link ActivityStarter#setTargetRootTaskIfNeeded} will task the
+     * adjacent task of indicated launch target into account. So the existing task will be launched
+     * into closer target.
+     */
+    @Test
+    public void testAdjustLaunchTargetWithAdjacentTask() {
+        // Create adjacent tasks and put one activity under it
+        final Task parent = new TaskBuilder(mSupervisor).build();
+        final Task adjacentParent = new TaskBuilder(mSupervisor).build();
+        parent.setAdjacentTaskFragment(adjacentParent, true);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setParentTask(parent)
+                .setCreateTask(true).build();
+
+        // Launch the activity to its adjacent parent
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchRootTask(adjacentParent.mRemoteToken.toWindowContainerToken());
+        prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */)
+                .setReason("testAdjustLaunchTargetWithAdjacentTask")
+                .setIntent(activity.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Verify the activity will be launched into the original parent
+        assertTrue(activity.isDescendantOf(parent));
+    }
+
+    /**
      * This test ensures that {@link ActivityStarter#setTargetRootTaskIfNeeded} will
      * move the existing task to front if the current focused root task doesn't have running task.
      */
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index eb6395b..3592158 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,8 +39,10 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 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.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
@@ -48,7 +50,9 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 
 import android.graphics.Rect;
 import android.os.Binder;
@@ -376,7 +380,7 @@
         doReturn(false).when(dc).onDescendantOrientationChanged(any());
         final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                 dc, "exiting app");
-        final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord;
+        final ActivityRecord exitingActivity = exitingAppWindow.mActivityRecord;
         // Wait until everything in animation handler get executed to prevent the exiting window
         // from being removed during WindowSurfacePlacer Traversal.
         waitUntilHandlersIdle();
@@ -405,6 +409,41 @@
     }
 
     @Test
+    public void testDelayWhileRecents() {
+        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        doReturn(false).when(dc).onDescendantOrientationChanged(any());
+        final Task task = createTask(dc);
+
+        // Simulate activity1 launches activity2.
+        final ActivityRecord activity1 = createActivityRecord(task);
+        activity1.setVisible(true);
+        activity1.mVisibleRequested = false;
+        activity1.allDrawn = true;
+        final ActivityRecord activity2 = createActivityRecord(task);
+        activity2.setVisible(false);
+        activity2.mVisibleRequested = true;
+        activity2.allDrawn = true;
+
+        dc.mClosingApps.add(activity1);
+        dc.mOpeningApps.add(activity2);
+        dc.prepareAppTransition(TRANSIT_OPEN);
+        assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
+
+        // Wait until everything in animation handler get executed to prevent the exiting window
+        // from being removed during WindowSurfacePlacer Traversal.
+        waitUntilHandlersIdle();
+
+        // Start recents
+        doReturn(true).when(task)
+                .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
+
+        dc.mAppTransitionController.handleAppTransitionReady();
+
+        verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
+        verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
+    }
+
+    @Test
     public void testGetAnimationStyleResId() {
         // Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without
         // specifying window type.
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 49cd343..873d9f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -306,6 +306,7 @@
         if (focus) {
             doReturn(window.getWindowInfo().token)
                     .when(mWindowManagerInternal).getFocusedWindowToken();
+            doReturn(window).when(mWm).getFocusedWindowLocked();
         }
     }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index e3485de..ec94f8a 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -46,6 +46,14 @@
  * See {@link PhoneAccount}, {@link TelecomManager}.
  */
 public final class PhoneAccountHandle implements Parcelable {
+    /**
+     * Expected component name of Telephony phone accounts; ONLY used to determine if we should log
+     * the phone account handle ID.
+     */
+    private static final ComponentName TELEPHONY_COMPONENT_NAME =
+            new ComponentName("com.android.phone",
+                    "com.android.services.telephony.TelephonyConnectionService");
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196)
     private final ComponentName mComponentName;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -136,14 +144,23 @@
 
     @Override
     public String toString() {
-        // Note: Log.pii called for mId as it can contain personally identifying phone account
-        // information such as SIP account IDs.
-        return new StringBuilder().append(mComponentName)
-                    .append(", ")
-                    .append(Log.pii(mId))
-                    .append(", ")
-                    .append(mUserHandle)
-                    .toString();
+        StringBuilder sb = new StringBuilder()
+                .append(mComponentName)
+                .append(", ");
+
+        if (TELEPHONY_COMPONENT_NAME.equals(mComponentName)) {
+            // Telephony phone account handles are now keyed by subscription id which is not
+            // sensitive.
+            sb.append(mId);
+        } else {
+            // Note: Log.pii called for mId as it can contain personally identifying phone account
+            // information such as SIP account IDs.
+            sb.append(Log.pii(mId));
+        }
+        sb.append(", ");
+        sb.append(mUserHandle);
+
+        return sb.toString();
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e21301e..70fe6b1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4980,6 +4980,25 @@
                 KEY_PREFIX + "use_sip_uri_for_presence_subscribe_bool";
 
         /**
+         * Flag indicating whether or not to use TEL URI when setting the entity uri field and
+         * contact element of each tuple.
+         *
+         * When {@code true}, the device sets the entity uri field and contact element to be
+         * TEL URI. This is done by first searching for the first TEL URI provided in
+         * p-associated-uri header. If there are no TEL URIs in the p-associated-uri header, we will
+         * convert the first SIP URI provided in the header to a TEL URI. If there are no URIs in
+         * the p-associated-uri header, we will then fall back to using the SIM card to generate the
+         * TEL URI.
+         * If {@code false}, the first URI provided in the p-associated-uri header is used,
+         * independent of the URI scheme. If there are no URIs available from p-associated-uri
+         * header, we will try to generate a SIP URI or TEL URI from the information provided by the
+         * SIM card, depending on the information available.
+         * @hide
+         */
+        public static final String KEY_USE_TEL_URI_FOR_PIDF_XML_BOOL =
+                KEY_PREFIX + "use_tel_uri_for_pidf_xml";
+
+        /**
          * An integer key associated with the period of time in seconds the non-rcs capability
          * information of each contact is cached on the device.
          * <p>
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index fa6de1a..ac1f376 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1673,4 +1673,9 @@
             return UNKNOWN;
         }
     }
+
+    /** @hide */
+    public static boolean isFailCauseExisting(@DataFailureCause int failCause) {
+        return sFailCauseMap.containsKey(failCause);
+    }
 }
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index d4fa1dd..62e16a5 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -37,8 +37,8 @@
         "general-tests",
         "vts",
     ],
-    data_device_bins: [
-        "block_device_writer",
+    target_required: [
+        "block_device_writer_module",
     ],
     data: [
         ":ApkVerityTestCertDer",
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index e5d009d..fdfa41f 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -24,7 +24,12 @@
 }
 
 cc_test {
-    name: "block_device_writer",
+    // Depending on how the test runs, the executable may be uploaded to different location.
+    // Before the bug in the file pusher is fixed, workaround by making the name unique.
+    // See b/124718249#comment12.
+    name: "block_device_writer_module",
+    stem: "block_device_writer",
+
     srcs: ["block_device_writer.cpp"],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
@@ -37,7 +42,22 @@
         "libbase",
         "libutils",
     ],
-    compile_multilib: "first",
+    // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when
+    // the uploader does not pick up the executable from correct output location. The following
+    // workaround allows the test to:
+    //  * upload the 32-bit exectuable for both 32 and 64 bits devices to use
+    //  * refer to the same executable name in Java
+    //  * no need to force the Java test to be archiecture specific.
+    //
+    // See b/145573317 for details.
+    multilib: {
+        lib32: {
+            suffix: "",
+        },
+        lib64: {
+            suffix: "64", // not really used
+        },
+    },
 
     auto_gen_config: false,
     test_suites: [
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
index 730daf3..5c2c15b 100644
--- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -32,7 +32,7 @@
  * <p>To use this class, please push block_device_writer binary to /data/local/tmp.
  * 1. In Android.bp, add:
  * <pre>
- *      data_device_bins: ["block_device_writer"],
+ *     target_required: ["block_device_writer_module"],
  * </pre>
  * 2. In AndroidText.xml, add:
  * <pre>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
new file mode 100644
index 0000000..16c4c25
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
+
+class ImeStateInitializeHelper @JvmOverloads constructor(
+    instr: Instrumentation,
+    launcherName: String = ActivityOptions.IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME,
+    component: FlickerComponentName =
+        ActivityOptions.IME_ACTIVITY_INITIALIZE_COMPONENT_NAME.toFlickerComponent(),
+    launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+            .getInstance(instr)
+            .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
index 9643909..b897ca2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -28,6 +28,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.traces.common.FlickerComponentName
 import org.junit.FixMethodOrder
@@ -71,17 +72,20 @@
 class LaunchAppShowImeOnStartTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+    private val initializeApp = ImeStateInitializeHelper(instrumentation)
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
             setup {
                 eachRun {
+                    initializeApp.launchViaIntent()
                     this.setRotation(testSpec.startRotation)
                 }
             }
             teardown {
                 eachRun {
+                    initializeApp.exit()
                     testApp.exit()
                 }
             }
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 2842232..43aa4b1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -52,6 +52,16 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".ImeStateInitializeActivity"
+                  android:theme="@style/no_starting_window"
+                  android:windowSoftInputMode="stateAlwaysHidden"
+                  android:label="ImeStateInitializeActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
         <activity android:name=".SeamlessRotationActivity"
              android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
              android:theme="@style/CutoutShortEdges"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 589df38..1d21fd5 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -42,4 +42,8 @@
         <item name="android:backgroundDimEnabled">false</item>
         <item name="android:windowSoftInputMode">stateUnchanged</item>
     </style>
+
+    <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowDisablePreview">true</item>
+    </style>
 </resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index e080709..6cda482 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -37,6 +37,11 @@
             new ComponentName(FLICKER_APP_PACKAGE,
                     FLICKER_APP_PACKAGE + ".ImeActivity");
 
+    public static final String IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME = "ImeStateInitializeActivity";
+    public static final ComponentName IME_ACTIVITY_INITIALIZE_COMPONENT_NAME =
+            new ComponentName(FLICKER_APP_PACKAGE,
+                    FLICKER_APP_PACKAGE + ".ImeStateInitializeActivity");
+
     public static final String SIMPLE_ACTIVITY_LAUNCHER_NAME = "SimpleApp";
     public static final ComponentName SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
             new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java
new file mode 100644
index 0000000..4be79c4
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A nop {@link Activity} to make sure that the test starts from a deterministic state.
+ *
+ * <p>Currently this {@link Activity} makes sure the following things</p>
+ * <li>
+ *     <ul>Hide the software keyboard with
+ *     {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}</ul>
+ *     <ul>Make sure that the navigation bar (if supported) is rendered with {@link Color#BLACK}.
+ *     </ul>
+ * </li>
+ */
+public class ImeStateInitializeActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        final View view = new View(this);
+        view.setBackgroundColor(Color.WHITE);
+        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+
+        // Make sure that navigation bar is rendered with black (if supported).
+        getWindow().setNavigationBarColor(Color.BLACK);
+
+        setContentView(view);
+    }
+}
diff --git a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
index 3c6d54d..ae72247 100644
--- a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
@@ -29,7 +29,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import androidx.test.uiautomator.UiDevice
-import com.google.common.truth.Truth.assertThat
+import android.trust.test.lib.wait
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -74,9 +74,9 @@
         uiDevice.sleep()
         lockStateTrackingRule.assertLocked()
 
+        uiDevice.wakeUp()
         trustAgentRule.agent.grantTrust(
             GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
-        uiDevice.wakeUp()
 
         lockStateTrackingRule.assertLocked()
     }
@@ -98,9 +98,9 @@
 
         lockStateTrackingRule.assertLocked()
 
+        uiDevice.wakeUp()
         trustAgentRule.agent.grantTrust(
             GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
-        uiDevice.wakeUp()
 
         lockStateTrackingRule.assertUnlocked()
     }
@@ -116,6 +116,7 @@
         uiDevice.sleep()
 
         lockStateTrackingRule.assertLocked()
+        uiDevice.wakeUp()
 
         Log.i(TAG, "Renewing trust and unlocking")
         var result: GrantTrustResult? = null
@@ -124,10 +125,9 @@
             Log.i(TAG, "Callback received; status=${it.status}")
             result = it
         }
-        uiDevice.wakeUp()
         lockStateTrackingRule.assertUnlocked()
 
-        assertThat(result?.status).isEqualTo(STATUS_UNLOCKED_BY_GRANT)
+        wait("callback triggered") { result?.status == STATUS_UNLOCKED_BY_GRANT }
     }
 
     @Test
@@ -141,7 +141,6 @@
         trustAgentRule.agent.revokeTrust()
         await(500)
         uiDevice.wakeUp()
-        await(500)
 
         trustAgentRule.agent.grantTrust(
             GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}