Merge "Modify current KCM files to support shift+capslock" into udc-dev
diff --git a/Android.bp b/Android.bp
index bc6cda3..cff863b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -343,6 +343,7 @@
             "hardware/interfaces/biometrics/fingerprint/aidl",
             "hardware/interfaces/graphics/common/aidl",
             "hardware/interfaces/keymaster/aidl",
+            "system/hardware/interfaces/media/aidl",
         ],
     },
     dxflags: [
@@ -632,6 +633,7 @@
             "hardware/interfaces/biometrics/fingerprint/aidl",
             "hardware/interfaces/graphics/common/aidl",
             "hardware/interfaces/keymaster/aidl",
+            "system/hardware/interfaces/media/aidl",
         ],
     },
     // These are libs from framework-internal-utils that are required (i.e. being referenced)
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 28d4433..2751b54 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6657,12 +6657,13 @@
             // Setup a location to store generated/compiled graphics code.
             final Context deviceContext = context.createDeviceProtectedStorageContext();
             final File codeCacheDir = deviceContext.getCodeCacheDir();
-            if (codeCacheDir != null) {
+            final File deviceCacheDir = deviceContext.getCacheDir();
+            if (codeCacheDir != null && deviceCacheDir != null) {
                 try {
                     int uid = Process.myUid();
                     String[] packages = getPackageManager().getPackagesForUid(uid);
                     if (packages != null) {
-                        HardwareRenderer.setupDiskCache(codeCacheDir);
+                        HardwareRenderer.setupDiskCache(deviceCacheDir);
                         RenderScriptCacheDir.setupDiskCache(codeCacheDir);
                     }
                 } catch (RemoteException e) {
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 0ef8e92..09450f5 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -155,6 +155,14 @@
             "android.app.extra.REMOTE_LOCKSCREEN_VALIDATION_SESSION";
 
     /**
+     * A boolean indicating that credential confirmation activity should be a task overlay.
+     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER}.
+     * @hide
+     */
+    public static final String EXTRA_FORCE_TASK_OVERLAY =
+            "android.app.KeyguardManager.FORCE_TASK_OVERLAY";
+
+    /**
      * Result code returned by the activity started by
      * {@link #createConfirmFactoryResetCredentialIntent} or
      * {@link #createConfirmDeviceCredentialForRemoteValidationIntent}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 440ee20..e78fb17 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8657,13 +8657,13 @@
              * where the platform doesn't support the MIME type, the original text provided in the
              * constructor will be used.
              * @param dataMimeType The MIME type of the content. See
-             * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
-             * types on Android and Android Wear.
+             * {@link android.graphics.ImageDecoder#isMimeTypeSupported(String)} for a list of
+             * supported image MIME types.
              * @param dataUri The uri containing the content whose type is given by the MIME type.
              * <p class="note">
+             * Notification Listeners including the System UI need permission to access the
+             * data the Uri points to. The recommended ways to do this are:
              * <ol>
-             *   <li>Notification Listeners including the System UI need permission to access the
-             *       data the Uri points to. The recommended ways to do this are:</li>
              *   <li>Store the data in your own ContentProvider, making sure that other apps have
              *       the correct permission to access your provider. The preferred mechanism for
              *       providing access is to use per-URI permissions which are temporary and only
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index bf34c1c..a23d7e4 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -42,7 +42,7 @@
     private final String mType;
 
     /**
-     * The flattened JSON string that will be matched with requests.
+     * Flattened semicolon separated keys of JSON values to match with requests.
      */
     @NonNull
     private final String mFlattenedRequestString;
@@ -57,7 +57,8 @@
      * Constructs a {@link CredentialDescription}.
      *
      * @param type the type of the credential returned.
-     * @param flattenedRequestString flattened JSON string that will be matched with requests.
+     * @param flattenedRequestString flattened semicolon separated keys of JSON values
+     *                              to match with requests.
      * @param credentialEntries a list of {@link CredentialEntry}s that are to be shown on the
      *                          account selector if a credential matches with this description.
      *                          Each entry contains information to be displayed within an
@@ -151,4 +152,29 @@
     public List<CredentialEntry> getCredentialEntries() {
         return mCredentialEntries;
     }
+
+    /**
+     * {@link CredentialDescription#mType} and
+     * {@link CredentialDescription#mFlattenedRequestString} are enough for hashing. Constructor
+     * enforces {@link CredentialEntry} to have the same type and
+     * {@link android.app.slice.Slice} contained by the entry can not be hashed.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mFlattenedRequestString);
+    }
+
+    /**
+     * {@link CredentialDescription#mType} and
+     * {@link CredentialDescription#mFlattenedRequestString} are enough for equality check.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof CredentialDescription)) {
+            return false;
+        }
+        CredentialDescription other = (CredentialDescription) obj;
+        return mType.equals(other.mType)
+                && mFlattenedRequestString.equals(other.mFlattenedRequestString);
+    }
 }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 722dd08..e6b3069 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -125,6 +125,23 @@
             "camera.enable_landscape_to_portrait";
 
     /**
+     * Enable physical camera availability callbacks when the logical camera is unavailable
+     *
+     * <p>Previously once a logical camera becomes unavailable, no {@link
+     * #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable} will be called until
+     * the logical camera becomes available again. The results in the app opening the logical
+     * camera not able to receive physical camera availability change.</p>
+     *
+     * <p>With this change, the {@link #onPhysicalCameraAvailable} and {@link
+     * #onPhysicalCameraUnavailable} can still be called while the logical camera is unavailable.
+     * </p>
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    private static final long ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA =
+            244358506L;
+
+    /**
      * @hide
      */
     public CameraManager(Context context) {
@@ -1194,6 +1211,14 @@
     }
 
     /**
+     * @hide
+     */
+    public static boolean physicalCallbacksAreEnabledForUnavailableCamera() {
+        return CompatChanges.isChangeEnabled(
+                ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA);
+    }
+
+    /**
      * A callback for camera devices becoming available or unavailable to open.
      *
      * <p>Cameras become available when they are no longer in use, or when a new
@@ -1270,9 +1295,10 @@
          * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
          * {@link #onCameraAvailable}.</p>
          *
-         * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable}
-         * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example,
-         * if app A opens the camera device:</p>
+         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
+         * &lt; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera
+         * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable}
+         * callbacks for its physical cameras. For example, if app A opens the camera device:</p>
          *
          * <ul>
          *
@@ -1284,6 +1310,33 @@
          *
          * </ul>
          *
+         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
+         * &ge; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p>
+         *
+         * <ul>
+         *
+         * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable}
+         * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes
+         * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the
+         * physical cameras' availability status. This makes it possible for an application opening
+         * the logical camera device to know which physical camera becomes unavailable or available
+         * to use.</li>
+         *
+         * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier,
+         * the logical camera's {@link #onCameraAvailable} callback implies all of its physical
+         * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called
+         * for any unavailable physical cameras upon the logical camera becoming available.</li>
+         *
+         * </ul>
+         *
+         * <p>Given the pipeline nature of the camera capture through {@link
+         * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application
+         * requests images from a physical camera of a logical multi-camera and that physical camera
+         * becomes unavailable. The application should stop requesting directly from an unavailable
+         * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be
+         * ready to robustly handle frame drop errors for requests targeting physical cameras,
+         * since those errors may arrive before the unavailability callback.</p>
+         *
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param cameraId The unique identifier of the logical multi-camera.
@@ -1306,9 +1359,10 @@
          * cameras of its parent logical multi-camera, when {@link #onCameraUnavailable} for
          * the logical multi-camera is invoked.</p>
          *
-         * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable}
-         * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example,
-         * if app A opens the camera device:</p>
+         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
+         * &lt; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera
+         * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable}
+         * callbacks for its physical cameras. For example, if app A opens the camera device:</p>
          *
          * <ul>
          *
@@ -1320,6 +1374,33 @@
          *
          * </ul>
          *
+         * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion}
+         * &ge; {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p>
+         *
+         * <ul>
+         *
+         * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable}
+         * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes
+         * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the
+         * physical cameras' availability status. This makes it possible for an application opening
+         * the logical camera device to know which physical camera becomes unavailable or available
+         * to use.</li>
+         *
+         * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier,
+         * the logical camera's {@link #onCameraAvailable} callback implies all of its physical
+         * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called
+         * for any unavailable physical cameras upon the logical camera becoming available.</li>
+         *
+         * </ul>
+         *
+         * <p>Given the pipeline nature of the camera capture through {@link
+         * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application
+         * requests images from a physical camera of a logical multi-camera and that physical camera
+         * becomes unavailable. The application should stop requesting directly from an unavailable
+         * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be
+         * ready to robustly handle frame drop errors for requests targeting physical cameras,
+         * since those errors may arrive before the unavailability callback.</p>
+         *
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param cameraId The unique identifier of the logical multi-camera.
@@ -2283,7 +2364,8 @@
                 postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
 
                 // Send the NOT_PRESENT state for unavailable physical cameras
-                if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) {
+                if ((isAvailable(status) || physicalCallbacksAreEnabledForUnavailableCamera())
+                        && mUnavailablePhysicalDevices.containsKey(id)) {
                     ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id);
                     for (String unavailableId : unavailableIds) {
                         postSingleUpdate(callback, executor, id, unavailableId,
@@ -2416,7 +2498,8 @@
                 return;
             }
 
-            if (!isAvailable(mDeviceStatus.get(id))) {
+            if (!physicalCallbacksAreEnabledForUnavailableCamera()
+                    && !isAvailable(mDeviceStatus.get(id))) {
                 Log.i(TAG, String.format("Camera %s is not available. Ignore physical camera "
                         + "status change callback(s)", id));
                 return;
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index db83e62..2fa8b87 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -101,15 +101,15 @@
 
 
     // Lock to synchronize cross-thread access to device public interface
-    final Object mInterfaceLock = new Object(); // access from this class and Session only!
+    final Object mInterfaceLock;
 
     /**
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.CAMERA)
     public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
-            @NonNull CameraDevice cameraDevice, @NonNull Context ctx,
-            @NonNull ExtensionSessionConfiguration config, int sessionId)
+            @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
+            @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId)
             throws CameraAccessException, RemoteException {
         long clientId = CameraExtensionCharacteristics.registerClient(ctx);
         if (clientId < 0) {
@@ -209,7 +209,8 @@
     }
 
     private CameraAdvancedExtensionSessionImpl(long extensionClientId,
-            @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
+            @NonNull IAdvancedExtenderImpl extender,
+            @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
             @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
             @Nullable Surface postviewSurface,
             @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor,
@@ -228,6 +229,7 @@
         mInitialized = false;
         mInitializeHandler = new InitializeSessionHandler();
         mSessionId = sessionId;
+        mInterfaceLock = cameraDevice.mInterfaceLock;
     }
 
     /**
@@ -599,13 +601,14 @@
         public void onConfigured(@NonNull CameraCaptureSession session) {
             synchronized (mInterfaceLock) {
                 mCaptureSession = session;
-                try {
-                    CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to initialize session! Extension service does"
-                            + " not respond!");
-                    notifyConfigurationFailure();
-                }
+            }
+
+            try {
+                CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to initialize session! Extension service does"
+                        + " not respond!");
+                notifyConfigurationFailure();
             }
         }
     }
@@ -613,46 +616,56 @@
     private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
         @Override
         public void onSuccess() {
-            boolean status = true;
-            synchronized (mInterfaceLock) {
-                try {
-                    if (mSessionProcessor != null) {
-                        mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
-                        mInitialized = true;
-                    } else {
-                        Log.v(TAG, "Failed to start capture session, session released before " +
-                                "extension start!");
-                        status = false;
-                        mCaptureSession.close();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    boolean status = true;
+                    synchronized (mInterfaceLock) {
+                        try {
+                            if (mSessionProcessor != null) {
+                                mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
+                                mInitialized = true;
+                            } else {
+                                Log.v(TAG, "Failed to start capture session, session " +
+                                                " released before extension start!");
+                                status = false;
+                            }
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Failed to start capture session,"
+                                    + " extension service does not respond!");
+                            status = false;
+                            mInitialized = false;
+                        }
                     }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to start capture session,"
-                            + " extension service does not respond!");
-                    status = false;
-                    mCaptureSession.close();
-                }
-            }
 
-            if (status) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    mExecutor.execute(
-                            () -> mCallbacks.onConfigured(CameraAdvancedExtensionSessionImpl.this));
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
+                    if (status) {
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            mExecutor.execute(() -> mCallbacks.onConfigured(
+                                    CameraAdvancedExtensionSessionImpl.this));
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
+                    } else {
+                        onFailure();
+                    }
                 }
-            } else {
-                notifyConfigurationFailure();
-            }
+            });
         }
 
         @Override
         public void onFailure() {
-            mCaptureSession.close();
-            Log.e(TAG, "Failed to initialize proxy service session!"
-                    + " This can happen when trying to configure multiple "
-                    + "concurrent extension sessions!");
-            notifyConfigurationFailure();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCaptureSession.close();
+
+                    Log.e(TAG, "Failed to initialize proxy service session!"
+                            + " This can happen when trying to configure multiple "
+                            + "concurrent extension sessions!");
+                    notifyConfigurationFailure();
+                }
+            });
         }
     }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index c2b3656..9c878c7 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -117,7 +117,7 @@
     private boolean mInternalRepeatingRequestEnabled = true;
 
     // Lock to synchronize cross-thread access to device public interface
-    final Object mInterfaceLock = new Object(); // access from this class and Session only!
+    final Object mInterfaceLock;
 
     private static int nativeGetSurfaceFormat(Surface surface) {
         return SurfaceUtils.getSurfaceFormat(surface);
@@ -128,7 +128,7 @@
      */
     @RequiresPermission(android.Manifest.permission.CAMERA)
     public static CameraExtensionSessionImpl createCameraExtensionSession(
-            @NonNull CameraDevice cameraDevice,
+            @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
             @NonNull Context ctx,
             @NonNull ExtensionSessionConfiguration config,
             int sessionId)
@@ -251,7 +251,7 @@
             @NonNull IPreviewExtenderImpl previewExtender,
             @NonNull List<Size> previewSizes,
             long extensionClientId,
-            @NonNull CameraDevice cameraDevice,
+            @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
             @Nullable Surface repeatingRequestSurface,
             @Nullable Surface burstCaptureSurface,
             @Nullable Surface postviewSurface,
@@ -279,6 +279,7 @@
         mSupportedRequestKeys = requestKeys;
         mSupportedResultKeys = resultKeys;
         mCaptureResultsSupported = !resultKeys.isEmpty();
+        mInterfaceLock = cameraDevice.mInterfaceLock;
     }
 
     private void initializeRepeatingRequestPipeline() throws RemoteException {
@@ -969,46 +970,56 @@
     private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
         @Override
         public void onSuccess() {
-            boolean status = true;
-            ArrayList<CaptureStageImpl> initialRequestList =
-                    compileInitialRequestList();
-            if (!initialRequestList.isEmpty()) {
-                try {
-                    setInitialCaptureRequest(initialRequestList,
-                            new InitialRequestHandler(
-                                    mRepeatingRequestImageCallback));
-                } catch (CameraAccessException e) {
-                    Log.e(TAG,
-                            "Failed to initialize the initial capture "
-                                    + "request!");
-                    status = false;
-                }
-            } else {
-                try {
-                    setRepeatingRequest(mPreviewExtender.getCaptureStage(),
-                            new PreviewRequestHandler(null, null, null,
-                                    mRepeatingRequestImageCallback));
-                } catch (CameraAccessException | RemoteException e) {
-                    Log.e(TAG,
-                            "Failed to initialize internal repeating "
-                                    + "request!");
-                    status = false;
-                }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    boolean status = true;
+                    ArrayList<CaptureStageImpl> initialRequestList =
+                            compileInitialRequestList();
+                    if (!initialRequestList.isEmpty()) {
+                        try {
+                            setInitialCaptureRequest(initialRequestList,
+                                    new InitialRequestHandler(
+                                            mRepeatingRequestImageCallback));
+                        } catch (CameraAccessException e) {
+                            Log.e(TAG,
+                                    "Failed to initialize the initial capture "
+                                            + "request!");
+                            status = false;
+                        }
+                    } else {
+                        try {
+                            setRepeatingRequest(mPreviewExtender.getCaptureStage(),
+                                    new PreviewRequestHandler(null, null, null,
+                                            mRepeatingRequestImageCallback));
+                        } catch (CameraAccessException | RemoteException e) {
+                            Log.e(TAG,
+                                    "Failed to initialize internal repeating "
+                                            + "request!");
+                            status = false;
+                        }
 
-            }
+                    }
 
-            if (!status) {
-                notifyConfigurationFailure();
-            }
+                    if (!status) {
+                        notifyConfigurationFailure();
+                    }
+                }
+            });
         }
 
         @Override
         public void onFailure() {
-            mCaptureSession.close();
-            Log.e(TAG, "Failed to initialize proxy service session!"
-                    + " This can happen when trying to configure multiple "
-                    + "concurrent extension sessions!");
-            notifyConfigurationFailure();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCaptureSession.close();
+                    Log.e(TAG, "Failed to initialize proxy service session!"
+                            + " This can happen when trying to configure multiple "
+                            + "concurrent extension sessions!");
+                    notifyConfigurationFailure();
+                }
+            });
         }
     }
 
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index a47783c..6b60442 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -205,6 +205,16 @@
     }
 
     /**
+     * Notify HandwritingInitiator that a delegate view (see {@link View#isHandwritingDelegate})
+     * gained focus.
+     */
+    public void onDelegateViewFocused(@NonNull View view) {
+        if (view == getConnectedView()) {
+            tryAcceptStylusHandwritingDelegation(view);
+        }
+    }
+
+    /**
      * Notify HandwritingInitiator that a new InputConnection is created.
      * The caller of this method should guarantee that each onInputConnectionCreated call
      * is paired with a onInputConnectionClosed call.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 800fc97..aec3487 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8324,6 +8324,13 @@
             onFocusLost();
         } else if (hasWindowFocus()) {
             notifyFocusChangeToImeFocusController(true /* hasFocus */);
+
+            if (mIsHandwritingDelegate) {
+                ViewRootImpl viewRoot = getViewRootImpl();
+                if (viewRoot != null) {
+                    viewRoot.getHandwritingInitiator().onDelegateViewFocused(this);
+                }
+            }
         }
 
         invalidate(true);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 24dcb69..fb25e7a6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -11626,7 +11626,8 @@
 
         mNumPausedForSync++;
         mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
-        mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, 1000);
+        mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT,
+                1000 * Build.HW_TIMEOUT_MULTIPLIER);
         return mActiveSurfaceSyncGroup;
     };
 
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 0672d63..7f99fb7 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -21,6 +21,7 @@
 import android.annotation.UiThread;
 import android.os.Binder;
 import android.os.BinderProxy;
+import android.os.Build;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -62,7 +63,7 @@
     private static final int MAX_COUNT = 100;
 
     private static final AtomicInteger sCounter = new AtomicInteger(0);
-    private static final int TRANSACTION_READY_TIMEOUT = 1000;
+    private static final int TRANSACTION_READY_TIMEOUT = 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     private static Supplier<Transaction> sTransactionFactory = Transaction::new;
 
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index a3209f6..fabb089 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -131,6 +131,19 @@
     }
 
     /**
+     * Sets the densityDpi value in the configuration for the given container.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setDensityDpi(@NonNull WindowContainerToken container,
+            int densityDpi) {
+        Change chg = getOrCreateChange(container.asBinder());
+        chg.mConfiguration.densityDpi = densityDpi;
+        chg.mConfigSetMask |= ActivityInfo.CONFIG_DENSITY;
+        return this;
+    }
+
+    /**
      * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task
      * has finished the enter animation with the given bounds.
      */
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index bce0d60..f6bcc46 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -27,12 +27,12 @@
 import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON;
 import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
-import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP_OR_FGS;
-import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
-import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
-import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_FGS;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED;
 import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
 import static com.android.internal.app.procstats.ProcessStats.STATE_FGS;
+import static com.android.internal.app.procstats.ProcessStats.STATE_FROZEN;
 import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
 import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
 import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
@@ -72,7 +72,8 @@
         STATE_NAMES = new String[STATE_COUNT];
         STATE_NAMES[STATE_PERSISTENT]               = "Persist";
         STATE_NAMES[STATE_TOP]                      = "Top";
-        STATE_NAMES[STATE_BOUND_TOP_OR_FGS]         = "BTopFgs";
+        STATE_NAMES[STATE_BOUND_FGS]                = "BFgs";
+        STATE_NAMES[STATE_BOUND_TOP]                = "BTop";
         STATE_NAMES[STATE_FGS]                      = "Fgs";
         STATE_NAMES[STATE_IMPORTANT_FOREGROUND]     = "ImpFg";
         STATE_NAMES[STATE_IMPORTANT_BACKGROUND]     = "ImpBg";
@@ -83,14 +84,14 @@
         STATE_NAMES[STATE_HEAVY_WEIGHT]             = "HeavyWt";
         STATE_NAMES[STATE_HOME]                     = "Home";
         STATE_NAMES[STATE_LAST_ACTIVITY]            = "LastAct";
-        STATE_NAMES[STATE_CACHED_ACTIVITY]          = "CchAct";
-        STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT]   = "CchCAct";
-        STATE_NAMES[STATE_CACHED_EMPTY]             = "CchEmty";
+        STATE_NAMES[STATE_CACHED]                   = "Cached";
+        STATE_NAMES[STATE_FROZEN]                   = "Frozen";
 
         STATE_LABELS = new String[STATE_COUNT];
         STATE_LABELS[STATE_PERSISTENT]              = "Persistent";
         STATE_LABELS[STATE_TOP]                     = "       Top";
-        STATE_LABELS[STATE_BOUND_TOP_OR_FGS]        = "Bnd TopFgs";
+        STATE_LABELS[STATE_BOUND_FGS]               = "   Bnd Fgs";
+        STATE_LABELS[STATE_BOUND_TOP]               = "   Bnd Top";
         STATE_LABELS[STATE_FGS]                     = "       Fgs";
         STATE_LABELS[STATE_IMPORTANT_FOREGROUND]    = "    Imp Fg";
         STATE_LABELS[STATE_IMPORTANT_BACKGROUND]    = "    Imp Bg";
@@ -101,16 +102,16 @@
         STATE_LABELS[STATE_HEAVY_WEIGHT]            = " Heavy Wgt";
         STATE_LABELS[STATE_HOME]                    = "    (Home)";
         STATE_LABELS[STATE_LAST_ACTIVITY]           = "(Last Act)";
-        STATE_LABELS[STATE_CACHED_ACTIVITY]         = " (Cch Act)";
-        STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT]  = "(Cch CAct)";
-        STATE_LABELS[STATE_CACHED_EMPTY]            = "(Cch Emty)";
+        STATE_LABELS[STATE_CACHED]                  = "  (Cached)";
+        STATE_LABELS[STATE_FROZEN]                  = "    Frozen";
         STATE_LABEL_CACHED                          = "  (Cached)";
         STATE_LABEL_TOTAL                           = "     TOTAL";
 
         STATE_NAMES_CSV = new String[STATE_COUNT];
         STATE_NAMES_CSV[STATE_PERSISTENT]               = "pers";
         STATE_NAMES_CSV[STATE_TOP]                      = "top";
-        STATE_NAMES_CSV[STATE_BOUND_TOP_OR_FGS]         = "btopfgs";
+        STATE_NAMES_CSV[STATE_BOUND_FGS]                = "bfgs";
+        STATE_NAMES_CSV[STATE_BOUND_TOP]                = "btop";
         STATE_NAMES_CSV[STATE_FGS]                      = "fgs";
         STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND]     = "impfg";
         STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND]     = "impbg";
@@ -121,14 +122,14 @@
         STATE_NAMES_CSV[STATE_HEAVY_WEIGHT]             = "heavy";
         STATE_NAMES_CSV[STATE_HOME]                     = "home";
         STATE_NAMES_CSV[STATE_LAST_ACTIVITY]            = "lastact";
-        STATE_NAMES_CSV[STATE_CACHED_ACTIVITY]          = "cch-activity";
-        STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT]   = "cch-aclient";
-        STATE_NAMES_CSV[STATE_CACHED_EMPTY]             = "cch-empty";
+        STATE_NAMES_CSV[STATE_CACHED]                   = "cached";
+        STATE_NAMES_CSV[STATE_FROZEN]                   = "frzn";
 
         STATE_TAGS = new String[STATE_COUNT];
         STATE_TAGS[STATE_PERSISTENT]                = "p";
         STATE_TAGS[STATE_TOP]                       = "t";
-        STATE_TAGS[STATE_BOUND_TOP_OR_FGS]          = "d";
+        STATE_TAGS[STATE_BOUND_FGS]                 = "y";
+        STATE_TAGS[STATE_BOUND_TOP]                 = "z";
         STATE_TAGS[STATE_FGS]                       = "g";
         STATE_TAGS[STATE_IMPORTANT_FOREGROUND]      = "f";
         STATE_TAGS[STATE_IMPORTANT_BACKGROUND]      = "b";
@@ -139,15 +140,14 @@
         STATE_TAGS[STATE_HEAVY_WEIGHT]              = "w";
         STATE_TAGS[STATE_HOME]                      = "h";
         STATE_TAGS[STATE_LAST_ACTIVITY]             = "l";
-        STATE_TAGS[STATE_CACHED_ACTIVITY]           = "a";
-        STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT]    = "c";
-        STATE_TAGS[STATE_CACHED_EMPTY]              = "e";
+        STATE_TAGS[STATE_CACHED]                    = "a";
+        STATE_TAGS[STATE_FROZEN]                    = "e";
 
         STATE_PROTO_ENUMS = new int[STATE_COUNT];
         STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsEnums.PROCESS_STATE_PERSISTENT;
         STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsEnums.PROCESS_STATE_TOP;
-        STATE_PROTO_ENUMS[STATE_BOUND_TOP_OR_FGS] =
-                ProcessStatsEnums.PROCESS_STATE_BOUND_TOP_OR_FGS;
+        STATE_PROTO_ENUMS[STATE_BOUND_FGS] = ProcessStatsEnums.PROCESS_STATE_BOUND_FGS;
+        STATE_PROTO_ENUMS[STATE_BOUND_TOP] = ProcessStatsEnums.PROCESS_STATE_BOUND_TOP;
         STATE_PROTO_ENUMS[STATE_FGS] = ProcessStatsEnums.PROCESS_STATE_FGS;
         STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] =
                 ProcessStatsEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
@@ -161,10 +161,8 @@
         STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsEnums.PROCESS_STATE_HEAVY_WEIGHT;
         STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsEnums.PROCESS_STATE_HOME;
         STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_LAST_ACTIVITY;
-        STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY;
-        STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
-                ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
-        STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
+        STATE_PROTO_ENUMS[STATE_CACHED] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY;
+        STATE_PROTO_ENUMS[STATE_FROZEN] = ProcessStatsEnums.PROCESS_STATE_FROZEN;
 
         // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data
         // aggregation / size reduction purposes.
@@ -173,7 +171,9 @@
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT;
         PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] =
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP;
-        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_TOP_OR_FGS] =
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_FGS] =
+                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS;
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_TOP] =
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS;
         PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_FGS] =
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_FGS;
@@ -196,11 +196,9 @@
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
         PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] =
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
-        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] =
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED] =
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
-        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] =
-                ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
-        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] =
+        PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_FROZEN] =
                 ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
     }
 
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 818a503..fff778c 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -28,10 +28,9 @@
 import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
 import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
 import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
-import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP_OR_FGS;
-import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
-import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
-import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_FGS;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED;
 import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
 import static com.android.internal.app.procstats.ProcessStats.STATE_FGS;
 import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
@@ -85,9 +84,9 @@
         STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
         STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
-        STATE_BOUND_TOP_OR_FGS,         // ActivityManager.PROCESS_STATE_BOUND_TOP
+        STATE_BOUND_TOP,                // ActivityManager.PROCESS_STATE_BOUND_TOP
         STATE_FGS,                      // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
-        STATE_BOUND_TOP_OR_FGS,         // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+        STATE_BOUND_FGS,                // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
         STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
         STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
@@ -98,10 +97,10 @@
         STATE_HEAVY_WEIGHT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
         STATE_HOME,                     // ActivityManager.PROCESS_STATE_HOME
         STATE_LAST_ACTIVITY,            // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
-        STATE_CACHED_ACTIVITY,          // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
-        STATE_CACHED_ACTIVITY_CLIENT,   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
-        STATE_CACHED_ACTIVITY,          // ActivityManager.PROCESS_STATE_CACHED_RECENT
-        STATE_CACHED_EMPTY,             // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+        STATE_CACHED,                   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+        STATE_CACHED,                   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+        STATE_CACHED,                   // ActivityManager.PROCESS_STATE_CACHED_RECENT
+        STATE_CACHED,                   // ActivityManager.PROCESS_STATE_CACHED_EMPTY
     };
 
     public static final Comparator<ProcessState> COMPARATOR = new Comparator<ProcessState>() {
@@ -926,8 +925,11 @@
                 screenStates, memStates, new int[] { STATE_PERSISTENT }, now, totalTime, true);
         dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_TOP],
                 screenStates, memStates, new int[] {STATE_TOP}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_TOP_OR_FGS],
-                screenStates, memStates, new int[] { STATE_BOUND_TOP_OR_FGS}, now, totalTime,
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_TOP],
+                screenStates, memStates, new int[] { STATE_BOUND_TOP }, now, totalTime,
+                true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_FGS],
+                screenStates, memStates, new int[] { STATE_BOUND_FGS }, now, totalTime,
                 true);
         dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_FGS],
                 screenStates, memStates, new int[] { STATE_FGS}, now, totalTime,
@@ -953,9 +955,6 @@
                 screenStates, memStates, new int[] {STATE_HOME}, now, totalTime, true);
         dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_LAST_ACTIVITY],
                 screenStates, memStates, new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_CACHED,
-                screenStates, memStates, new int[] {STATE_CACHED_ACTIVITY,
-                        STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY}, now, totalTime, true);
     }
 
     public void dumpProcessState(PrintWriter pw, String prefix,
@@ -1563,7 +1562,10 @@
                 case STATE_TOP:
                     topMs += duration;
                     break;
-                case STATE_BOUND_TOP_OR_FGS:
+                case STATE_BOUND_FGS:
+                    boundFgsMs += duration;
+                    break;
+                case STATE_BOUND_TOP:
                     boundTopMs += duration;
                     break;
                 case STATE_FGS:
@@ -1583,13 +1585,10 @@
                 case STATE_PERSISTENT:
                     otherMs += duration;
                     break;
-                case STATE_CACHED_ACTIVITY:
-                case STATE_CACHED_ACTIVITY_CLIENT:
-                case STATE_CACHED_EMPTY:
+                case STATE_CACHED:
                     cachedMs += duration;
                     break;
-                    // TODO (b/261910877) Add support for tracking boundFgsMs and
-                    // frozenMs.
+                    // TODO (b/261910877) Add support for tracking frozenMs.
             }
         }
         statsEventOutput.write(
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index f3ed09a..3ce234b 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -81,21 +81,21 @@
     public static final int STATE_NOTHING = -1;
     public static final int STATE_PERSISTENT = 0;
     public static final int STATE_TOP = 1;
-    public static final int STATE_BOUND_TOP_OR_FGS = 2;
+    public static final int STATE_BOUND_TOP = 2;
     public static final int STATE_FGS = 3;
-    public static final int STATE_IMPORTANT_FOREGROUND = 4;
-    public static final int STATE_IMPORTANT_BACKGROUND = 5;
-    public static final int STATE_BACKUP = 6;
-    public static final int STATE_SERVICE = 7;
-    public static final int STATE_SERVICE_RESTARTING = 8;
-    public static final int STATE_RECEIVER = 9;
-    public static final int STATE_HEAVY_WEIGHT = 10;
-    public static final int STATE_HOME = 11;
-    public static final int STATE_LAST_ACTIVITY = 12;
-    public static final int STATE_CACHED_ACTIVITY = 13;
-    public static final int STATE_CACHED_ACTIVITY_CLIENT = 14;
-    public static final int STATE_CACHED_EMPTY = 15;
-    public static final int STATE_COUNT = STATE_CACHED_EMPTY+1;
+    public static final int STATE_BOUND_FGS = 4;
+    public static final int STATE_IMPORTANT_FOREGROUND = 5;
+    public static final int STATE_IMPORTANT_BACKGROUND = 6;
+    public static final int STATE_BACKUP = 7;
+    public static final int STATE_SERVICE = 8;
+    public static final int STATE_SERVICE_RESTARTING = 9;
+    public static final int STATE_RECEIVER = 10;
+    public static final int STATE_HEAVY_WEIGHT = 11;
+    public static final int STATE_HOME = 12;
+    public static final int STATE_LAST_ACTIVITY = 13;
+    public static final int STATE_CACHED = 14;
+    public static final int STATE_FROZEN = 15;
+    public static final int STATE_COUNT = STATE_FROZEN + 1;
 
     public static final int PSS_SAMPLE_COUNT = 0;
     public static final int PSS_MINIMUM = 1;
@@ -154,9 +154,10 @@
     public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
 
     public static final int[] NON_CACHED_PROC_STATES = new int[] {
-            STATE_PERSISTENT, STATE_TOP, STATE_BOUND_TOP_OR_FGS, STATE_FGS,
+            STATE_PERSISTENT, STATE_TOP, STATE_FGS,
             STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
-            STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HEAVY_WEIGHT
+            STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HEAVY_WEIGHT,
+            STATE_BOUND_TOP, STATE_BOUND_FGS
     };
 
     public static final int[] BACKGROUND_PROC_STATES = new int[] {
@@ -165,11 +166,11 @@
     };
 
     public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
-            STATE_TOP, STATE_BOUND_TOP_OR_FGS, STATE_FGS, STATE_IMPORTANT_FOREGROUND,
+            STATE_TOP, STATE_FGS, STATE_IMPORTANT_FOREGROUND,
             STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
             STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER,
-            STATE_HEAVY_WEIGHT, STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
-            STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
+            STATE_HEAVY_WEIGHT, STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED,
+            STATE_BOUND_TOP, STATE_BOUND_FGS, STATE_FROZEN
     };
 
     // Should report process stats.
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 1ec2613..fccb177 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -24,6 +24,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -224,7 +225,7 @@
     }
 
     @Test
-    public void onTouchEvent_startHandwriting_delegate() {
+    public void onTouchEvent_tryAcceptDelegation_delegatorCallbackCreatesInputConnection() {
         View delegateView = new View(mContext);
         delegateView.setIsHandwritingDelegate(true);
 
@@ -245,6 +246,29 @@
     }
 
     @Test
+    public void onTouchEvent_tryAcceptDelegation_delegatorCallbackFocusesDelegate() {
+        View delegateView = new View(mContext);
+        delegateView.setIsHandwritingDelegate(true);
+        mHandwritingInitiator.onInputConnectionCreated(delegateView);
+        reset(mHandwritingInitiator);
+
+        mTestView1.setHandwritingDelegatorCallback(
+                () -> mHandwritingInitiator.onDelegateViewFocused(delegateView));
+
+        final int x1 = (sHwArea1.left + sHwArea1.right) / 2;
+        final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + mHandwritingSlop * 2;
+        final int y2 = y1;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        verify(mHandwritingInitiator, times(1)).tryAcceptStylusHandwritingDelegation(delegateView);
+    }
+
+    @Test
     public void onTouchEvent_notStartHandwriting_whenHandwritingNotAvailable() {
         final Rect rect = new Rect(600, 600, 900, 900);
         final View testView = createView(rect, true /* autoHandwritingEnabled */,
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
index 35b3267..6189914 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -23,6 +23,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.MockitoAnnotations.initMocks;
 
+import android.app.ActivityManager;
+
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -128,6 +130,34 @@
     }
 
     @SmallTest
+    public void testDumpBoundFgsDuration() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        ProcessState processState =
+                processStats.getProcessStateLocked(
+                        APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+        processState.setState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE,
+                ProcessStats.ADJ_MEM_FACTOR_NORMAL, NOW_MS, /* pkgList */ null);
+        processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS));
+        processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_STATE),
+                        eq(APP_1_UID),
+                        eq(APP_1_PROCESS_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(DURATION_SECS),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0));
+    }
+
+    @SmallTest
     public void testDumpProcessAssociation() throws Exception {
         ProcessStats processStats = new ProcessStats();
         AssociationState associationState =
diff --git a/data/keyboards/Vendor_004c_Product_0265.idc b/data/keyboards/Vendor_004c_Product_0265.idc
deleted file mode 120000
index 707dfcf..0000000
--- a/data/keyboards/Vendor_004c_Product_0265.idc
+++ /dev/null
@@ -1 +0,0 @@
-Vendor_05ac_Product_0265.idc
\ No newline at end of file
diff --git a/data/keyboards/Vendor_004c_Product_0265.idc b/data/keyboards/Vendor_004c_Product_0265.idc
new file mode 100644
index 0000000..bfea4db
--- /dev/null
+++ b/data/keyboards/Vendor_004c_Product_0265.idc
@@ -0,0 +1,34 @@
+# Copyright 2023 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.
+
+#
+# Apple Magic Trackpad 2 (Bluetooth) configuration file
+#
+# WHEN MODIFYING, also change the USB file (Vendor_05ac_Product_0265.idc)
+#
+
+gestureProp.Pressure_Calibration_Offset = 30
+gestureProp.Palm_Pressure = 250.0
+gestureProp.Palm_Width = 20.0
+gestureProp.Multiple_Palm_Width = 20.0
+
+# Enable Stationary Wiggle Filter
+gestureProp.Stationary_Wiggle_Filter_Enabled = 1
+gestureProp.Finger_Moving_Energy = 0.0008
+gestureProp.Finger_Moving_Hysteresis = 0.0004
+
+# Avoid accidental scroll/move on finger lift
+gestureProp.Max_Stationary_Move_Speed = 47
+gestureProp.Max_Stationary_Move_Speed_Hysteresis = 1
+gestureProp.Max_Stationary_Move_Suppress_Distance = 0.2
diff --git a/data/keyboards/Vendor_05ac_Product_0265.idc b/data/keyboards/Vendor_05ac_Product_0265.idc
index d72de64..520d188 100644
--- a/data/keyboards/Vendor_05ac_Product_0265.idc
+++ b/data/keyboards/Vendor_05ac_Product_0265.idc
@@ -13,9 +13,9 @@
 # limitations under the License.
 
 #
-# Apple Magic Trackpad 2 configuration file
-#   Bluetooth vendor ID 004c
-#   USB vendor ID 05ac
+# Apple Magic Trackpad 2 (USB) configuration file
+#
+# WHEN MODIFYING, also change the Bluetooth file (Vendor_004c_Product_0265.idc)
 #
 
 gestureProp.Pressure_Calibration_Offset = 30
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 73a7403..31c5e33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -25,6 +25,7 @@
 import android.app.WindowConfiguration.WindowingMode
 import android.content.Context
 import android.os.IBinder
+import android.os.SystemProperties
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_NONE
@@ -32,6 +33,7 @@
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
 import android.window.TransitionRequestInfo
+import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
 import androidx.annotation.BinderThread
 import com.android.internal.protolog.common.ProtoLog
@@ -115,10 +117,7 @@
         val wct = WindowContainerTransaction()
         // Bring other apps to front first
         bringDesktopAppsToFront(wct)
-
-        wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FREEFORM)
-        wct.reorder(task.getToken(), true /* onTop */)
-
+        addMoveToDesktopChanges(wct, task.token)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
         } else {
@@ -136,8 +135,7 @@
         ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId)
 
         val wct = WindowContainerTransaction()
-        wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FULLSCREEN)
-        wct.setBounds(task.getToken(), null)
+        addMoveToFullscreenChanges(wct, task.token)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
         } else {
@@ -234,8 +232,8 @@
                         " taskId=%d",
                     task.taskId
                 )
-                return WindowContainerTransaction().apply {
-                    setWindowingMode(task.token, WINDOWING_MODE_FREEFORM)
+                return WindowContainerTransaction().also { wct ->
+                    addMoveToDesktopChanges(wct, task.token)
                 }
             }
         }
@@ -251,15 +249,44 @@
                         " taskId=%d",
                     task.taskId
                 )
-                return WindowContainerTransaction().apply {
-                    setWindowingMode(task.token, WINDOWING_MODE_FULLSCREEN)
-                    setBounds(task.token, null)
+                return WindowContainerTransaction().also { wct ->
+                    addMoveToFullscreenChanges(wct, task.token)
                 }
             }
         }
         return null
     }
 
+    private fun addMoveToDesktopChanges(
+        wct: WindowContainerTransaction,
+        token: WindowContainerToken
+    ) {
+        wct.setWindowingMode(token, WINDOWING_MODE_FREEFORM)
+        wct.reorder(token, true /* onTop */)
+        if (isDesktopDensityOverrideSet()) {
+            wct.setDensityDpi(token, getDesktopDensityDpi())
+        }
+    }
+
+    private fun addMoveToFullscreenChanges(
+        wct: WindowContainerTransaction,
+        token: WindowContainerToken
+    ) {
+        wct.setWindowingMode(token, WINDOWING_MODE_FULLSCREEN)
+        wct.setBounds(token, null)
+        if (isDesktopDensityOverrideSet()) {
+            wct.setDensityDpi(token, getFullscreenDensityDpi())
+        }
+    }
+
+    private fun getFullscreenDensityDpi(): Int {
+        return context.resources.displayMetrics.densityDpi
+    }
+
+    private fun getDesktopDensityDpi(): Int {
+        return DESKTOP_DENSITY_OVERRIDE
+    }
+
     /** Creates a new instance of the external interface to pass to another process. */
     private fun createExternalInterface(): ExternalInterfaceBinder {
         return IDesktopModeImpl(this)
@@ -318,4 +345,18 @@
             return result[0]
         }
     }
+
+    companion object {
+        private val DESKTOP_DENSITY_OVERRIDE =
+            SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 0)
+        private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000)
+
+        /**
+         * Check if desktop density override is enabled
+         */
+        @JvmStatic
+        fun isDesktopDensityOverrideSet(): Boolean {
+            return DESKTOP_DENSITY_OVERRIDE in DESKTOP_DENSITY_ALLOWED_RANGE
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 4b5ad1d..e09c3c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -68,6 +68,7 @@
 
     DismissTransition mPendingDismiss = null;
     TransitSession mPendingEnter = null;
+    TransitSession mPendingRecent = null;
     TransitSession mPendingResize = null;
 
     private IBinder mAnimatingTransition = null;
@@ -259,6 +260,10 @@
         return mPendingEnter != null && mPendingEnter.mTransition == transition;
     }
 
+    boolean isPendingRecent(IBinder transition) {
+        return mPendingRecent != null && mPendingRecent.mTransition == transition;
+    }
+
     boolean isPendingDismiss(IBinder transition) {
         return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
     }
@@ -271,6 +276,8 @@
     private TransitSession getPendingTransition(IBinder transition) {
         if (isPendingEnter(transition)) {
             return mPendingEnter;
+        } else if (isPendingRecent(transition)) {
+            return mPendingRecent;
         } else if (isPendingDismiss(transition)) {
             return mPendingDismiss;
         } else if (isPendingResize(transition)) {
@@ -354,10 +361,32 @@
                 + " deduced Resize split screen");
     }
 
+    void setRecentTransition(@NonNull IBinder transition,
+            @Nullable RemoteTransition remoteTransition,
+            @Nullable TransitionFinishedCallback finishCallback) {
+        mPendingRecent = new TransitSession(transition, null /* consumedCb */, finishCallback);
+
+        if (remoteTransition != null) {
+            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
+            mPendingRemoteHandler = new OneShotRemoteHandler(
+                    mTransitions.getMainExecutor(), remoteTransition);
+            mPendingRemoteHandler.setTransition(transition);
+        }
+
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+                + " deduced Enter recent panel");
+    }
+
     void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
             IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
         if (mergeTarget != mAnimatingTransition) return;
 
+        if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
+            // Since there's an entering transition merged, recent transition no longer
+            // need to handle entering split screen after the transition finished.
+            mPendingRecent.setFinishedCallback(null);
+        }
+
         if (mActiveRemoteHandler != null) {
             mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
         } else {
@@ -394,6 +423,10 @@
         } else if (isPendingDismiss(transition)) {
             mPendingDismiss.onConsumed(aborted);
             mPendingDismiss = null;
+        } else if (isPendingRecent(transition)) {
+            mPendingRecent.onConsumed(aborted);
+            mPendingRecent = null;
+            mPendingRemoteHandler = null;
         } else if (isPendingResize(transition)) {
             mPendingResize.onConsumed(aborted);
             mPendingResize = null;
@@ -407,6 +440,9 @@
         if (isPendingEnter(mAnimatingTransition)) {
             mPendingEnter.onFinished(wct, mFinishTransaction);
             mPendingEnter = null;
+        } else if (isPendingRecent(mAnimatingTransition)) {
+            mPendingRecent.onFinished(wct, mFinishTransaction);
+            mPendingRecent = null;
         } else if (isPendingDismiss(mAnimatingTransition)) {
             mPendingDismiss.onFinished(wct, mFinishTransaction);
             mPendingDismiss = null;
@@ -516,7 +552,7 @@
     }
 
     /** Calls when the transition finished. */
-    public interface TransitionFinishedCallback {
+    interface TransitionFinishedCallback {
         void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 9d1a7ea..a5546e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -259,6 +259,37 @@
                 }
             };
 
+    private final SplitScreenTransitions.TransitionFinishedCallback
+            mRecentTransitionFinishedCallback =
+            new SplitScreenTransitions.TransitionFinishedCallback() {
+                @Override
+                public void onFinished(WindowContainerTransaction finishWct,
+                        SurfaceControl.Transaction finishT) {
+                    // Check if the recent transition is finished by returning to the current
+                    // split, so we
+                    // can restore the divider bar.
+                    for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
+                        final WindowContainerTransaction.HierarchyOp op =
+                                finishWct.getHierarchyOps().get(i);
+                        final IBinder container = op.getContainer();
+                        if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
+                                && (mMainStage.containsContainer(container)
+                                || mSideStage.containsContainer(container))) {
+                            updateSurfaceBounds(mSplitLayout, finishT,
+                                    false /* applyResizingOffset */);
+                            setDividerVisibility(true, finishT);
+                            return;
+                        }
+                    }
+
+                    // Dismiss the split screen if it's not returning to split.
+                    prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
+                    setSplitsVisible(false);
+                    setDividerVisibility(false, finishT);
+                    logExit(EXIT_REASON_UNKNOWN);
+                }
+            };
+
     protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             ShellTaskOrganizer taskOrganizer, DisplayController displayController,
             DisplayImeController displayImeController,
@@ -357,11 +388,6 @@
         return mMainStage.isActive();
     }
 
-    /** Checks if `transition` is a pending enter-split transition. */
-    public boolean isPendingEnter(IBinder transition) {
-        return mSplitTransitions.isPendingEnter(transition);
-    }
-
     @StageType
     int getStageOfTask(int taskId) {
         if (mMainStage.containsTask(taskId)) {
@@ -2240,8 +2266,9 @@
                 final int activityType = triggerTask.getActivityType();
                 if (activityType == ACTIVITY_TYPE_HOME
                         || activityType == ACTIVITY_TYPE_RECENTS) {
-                    // starting recents, so don't handle this.
-                    return null;
+                    // Enter overview panel, so start recent transition.
+                    mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
+                            mRecentTransitionFinishedCallback);
                 }
             }
         } else {
@@ -2370,6 +2397,8 @@
         if (mSplitTransitions.isPendingEnter(transition)) {
             shouldAnimate = startPendingEnterAnimation(
                     transition, info, startTransaction, finishTransaction);
+        } else if (mSplitTransitions.isPendingRecent(transition)) {
+            shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
         } else if (mSplitTransitions.isPendingDismiss(transition)) {
             shouldAnimate = startPendingDismissAnimation(
                     mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
@@ -2624,35 +2653,10 @@
         return true;
     }
 
-    /** Call this when starting the open-recents animation while split-screen is active. */
-    public void onRecentsInSplitAnimationStart(@NonNull SurfaceControl.Transaction t) {
+    private boolean startPendingRecentAnimation(@NonNull IBinder transition,
+            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
         setDividerVisibility(false, t);
-    }
-
-    /** Call this when the recents animation during split-screen finishes. */
-    public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
-            SurfaceControl.Transaction finishT) {
-        // Check if the recent transition is finished by returning to the current
-        // split, so we can restore the divider bar.
-        for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
-            final WindowContainerTransaction.HierarchyOp op =
-                    finishWct.getHierarchyOps().get(i);
-            final IBinder container = op.getContainer();
-            if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
-                    && (mMainStage.containsContainer(container)
-                    || mSideStage.containsContainer(container))) {
-                updateSurfaceBounds(mSplitLayout, finishT,
-                        false /* applyResizingOffset */);
-                setDividerVisibility(true, finishT);
-                return;
-            }
-        }
-
-        // Dismiss the split screen if it's not returning to split.
-        prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
-        setSplitsVisible(false);
-        setDividerVisibility(false, finishT);
-        logExit(EXIT_REASON_UNKNOWN);
+        return true;
     }
 
     private void addDividerBarToTransition(@NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index b9f6a01..2e86448 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -18,7 +18,6 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -26,7 +25,6 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
-import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -70,20 +68,14 @@
         /** Pip was entered while handling an intent with its own remoteTransition. */
         static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3;
 
-        /** Recents transition while split-screen active. */
-        static final int TYPE_RECENTS_DURING_SPLIT = 4;
-
         /** The default animation for this mixed transition. */
         static final int ANIM_TYPE_DEFAULT = 0;
 
         /** For ENTER_PIP_FROM_SPLIT, indicates that this is a to-home animation. */
         static final int ANIM_TYPE_GOING_HOME = 1;
 
-        /** For RECENTS_DURING_SPLIT, is set when this turns into a pair->pair task switch. */
-        static final int ANIM_TYPE_PAIR_TO_PAIR = 1;
-
         final int mType;
-        int mAnimType = ANIM_TYPE_DEFAULT;
+        int mAnimType = 0;
         final IBinder mTransition;
 
         Transitions.TransitionHandler mLeftoversHandler = null;
@@ -175,25 +167,6 @@
             mixed.mLeftoversHandler = handler.first;
             mActiveTransitions.add(mixed);
             return handler.second;
-        } else if (mSplitHandler.isSplitActive()
-                && isOpeningType(request.getType())
-                && request.getTriggerTask() != null
-                && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                && (request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME
-                        || request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_RECENTS)) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
-                    + "Split-Screen is active, so treat it as Mixed.");
-            Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler =
-                    mPlayer.dispatchRequest(transition, request, this);
-            if (handler == null) {
-                // fall through -- it will probably be picked-up by normal split handler.
-                return null;
-            }
-            final MixedTransition mixed = new MixedTransition(
-                    MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
-            mixed.mLeftoversHandler = handler.first;
-            mActiveTransitions.add(mixed);
-            return handler.second;
         }
         return null;
     }
@@ -243,9 +216,6 @@
         } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
             return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction,
                     finishTransaction, finishCallback);
-        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
-            return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
-                    finishCallback);
         } else {
             mActiveTransitions.remove(mixed);
             throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -471,40 +441,12 @@
         return true;
     }
 
-    private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        // Split-screen is only interested in the recents transition finishing (and merging), so
-        // just wrap finish and start recents animation directly.
-        Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
-            mixed.mInFlightSubAnimations = 0;
-            mActiveTransitions.remove(mixed);
-            // If pair-to-pair switching, the post-recents clean-up isn't needed.
-            if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) {
-                wct = wct != null ? wct : new WindowContainerTransaction();
-                mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
-            }
-            mSplitHandler.onTransitionAnimationComplete();
-            finishCallback.onTransitionFinished(wct, wctCB);
-        };
-        mixed.mInFlightSubAnimations = 1;
-        mSplitHandler.onRecentsInSplitAnimationStart(startTransaction);
-        final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
-                startTransaction, finishTransaction, finishCB);
-        if (!handled) {
-            mActiveTransitions.remove(mixed);
-        }
-        return handled;
-    }
-
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         for (int i = 0; i < mActiveTransitions.size(); ++i) {
-            if (mActiveTransitions.get(i).mTransition != mergeTarget) continue;
+            if (mActiveTransitions.get(i) != mergeTarget) continue;
             MixedTransition mixed = mActiveTransitions.get(i);
             if (mixed.mInFlightSubAnimations <= 0) {
                 // Already done, so no need to end it.
@@ -532,14 +474,6 @@
                     mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
                             finishCallback);
                 }
-            } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
-                if (mSplitHandler.isPendingEnter(transition)) {
-                    // Recents -> enter-split means that we are switching from one pair to
-                    // another pair.
-                    mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR;
-                }
-                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
-                        finishCallback);
             } else {
                 throw new IllegalStateException("Playing a mixed transition with unknown type? "
                         + mixed.mType);
@@ -559,8 +493,6 @@
         if (mixed == null) return;
         if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
             mPipHandler.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
-            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 72da108..3c0ef96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Point;
@@ -46,6 +47,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
 
 /**
  * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -95,6 +97,17 @@
         mDesktopActive = DesktopModeStatus.isActive(mContext);
     }
 
+    @Override
+    protected Configuration getConfigurationWithOverrides(
+            ActivityManager.RunningTaskInfo taskInfo) {
+        Configuration configuration = taskInfo.getConfiguration();
+        if (DesktopTasksController.isDesktopDensityOverrideSet()) {
+            // Density is overridden for desktop tasks. Keep system density for window decoration.
+            configuration.densityDpi = mContext.getResources().getConfiguration().densityDpi;
+        }
+        return configuration;
+    }
+
     void setCaptionListeners(
             View.OnClickListener onCaptionButtonClickListener,
             View.OnTouchListener onCaptionTouchListener) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7a7ac47..ddd3b44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -131,7 +131,17 @@
         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
 
         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
-        mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration());
+        mDecorWindowContext = mContext.createConfigurationContext(
+                getConfigurationWithOverrides(mTaskInfo));
+    }
+
+    /**
+     * Get {@link Configuration} from supplied {@link RunningTaskInfo}.
+     *
+     * Allows values to be overridden before returning the configuration.
+     */
+    protected Configuration getConfigurationWithOverrides(RunningTaskInfo taskInfo) {
+        return taskInfo.getConfiguration();
     }
 
     /**
@@ -165,7 +175,7 @@
 
         outResult.mRootView = rootView;
         rootView = null; // Clear it just in case we use it accidentally
-        final Configuration taskConfig = mTaskInfo.getConfiguration();
+        final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo);
         if (oldTaskConfig.densityDpi != taskConfig.densityDpi
                 || mDisplay == null
                 || mDisplay.getDisplayId() != mTaskInfo.displayId) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 2edec7d..3901dab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -35,7 +35,6 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -250,7 +249,7 @@
 
     @Test
     @UiThreadTest
-    public void testEnterRecentsAndCommit() {
+    public void testEnterRecents() {
         enterSplit();
 
         ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
@@ -262,60 +261,24 @@
         TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
         IBinder transition = mock(IBinder.class);
         WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
-        // Don't handle recents opening
-        assertNull(result);
+
+        assertTrue(result.isEmpty());
 
         // make sure we haven't made any local changes yet (need to wait until transition is ready)
         assertTrue(mStageCoordinator.isSplitScreenVisible());
 
-        // simulate the start of recents transition
-        mMainStage.onTaskVanished(mMainChild);
-        mSideStage.onTaskVanished(mSideChild);
-        mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
-        assertTrue(mStageCoordinator.isSplitScreenVisible());
-
-        // Make sure it cleans-up if recents doesn't restore
-        WindowContainerTransaction commitWCT = new WindowContainerTransaction();
-        mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
-                mock(SurfaceControl.Transaction.class));
-        assertFalse(mStageCoordinator.isSplitScreenVisible());
-    }
-
-    @Test
-    @UiThreadTest
-    public void testEnterRecentsAndRestore() {
-        enterSplit();
-
-        ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                .setActivityType(ACTIVITY_TYPE_HOME)
+        // simulate the transition
+        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT, 0)
+                .addChange(TRANSIT_TO_FRONT, homeTask)
+                .addChange(TRANSIT_TO_BACK, mMainChild)
+                .addChange(TRANSIT_TO_BACK, mSideChild)
                 .build();
-
-        // Create a request to bring home forward
-        TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
-        IBinder transition = mock(IBinder.class);
-        WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
-        // Don't handle recents opening
-        assertNull(result);
-
-        // make sure we haven't made any local changes yet (need to wait until transition is ready)
-        assertTrue(mStageCoordinator.isSplitScreenVisible());
-
-        // simulate the start of recents transition
         mMainStage.onTaskVanished(mMainChild);
         mSideStage.onTaskVanished(mSideChild);
-        mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
-        assertTrue(mStageCoordinator.isSplitScreenVisible());
-
-        // Make sure we remain in split after recents restores.
-        WindowContainerTransaction restoreWCT = new WindowContainerTransaction();
-        restoreWCT.reorder(mMainChild.token, true /* toTop */);
-        restoreWCT.reorder(mSideChild.token, true /* toTop */);
-        // simulate the restoreWCT being applied:
-        mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
-        mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
-        mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
-                mock(SurfaceControl.Transaction.class));
+        mStageCoordinator.startAnimation(transition, info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(Transitions.TransitionFinishCallback.class));
         assertTrue(mStageCoordinator.isSplitScreenVisible());
     }
 
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 23f87ab..f86b9af 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1566,7 +1566,7 @@
         FileInputStream in = null;
         try {
             in = new FileInputStream(fileDescriptor);
-            loadAttributes(in);
+            loadAttributes(in, fileDescriptor);
         } finally {
             closeQuietly(in);
             if (isFdDuped) {
@@ -1637,7 +1637,7 @@
                 mSeekableFileDescriptor = null;
             }
         }
-        loadAttributes(inputStream);
+        loadAttributes(inputStream, null);
     }
 
     /**
@@ -1963,7 +1963,7 @@
      * This function decides which parser to read the image data according to the given input stream
      * type and the content of the input stream.
      */
-    private void loadAttributes(@NonNull InputStream in) {
+    private void loadAttributes(@NonNull InputStream in, @Nullable FileDescriptor fd) {
         if (in == null) {
             throw new NullPointerException("inputstream shouldn't be null");
         }
@@ -1993,7 +1993,7 @@
                         break;
                     }
                     case IMAGE_TYPE_HEIF: {
-                        getHeifAttributes(inputStream);
+                        getHeifAttributes(inputStream, fd);
                         break;
                     }
                     case IMAGE_TYPE_ORF: {
@@ -2580,7 +2580,7 @@
             } else if (isSeekableFD(in.getFD())) {
                 mSeekableFileDescriptor = in.getFD();
             }
-            loadAttributes(in);
+            loadAttributes(in, null);
         } finally {
             closeQuietly(in);
             if (modernFd != null) {
@@ -3068,59 +3068,66 @@
         }
     }
 
-    private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException {
+    private void getHeifAttributes(ByteOrderedDataInputStream in, @Nullable FileDescriptor fd)
+            throws IOException {
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         try {
-            retriever.setDataSource(new MediaDataSource() {
-                long mPosition;
+            if (fd != null) {
+                retriever.setDataSource(fd);
+            } else {
+                retriever.setDataSource(new MediaDataSource() {
+                    long mPosition;
 
-                @Override
-                public void close() throws IOException {}
+                    @Override
+                    public void close() throws IOException {}
 
-                @Override
-                public int readAt(long position, byte[] buffer, int offset, int size)
-                        throws IOException {
-                    if (size == 0) {
-                        return 0;
-                    }
-                    if (position < 0) {
+                    @Override
+                    public int readAt(long position, byte[] buffer, int offset, int size)
+                            throws IOException {
+                        if (size == 0) {
+                            return 0;
+                        }
+                        if (position < 0) {
+                            return -1;
+                        }
+                        try {
+                            if (mPosition != position) {
+                                // We don't allow seek to positions after the available bytes,
+                                // the input stream won't be able to seek back then.
+                                // However, if we hit an exception before (mPosition set to -1),
+                                // let it try the seek in hope it might recover.
+                                if (mPosition >= 0 && position >= mPosition + in.available()) {
+                                    return -1;
+                                }
+                                in.seek(position);
+                                mPosition = position;
+                            }
+
+                            // If the read will cause us to go over the available bytes,
+                            // reduce the size so that we stay in the available range.
+                            // Otherwise the input stream may not be able to seek back.
+                            if (size > in.available()) {
+                                size = in.available();
+                            }
+
+                            int bytesRead = in.read(buffer, offset, size);
+                            if (bytesRead >= 0) {
+                                mPosition += bytesRead;
+                                return bytesRead;
+                            }
+                        } catch (IOException e) {
+                            // absorb the exception and fall through to the 'failed read' path below
+                        }
+                        mPosition = -1; // need to seek on next read
                         return -1;
                     }
-                    try {
-                        if (mPosition != position) {
-                            // We don't allow seek to positions after the available bytes,
-                            // the input stream won't be able to seek back then.
-                            // However, if we hit an exception before (mPosition set to -1),
-                            // let it try the seek in hope it might recover.
-                            if (mPosition >= 0 && position >= mPosition + in.available()) {
-                                return -1;
-                            }
-                            in.seek(position);
-                            mPosition = position;
-                        }
 
-                        // If the read will cause us to go over the available bytes,
-                        // reduce the size so that we stay in the available range.
-                        // Otherwise the input stream may not be able to seek back.
-                        if (size > in.available()) {
-                            size = in.available();
-                        }
-
-                        int bytesRead = in.read(buffer, offset, size);
-                        if (bytesRead >= 0) {
-                            mPosition += bytesRead;
-                            return bytesRead;
-                        }
-                    } catch (IOException e) {}
-                    mPosition = -1; // need to seek on next read
-                    return -1;
-                }
-
-                @Override
-                public long getSize() throws IOException {
-                    return -1;
-                }
-            });
+                    @Override
+                    public long getSize() throws IOException {
+                        return -1;
+                    }
+                });
+            }
 
             String exifOffsetStr = retriever.extractMetadata(
                     MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET);
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index e91d35b..f4e89a14 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="app_name">CarrierDefaultApp</string>
+    <string name="app_name">Carrier Communications</string>
     <string name="android_system_label">Mobile Carrier</string>
     <string name="portal_notification_id">Mobile data has run out</string>
     <string name="no_data_notification_id">Your mobile data has been deactivated</string>
@@ -17,9 +17,9 @@
     <!-- Telephony notification channel name for performance boost notifications. -->
     <string name="performance_boost_notification_channel">Performance boost</string>
     <!-- Notification title text for the performance boost notification. -->
-    <string name="performance_boost_notification_title">Improve your app experience</string>
+    <string name="performance_boost_notification_title">5G options from your carrier</string>
     <!-- Notification detail text for the performance boost notification. -->
-    <string name="performance_boost_notification_detail">Tap to visit %s\'s website and learn more</string>
+    <string name="performance_boost_notification_detail">Visit %s\'s website to see options for your app experience</string>
     <!-- Notification button text to cancel the performance boost notification. -->
     <string name="performance_boost_notification_button_not_now">Not now</string>
     <!-- Notification button text to manage the performance boost notification. -->
diff --git a/packages/SystemUI/res/drawable/ic_important_outline.xml b/packages/SystemUI/res/drawable/ic_important_outline.xml
index 7a628bb..642582c 100644
--- a/packages/SystemUI/res/drawable/ic_important_outline.xml
+++ b/packages/SystemUI/res/drawable/ic_important_outline.xml
@@ -20,6 +20,7 @@
         android:height="24dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"
+        android:autoMirrored="true"
         android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="@android:color/white"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 0e5f8c1..553453d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -18,13 +18,9 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.TypedValue;
-import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
@@ -33,22 +29,10 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
 
-import java.lang.ref.WeakReference;
-
 /***
  * Manages a number of views inside of the given layout. See below for a list of widgets.
  */
 public abstract class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
-    /** Handler token posted with accessibility announcement runnables. */
-    private static final Object ANNOUNCE_TOKEN = new Object();
-
-    /**
-     * Delay before speaking an accessibility announcement. Used to prevent
-     * lift-to-type from interrupting itself.
-     */
-    private static final long ANNOUNCEMENT_DELAY = 250;
-
-    private final Handler mHandler;
 
     private CharSequence mMessage;
     private boolean mIsVisible;
@@ -65,7 +49,6 @@
         super(context, attrs);
         setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
 
-        mHandler = new Handler(Looper.myLooper());
         onThemeChanged();
     }
 
@@ -127,9 +110,6 @@
     private void securityMessageChanged(CharSequence message) {
         mMessage = message;
         update();
-        mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
-        mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
-                (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
     }
 
     private void clearMessage() {
@@ -156,25 +136,4 @@
 
     /** Set the text color */
     protected abstract void updateTextColor();
-
-    /**
-     * Runnable used to delay accessibility announcements.
-     */
-    private static class AnnounceRunnable implements Runnable {
-        private final WeakReference<View> mHost;
-        private final CharSequence mTextToAnnounce;
-
-        AnnounceRunnable(View host, CharSequence textToAnnounce) {
-            mHost = new WeakReference<View>(host);
-            mTextToAnnounce = textToAnnounce;
-        }
-
-        @Override
-        public void run() {
-            final View host = mHost.get();
-            if (host != null) {
-                host.announceForAccessibility(mTextToAnnounce);
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 6a92162..c1896fc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -18,11 +18,17 @@
 
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.text.TextUtils;
+import android.view.View;
+
+import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.util.ViewController;
 
+import java.lang.ref.WeakReference;
+
 import javax.inject.Inject;
 
 /**
@@ -31,8 +37,14 @@
  */
 public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
         extends ViewController<T> {
+    /**
+     * Delay before speaking an accessibility announcement. Used to prevent
+     * lift-to-type from interrupting itself.
+     */
+    private static final long ANNOUNCEMENT_DELAY = 250;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final ConfigurationController mConfigurationController;
+    private final AnnounceRunnable mAnnounceRunnable;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
         public void onFinishedGoingToSleep(int why) {
@@ -68,6 +80,7 @@
 
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mConfigurationController = configurationController;
+        mAnnounceRunnable = new AnnounceRunnable(mView);
     }
 
     @Override
@@ -100,6 +113,12 @@
      */
     public void setMessage(CharSequence s, boolean animate) {
         mView.setMessage(s, animate);
+        CharSequence msg = mView.getText();
+        if (!TextUtils.isEmpty(msg)) {
+            mView.removeCallbacks(mAnnounceRunnable);
+            mAnnounceRunnable.setTextToAnnounce(msg);
+            mView.postDelayed(mAnnounceRunnable, ANNOUNCEMENT_DELAY);
+        }
     }
 
     public void setMessage(int resId) {
@@ -134,4 +153,30 @@
                     view, mKeyguardUpdateMonitor, mConfigurationController);
         }
     }
+
+    /**
+     * Runnable used to delay accessibility announcements.
+     */
+    @VisibleForTesting
+    public static class AnnounceRunnable implements Runnable {
+        private final WeakReference<View> mHost;
+        private CharSequence mTextToAnnounce;
+
+        AnnounceRunnable(View host) {
+            mHost = new WeakReference<>(host);
+        }
+
+        /** Sets the text to announce. */
+        public void setTextToAnnounce(CharSequence textToAnnounce) {
+            mTextToAnnounce = textToAnnounce;
+        }
+
+        @Override
+        public void run() {
+            final View host = mHost.get();
+            if (host != null) {
+                host.announceForAccessibility(mTextToAnnounce);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 98866c6..7255383 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -1066,23 +1066,28 @@
     }
 
     private void reloadColors() {
-        reinflateViewFlipper();
-        mView.reloadColors();
+        reinflateViewFlipper(() -> mView.reloadColors());
     }
 
     /** Handles density or font scale changes. */
     private void onDensityOrFontScaleChanged() {
-        reinflateViewFlipper();
-        mView.onDensityOrFontScaleChanged();
+        reinflateViewFlipper(() -> mView.onDensityOrFontScaleChanged());
     }
 
     /**
      * Reinflate the view flipper child view.
      */
-    public void reinflateViewFlipper() {
+    public void reinflateViewFlipper(
+            KeyguardSecurityViewFlipperController.OnViewInflatedListener onViewInflatedListener) {
         mSecurityViewFlipperController.clearViews();
-        mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
-                mKeyguardSecurityCallback);
+        if (mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)) {
+            mSecurityViewFlipperController.asynchronouslyInflateView(mCurrentSecurityMode,
+                    mKeyguardSecurityCallback, onViewInflatedListener);
+        } else {
+            mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
+                    mKeyguardSecurityCallback);
+            onViewInflatedListener.onViewInflated();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 39b567f..68e1dd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -19,11 +19,16 @@
 import android.util.Log;
 import android.view.LayoutInflater;
 
+import androidx.annotation.Nullable;
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardInputViewController.Factory;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
 import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.util.ViewController;
 
 import java.util.ArrayList;
@@ -44,18 +49,24 @@
     private final List<KeyguardInputViewController<KeyguardInputView>> mChildren =
             new ArrayList<>();
     private final LayoutInflater mLayoutInflater;
+    private final AsyncLayoutInflater mAsyncLayoutInflater;
     private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
     private final Factory mKeyguardSecurityViewControllerFactory;
+    private final FeatureFlags mFeatureFlags;
 
     @Inject
     protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
             LayoutInflater layoutInflater,
+            AsyncLayoutInflater asyncLayoutInflater,
             KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory,
-            EmergencyButtonController.Factory emergencyButtonControllerFactory) {
+            EmergencyButtonController.Factory emergencyButtonControllerFactory,
+            FeatureFlags featureFlags) {
         super(view);
         mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
         mLayoutInflater = layoutInflater;
         mEmergencyButtonControllerFactory = emergencyButtonControllerFactory;
+        mAsyncLayoutInflater = asyncLayoutInflater;
+        mFeatureFlags = featureFlags;
     }
 
     @Override
@@ -92,13 +103,12 @@
             }
         }
 
-        if (childController == null
+        if (!mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER) && childController == null
                 && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) {
-
             int layoutId = getLayoutIdFor(securityMode);
             KeyguardInputView view = null;
             if (layoutId != 0) {
-                if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
+                if (DEBUG) Log.v(TAG, "inflating on main thread id = " + layoutId);
                 view = (KeyguardInputView) mLayoutInflater.inflate(
                         layoutId, mView, false);
                 mView.addView(view);
@@ -119,6 +129,36 @@
         return childController;
     }
 
+    /**
+     * Asynchronously inflate view and then add it to view flipper on the main thread when complete.
+     *
+     * OnInflateFinishedListener will be called on the main thread.
+     *
+     * @param securityMode
+     * @param keyguardSecurityCallback
+     */
+    public void asynchronouslyInflateView(SecurityMode securityMode,
+            KeyguardSecurityCallback keyguardSecurityCallback,
+            @Nullable OnViewInflatedListener onViewInflatedListener) {
+        int layoutId = getLayoutIdFor(securityMode);
+        if (layoutId != 0) {
+            if (DEBUG) Log.v(TAG, "inflating on bg thread id = " + layoutId);
+            mAsyncLayoutInflater.inflate(layoutId, mView,
+                    (view, resId, parent) -> {
+                        mView.addView(view);
+                        KeyguardInputViewController<KeyguardInputView> childController =
+                                mKeyguardSecurityViewControllerFactory.create(
+                                        (KeyguardInputView) view, securityMode,
+                                        keyguardSecurityCallback);
+                        childController.init();
+                        mChildren.add(childController);
+                        if (onViewInflatedListener != null) {
+                            onViewInflatedListener.onViewInflated();
+                        }
+                    });
+        }
+    }
+
     private int getLayoutIdFor(SecurityMode securityMode) {
         switch (securityMode) {
             case Pattern: return R.layout.keyguard_pattern_view;
@@ -162,4 +202,10 @@
             return 0;
         }
     }
+
+    /** Listener to when view has finished inflation. */
+    public interface OnViewInflatedListener {
+        /** Notifies that view has been inflated */
+        void onViewInflated();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b054c7e..0be3bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -98,6 +98,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.view.textclassifier.TextClassificationManager;
 
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
 import androidx.core.app.NotificationManagerCompat;
 
 import com.android.internal.app.IBatteryStats;
@@ -395,6 +396,13 @@
         return LayoutInflater.from(context);
     }
 
+    /** */
+    @Provides
+    @Singleton
+    public AsyncLayoutInflater provideAsyncLayoutInflater(Context context) {
+        return new AsyncLayoutInflater(context);
+    }
+
     @Provides
     static MediaProjectionManager provideMediaProjectionManager(Context context) {
         return context.getSystemService(MediaProjectionManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index 450fa14..82be009 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -176,10 +176,10 @@
             return;
         }
 
-        final Intent credential = getKeyguardManager()
+        final Intent confirmCredentialIntent = getKeyguardManager()
                 .createConfirmDeviceCredentialIntent(null, null, getTargetUserId(),
                 true /* disallowBiometricsIfPolicyExists */);
-        if (credential == null) {
+        if (confirmCredentialIntent == null) {
             return;
         }
 
@@ -193,14 +193,18 @@
                 PendingIntent.FLAG_IMMUTABLE, options.toBundle());
 
         if (target != null) {
-            credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
+            confirmCredentialIntent.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
         }
 
+        // WorkLockActivity is started as a task overlay, so unless credential confirmation is also
+        // started as an overlay, it won't be visible.
         final ActivityOptions launchOptions = ActivityOptions.makeBasic();
         launchOptions.setLaunchTaskId(getTaskId());
         launchOptions.setTaskOverlay(true /* taskOverlay */, true /* canResume */);
+        // Propagate it in case more than one activity is launched.
+        confirmCredentialIntent.putExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, true);
 
-        startActivityForResult(credential, REQUEST_CODE_CONFIRM_CREDENTIALS,
+        startActivityForResult(confirmCredentialIntent, REQUEST_CODE_CONFIRM_CREDENTIALS,
                 launchOptions.toBundle());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index d716784..5fcf105 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.ActivityStarter
 import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
 
@@ -112,16 +111,18 @@
                     launch {
                         viewModel.show.collect {
                             // Reset Security Container entirely.
-                            view.visibility = View.VISIBLE
-                            securityContainerController.onBouncerVisibilityChanged(
-                                /* isVisible= */ true
-                            )
-                            securityContainerController.reinflateViewFlipper()
-                            securityContainerController.showPrimarySecurityScreen(
-                                /* turningOff= */ false
-                            )
-                            securityContainerController.appear()
-                            securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
+                            securityContainerController.reinflateViewFlipper {
+                                // Reset Security Container entirely.
+                                view.visibility = View.VISIBLE
+                                securityContainerController.onBouncerVisibilityChanged(
+                                    /* isVisible= */ true
+                                )
+                                securityContainerController.showPrimarySecurityScreen(
+                                    /* turningOff= */ false
+                                )
+                                securityContainerController.appear()
+                                securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
+                            }
                         }
                     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e2f31e8..2899081 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade;
 
+import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_BACK;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.app.StatusBarManager;
@@ -39,6 +40,7 @@
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -88,10 +90,12 @@
     private final NotificationInsetsController mNotificationInsetsController;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
     private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
+    private final boolean mIsTrackpadGestureBackEnabled;
     private GestureDetector mPulsingWakeupGestureHandler;
     private View mBrightnessMirror;
     private boolean mTouchActive;
     private boolean mTouchCancelled;
+    private MotionEvent mDownEvent;
     private boolean mExpandAnimationRunning;
     private NotificationStackScrollLayout mStackScrollLayout;
     private PhoneStatusBarViewController mStatusBarViewController;
@@ -137,8 +141,8 @@
             AlternateBouncerInteractor alternateBouncerInteractor,
             UdfpsOverlayInteractor udfpsOverlayInteractor,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
-            PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
-    ) {
+            PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+            FeatureFlags featureFlags) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
@@ -159,6 +163,7 @@
         mNotificationInsetsController = notificationInsetsController;
         mAlternateBouncerInteractor = alternateBouncerInteractor;
         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
+        mIsTrackpadGestureBackEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_BACK);
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -219,9 +224,11 @@
                 if (isDown) {
                     mTouchActive = true;
                     mTouchCancelled = false;
+                    mDownEvent = ev;
                 } else if (ev.getActionMasked() == MotionEvent.ACTION_UP
                         || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
                     mTouchActive = false;
+                    mDownEvent = null;
                 }
                 if (mTouchCancelled || mExpandAnimationRunning) {
                     return false;
@@ -447,9 +454,17 @@
     public void cancelCurrentTouch() {
         if (mTouchActive) {
             final long now = SystemClock.uptimeMillis();
-            MotionEvent event = MotionEvent.obtain(now, now,
-                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+            final MotionEvent event;
+            if (mIsTrackpadGestureBackEnabled) {
+                event = MotionEvent.obtain(mDownEvent);
+                event.setDownTime(now);
+                event.setAction(MotionEvent.ACTION_CANCEL);
+                event.setLocation(0.0f, 0.0f);
+            } else {
+                event = MotionEvent.obtain(now, now,
+                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+            }
             mView.dispatchTouchEvent(event);
             event.recycle();
             mTouchCancelled = true;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 0e837d2..a35e5b5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -18,12 +18,15 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -37,6 +40,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
+@TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
     @Mock
@@ -45,14 +49,14 @@
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
     private KeyguardMessageArea mKeyguardMessageArea;
-
     private KeyguardMessageAreaController mMessageAreaController;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mMessageAreaController = new KeyguardMessageAreaController.Factory(
-                mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea);
+                mKeyguardUpdateMonitor, mConfigurationController).create(
+                mKeyguardMessageArea);
     }
 
     @Test
@@ -89,6 +93,19 @@
     }
 
     @Test
+    public void testSetMessage_AnnounceForAccessibility() {
+        ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+        when(mKeyguardMessageArea.getText()).thenReturn("abc");
+        mMessageAreaController.setMessage("abc");
+
+        verify(mKeyguardMessageArea).setMessage("abc", /* animate= */ true);
+        verify(mKeyguardMessageArea).removeCallbacks(any(Runnable.class));
+        verify(mKeyguardMessageArea).postDelayed(argumentCaptor.capture(), anyLong());
+        argumentCaptor.getValue().run();
+        verify(mKeyguardMessageArea).announceForAccessibility("abc");
+    }
+
+    @Test
     public void testSetBouncerVisible() {
         mMessageAreaController.setIsVisible(true);
         verify(mKeyguardMessageArea).setIsVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 38d3a3e..f966eb3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -66,6 +66,7 @@
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
@@ -619,13 +620,26 @@
 
     @Test
     public void testReinflateViewFlipper() {
-        mKeyguardSecurityContainerController.reinflateViewFlipper();
+        mKeyguardSecurityContainerController.reinflateViewFlipper(() -> {});
         verify(mKeyguardSecurityViewFlipperController).clearViews();
         verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
                 any(KeyguardSecurityCallback.class));
     }
 
     @Test
+    public void testReinflateViewFlipper_asyncBouncerFlagOn() {
+        when(mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)).thenReturn(true);
+        KeyguardSecurityViewFlipperController.OnViewInflatedListener onViewInflatedListener =
+                () -> {
+                };
+        mKeyguardSecurityContainerController.reinflateViewFlipper(onViewInflatedListener);
+        verify(mKeyguardSecurityViewFlipperController).clearViews();
+        verify(mKeyguardSecurityViewFlipperController).asynchronouslyInflateView(
+                any(SecurityMode.class),
+                any(KeyguardSecurityCallback.class), eq(onViewInflatedListener));
+    }
+
+    @Test
     public void testSideFpsControllerShow() {
         mKeyguardSecurityContainerController.updateSideFpsVisibility(/* isVisible= */ true);
         verify(mSideFpsController).show(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 1614b57..afb54d2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -31,10 +31,12 @@
 import android.view.ViewGroup;
 import android.view.WindowInsetsController;
 
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -57,6 +59,8 @@
     @Mock
     private LayoutInflater mLayoutInflater;
     @Mock
+    private AsyncLayoutInflater mAsyncLayoutInflater;
+    @Mock
     private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
     @Mock
     private EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
@@ -70,6 +74,8 @@
     private WindowInsetsController mWindowInsetsController;
     @Mock
     private KeyguardSecurityCallback mKeyguardSecurityCallback;
+    @Mock
+    private FeatureFlags mFeatureFlags;
 
     private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
 
@@ -82,10 +88,11 @@
         when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
         when(mEmergencyButtonControllerFactory.create(any(EmergencyButton.class)))
                 .thenReturn(mEmergencyButtonController);
+        when(mView.getContext()).thenReturn(getContext());
 
         mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView,
-                mLayoutInflater, mKeyguardSecurityViewControllerFactory,
-                mEmergencyButtonControllerFactory);
+                mLayoutInflater, mAsyncLayoutInflater, mKeyguardSecurityViewControllerFactory,
+                mEmergencyButtonControllerFactory, mFeatureFlags);
     }
 
     @Test
@@ -108,6 +115,14 @@
     }
 
     @Test
+    public void asynchronouslyInflateView() {
+        mKeyguardSecurityViewFlipperController.asynchronouslyInflateView(SecurityMode.PIN,
+                mKeyguardSecurityCallback, null);
+        verify(mAsyncLayoutInflater).inflate(anyInt(), eq(mView), any(
+                AsyncLayoutInflater.OnInflateFinishedListener.class));
+    }
+
+    @Test
     public void onDensityOrFontScaleChanged() {
         mKeyguardSecurityViewFlipperController.clearViews();
         verify(mView).removeAllViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 51492eb..0dc2d17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -29,6 +29,8 @@
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.dock.DockManager
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -112,6 +114,9 @@
             .thenReturn(keyguardSecurityContainerController)
         whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
             .thenReturn(emptyFlow<TransitionStep>())
+
+        val featureFlags = FakeFeatureFlags();
+        featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false)
         underTest =
             NotificationShadeWindowViewController(
                 lockscreenShadeTransitionController,
@@ -138,6 +143,7 @@
                 udfpsOverlayInteractor,
                 keyguardTransitionInteractor,
                 primaryBouncerToGoneTransitionViewModel,
+                featureFlags,
             )
         underTest.setupExpandedStatusBar()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 2f528a8..2797440 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -43,6 +43,8 @@
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -132,6 +134,8 @@
         when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition())
                 .thenReturn(emptyFlow());
 
+        FakeFeatureFlags featureFlags = new FakeFeatureFlags();
+        featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false);
         mController = new NotificationShadeWindowViewController(
                 mLockscreenShadeTransitionController,
                 new FalsingCollectorFake(),
@@ -156,7 +160,8 @@
                 mAlternateBouncerInteractor,
                 mUdfpsOverlayInteractor,
                 mKeyguardTransitionInteractor,
-                mPrimaryBouncerToGoneTransitionViewModel
+                mPrimaryBouncerToGoneTransitionViewModel,
+                featureFlags
         );
         mController.setupExpandedStatusBar();
         mController.setDragDownHelper(mDragDownHelper);
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index bac9253..8e93c1b 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -69,7 +69,7 @@
     // define the encoding of that data in an integer.
 
     static final int MAX_HISTORIC_STATES = 8;   // Maximum number of historic states we will keep.
-    static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
+    static final String STATE_FILE_PREFIX = "state-v2-"; // Prefix to use for state filenames.
     static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
     static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
     static long WRITE_PERIOD = 30*60*1000;      // Write file every 30 minutes or so.
@@ -462,6 +462,10 @@
             File file = files[i];
             String fileStr = file.getPath();
             if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr);
+            if (!file.getName().startsWith(STATE_FILE_PREFIX)) {
+                if (DEBUG) Slog.d(TAG, "Skipping: mismatching prefix");
+                continue;
+            }
             if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) {
                 if (DEBUG) Slog.d(TAG, "Skipping: already checked in");
                 continue;
@@ -478,6 +482,14 @@
 
     @GuardedBy("mFileLock")
     private void trimHistoricStatesWriteLF() {
+        File[] files = mBaseDir.listFiles();
+        if (files != null) {
+            for (int i = 0; i < files.length; i++) {
+                if (!files[i].getName().startsWith(STATE_FILE_PREFIX)) {
+                    files[i].delete();
+                }
+            }
+        }
         ArrayList<String> filesArray = getCommittedFilesLF(MAX_HISTORIC_STATES, false, true);
         if (filesArray == null) {
             return;
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 5e0a180..8417049 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -62,7 +62,7 @@
 public class ValidateNotificationPeople implements NotificationSignalExtractor {
     // Using a shorter log tag since setprop has a limit of 32chars on variable name.
     private static final String TAG = "ValidateNoPeople";
-    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);;
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
@@ -105,12 +105,13 @@
     private int mEvictionCount;
     private NotificationUsageStats mUsageStats;
 
+    @Override
     public void initialize(Context context, NotificationUsageStats usageStats) {
         if (DEBUG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
         mUserToContextMap = new ArrayMap<>();
         mBaseContext = context;
         mUsageStats = usageStats;
-        mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
+        mPeopleCache = new LruCache<>(PEOPLE_CACHE_SIZE);
         mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
                 mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
         if (mEnabled) {
@@ -134,7 +135,7 @@
     // For tests: just do the setting of various local variables without actually doing work
     @VisibleForTesting
     protected void initForTests(Context context, NotificationUsageStats usageStats,
-            LruCache peopleCache) {
+            LruCache<String, LookupResult> peopleCache) {
         mUserToContextMap = new ArrayMap<>();
         mBaseContext = context;
         mUsageStats = usageStats;
@@ -142,6 +143,7 @@
         mEnabled = true;
     }
 
+    @Override
     public RankingReconsideration process(NotificationRecord record) {
         if (!mEnabled) {
             if (VERBOSE) Slog.i(TAG, "disabled");
@@ -272,7 +274,7 @@
         }
 
         if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
-        final LinkedList<String> pendingLookups = new LinkedList<String>();
+        final LinkedList<String> pendingLookups = new LinkedList<>();
         int personIdx = 0;
         for (String handle : people) {
             if (TextUtils.isEmpty(handle)) continue;
@@ -320,7 +322,6 @@
         return Integer.toString(userId) + ":" + handle;
     }
 
-    // VisibleForTesting
     public static String[] getExtraPeople(Bundle extras) {
         String[] peopleList = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE_LIST);
         String[] legacyPeople = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE);
@@ -417,101 +418,6 @@
         return null;
     }
 
-    private LookupResult resolvePhoneContact(Context context, final String number) {
-        Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
-                Uri.encode(number));
-        return searchContacts(context, phoneUri);
-    }
-
-    private LookupResult resolveEmailContact(Context context, final String email) {
-        Uri numberUri = Uri.withAppendedPath(
-                ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
-                Uri.encode(email));
-        return searchContacts(context, numberUri);
-    }
-
-    @VisibleForTesting
-    LookupResult searchContacts(Context context, Uri lookupUri) {
-        LookupResult lookupResult = new LookupResult();
-        final Uri corpLookupUri =
-                ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri);
-        if (corpLookupUri == null) {
-            addContacts(lookupResult, context, lookupUri);
-        } else {
-            addWorkContacts(lookupResult, context, corpLookupUri);
-        }
-        return lookupResult;
-    }
-
-    @VisibleForTesting
-    // Performs a contacts search using searchContacts, and then follows up by looking up
-    // any phone numbers associated with the resulting contact information and merge those
-    // into the lookup result as well. Will have no additional effect if the contact does
-    // not have any phone numbers.
-    LookupResult searchContactsAndLookupNumbers(Context context, Uri lookupUri) {
-        LookupResult lookupResult = searchContacts(context, lookupUri);
-        String phoneLookupKey = lookupResult.getPhoneLookupKey();
-        if (phoneLookupKey != null) {
-            String selection = Contacts.LOOKUP_KEY + " = ?";
-            String[] selectionArgs = new String[] { phoneLookupKey };
-            try (Cursor cursor = context.getContentResolver().query(
-                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONE_LOOKUP_PROJECTION,
-                    selection, selectionArgs, /* sortOrder= */ null)) {
-                if (cursor == null) {
-                    Slog.w(TAG, "Cursor is null when querying contact phone number.");
-                    return lookupResult;
-                }
-
-                while (cursor.moveToNext()) {
-                    lookupResult.mergePhoneNumber(cursor);
-                }
-            } catch (Throwable t) {
-                Slog.w(TAG, "Problem getting content resolver or querying phone numbers.", t);
-            }
-        }
-        return lookupResult;
-    }
-
-    private void addWorkContacts(LookupResult lookupResult, Context context, Uri corpLookupUri) {
-        final int workUserId = findWorkUserId(context);
-        if (workUserId == -1) {
-            Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri);
-            return;
-        }
-        final Uri corpLookupUriWithUserId =
-                ContentProvider.maybeAddUserId(corpLookupUri, workUserId);
-        addContacts(lookupResult, context, corpLookupUriWithUserId);
-    }
-
-    /** Returns the user ID of the managed profile or -1 if none is found. */
-    private int findWorkUserId(Context context) {
-        final UserManager userManager = context.getSystemService(UserManager.class);
-        final int[] profileIds =
-                userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true);
-        for (int profileId : profileIds) {
-            if (userManager.isManagedProfile(profileId)) {
-                return profileId;
-            }
-        }
-        return -1;
-    }
-
-    /** Modifies the given lookup result to add contacts found at the given URI. */
-    private void addContacts(LookupResult lookupResult, Context context, Uri uri) {
-        try (Cursor c = context.getContentResolver().query(
-                uri, LOOKUP_PROJECTION, null, null, null)) {
-            if (c == null) {
-                Slog.w(TAG, "Null cursor from contacts query.");
-                return;
-            }
-            while (c.moveToNext()) {
-                lookupResult.mergeContact(c);
-            }
-        } catch (Throwable t) {
-            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
-        }
-    }
-
     @VisibleForTesting
     protected static class LookupResult {
         private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
@@ -619,19 +525,18 @@
         }
     }
 
-    private class PeopleRankingReconsideration extends RankingReconsideration {
+    @VisibleForTesting
+    class PeopleRankingReconsideration extends RankingReconsideration {
         private final LinkedList<String> mPendingLookups;
         private final Context mContext;
 
-        // Amount of time to wait for a result from the contacts db before rechecking affinity.
-        private static final long LOOKUP_TIME = 1000;
         private float mContactAffinity = NONE;
         private ArraySet<String> mPhoneNumbers = null;
         private NotificationRecord mRecord;
 
         private PeopleRankingReconsideration(Context context, String key,
                 LinkedList<String> pendingLookups) {
-            super(key, LOOKUP_TIME);
+            super(key);
             mContext = context;
             mPendingLookups = pendingLookups;
         }
@@ -642,7 +547,7 @@
             long timeStartMs = System.currentTimeMillis();
             for (final String handle: mPendingLookups) {
                 final String cacheKey = getCacheKey(mContext.getUserId(), handle);
-                LookupResult lookupResult = null;
+                LookupResult lookupResult;
                 boolean cacheHit = false;
                 synchronized (mPeopleCache) {
                     lookupResult = mPeopleCache.get(cacheKey);
@@ -703,6 +608,102 @@
             }
         }
 
+        private static LookupResult resolvePhoneContact(Context context, final String number) {
+            Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+                    Uri.encode(number));
+            return searchContacts(context, phoneUri);
+        }
+
+        private static LookupResult resolveEmailContact(Context context, final String email) {
+            Uri numberUri = Uri.withAppendedPath(
+                    ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
+                    Uri.encode(email));
+            return searchContacts(context, numberUri);
+        }
+
+        @VisibleForTesting
+        static LookupResult searchContacts(Context context, Uri lookupUri) {
+            LookupResult lookupResult = new LookupResult();
+            final Uri corpLookupUri =
+                    ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri);
+            if (corpLookupUri == null) {
+                addContacts(lookupResult, context, lookupUri);
+            } else {
+                addWorkContacts(lookupResult, context, corpLookupUri);
+            }
+            return lookupResult;
+        }
+
+        @VisibleForTesting
+        // Performs a contacts search using searchContacts, and then follows up by looking up
+        // any phone numbers associated with the resulting contact information and merge those
+        // into the lookup result as well. Will have no additional effect if the contact does
+        // not have any phone numbers.
+        static LookupResult searchContactsAndLookupNumbers(Context context, Uri lookupUri) {
+            LookupResult lookupResult = searchContacts(context, lookupUri);
+            String phoneLookupKey = lookupResult.getPhoneLookupKey();
+            if (phoneLookupKey != null) {
+                String selection = Contacts.LOOKUP_KEY + " = ?";
+                String[] selectionArgs = new String[] { phoneLookupKey };
+                try (Cursor cursor = context.getContentResolver().query(
+                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONE_LOOKUP_PROJECTION,
+                        selection, selectionArgs, /* sortOrder= */ null)) {
+                    if (cursor == null) {
+                        Slog.w(TAG, "Cursor is null when querying contact phone number.");
+                        return lookupResult;
+                    }
+
+                    while (cursor.moveToNext()) {
+                        lookupResult.mergePhoneNumber(cursor);
+                    }
+                } catch (Throwable t) {
+                    Slog.w(TAG, "Problem getting content resolver or querying phone numbers.", t);
+                }
+            }
+            return lookupResult;
+        }
+
+        private static void addWorkContacts(LookupResult lookupResult, Context context,
+                Uri corpLookupUri) {
+            final int workUserId = findWorkUserId(context);
+            if (workUserId == -1) {
+                Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri);
+                return;
+            }
+            final Uri corpLookupUriWithUserId =
+                    ContentProvider.maybeAddUserId(corpLookupUri, workUserId);
+            addContacts(lookupResult, context, corpLookupUriWithUserId);
+        }
+
+        /** Returns the user ID of the managed profile or -1 if none is found. */
+        private static int findWorkUserId(Context context) {
+            final UserManager userManager = context.getSystemService(UserManager.class);
+            final int[] profileIds =
+                    userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true);
+            for (int profileId : profileIds) {
+                if (userManager.isManagedProfile(profileId)) {
+                    return profileId;
+                }
+            }
+            return -1;
+        }
+
+        /** Modifies the given lookup result to add contacts found at the given URI. */
+        private static void addContacts(LookupResult lookupResult, Context context, Uri uri) {
+            try (Cursor c = context.getContentResolver().query(
+                    uri, LOOKUP_PROJECTION, null, null, null)) {
+                if (c == null) {
+                    Slog.w(TAG, "Null cursor from contacts query.");
+                    return;
+                }
+                while (c.moveToNext()) {
+                    lookupResult.mergeContact(c);
+                }
+            } catch (Throwable t) {
+                Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+            }
+        }
+
         @Override
         public void applyChangesLocked(NotificationRecord operand) {
             float affinityBound = operand.getContactAffinity();
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7fe6c7d..569999e 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1134,22 +1134,22 @@
         // behavior.
         if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                 "MinInstallableTargetSdk__install_block_enabled",
-                false)) {
+                true)) {
             int minInstallableTargetSdk =
                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                             "MinInstallableTargetSdk__min_installable_target_sdk",
-                            0);
+                            PackageManagerService.MIN_INSTALLABLE_TARGET_SDK);
 
             // Determine if enforcement is in strict mode
             boolean strictMode = false;
 
             if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                     "MinInstallableTargetSdk__install_block_strict_mode_enabled",
-                    false)) {
+                    true)) {
                 if (parsedPackage.getTargetSdkVersion()
                         < DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
                         "MinInstallableTargetSdk__strict_mode_target_sdk",
-                        0)) {
+                        PackageManagerService.MIN_INSTALLABLE_TARGET_SDK)) {
                     strictMode = true;
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d3ee52c..c5d7d07 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -557,6 +557,14 @@
     // How many required verifiers can be on the system.
     private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2;
 
+    /**
+     * Specifies the minimum target SDK version an apk must specify in order to be installed
+     * on the system. This improves security and privacy by blocking low
+     * target sdk apps as malware can target older sdk versions to avoid
+     * the enforcement of new API behavior.
+     */
+    public static final int MIN_INSTALLABLE_TARGET_SDK = Build.VERSION_CODES.M;
+
     // Compilation reasons.
     // TODO(b/260124949): Clean this up with the legacy dexopt code.
     public static final int REASON_FIRST_BOOT = 0;
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index b67850a..3710af6 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -63,19 +63,39 @@
 /**
  * Class responsible for deciding whether a user is visible (or visible for a given display).
  *
- * <p>Currently, it has 2 "modes" (set on constructor), which defines the class behavior (i.e, the
+ * <p>Currently, it has 3 "modes" (set on constructor), which defines the class behavior (i.e, the
  * logic that dictates the result of methods such as {@link #isUserVisible(int)} and
  * {@link #isUserVisible(int, int)}):
  *
  * <ul>
- *   <li>default: this is the most common mode (used by phones, tablets, foldables, automotives with
- *   just cluster and driver displayes, etc...), where the logic is based solely on the current
- *   foreground user (and its started profiles)
- *   <li>{@code MUMD}: mode for "(concurrent) Multiple Users on Multiple Displays", which is used on
- *   automotives with passenger display. In this mode, users started in background on the secondary
- *   display are stored in map.
+ *   <li>default (A.K.A {@code SUSD} - Single User on Single Display): this is the most common mode
+ *   (used by phones, tablets, foldables, cars with just cluster and driver displays, etc.),
+ *   where just the current foreground user and its profiles are visible; hence, most methods are
+ *   optimized to just check for the current user / profile. This mode is unit tested by
+ *   {@link com.android.server.pm.UserVisibilityMediatorSUSDTest} and CTS tested by
+ *   {@link android.multiuser.cts.UserVisibilityTest}.
+ *   <li>concurrent users (A.K.A. {@code MUMD} - Multiple Users on Multiple Displays): typically
+ *   used on automotive builds where the car has additional displays for passengers, it allows users
+ *   to be started in the background but visible on these displays; hence, it contains additional
+ *   maps to account for the visibility state. This mode is unit tested by
+ *   {@link com.android.server.pm.UserVisibilityMediatorMUMDTest} and CTS tested by
+ *   {@link android.multiuser.cts.UserVisibilityTest}.
+ *   <li>no driver (A.K.A. {@code MUPAND} - MUltiple PAssengers, No Driver): extension of the
+ *   previous mode and typically used on automotive builds where the car has additional displays for
+ *   passengers but uses a secondary Android system for the back passengers, so all "human" users
+ *   are started in the background (and the current foreground user is the system user), hence the
+ *   "no driver name". This mode is unit tested by
+ *   {@link com.android.server.pm.UserVisibilityMediatorMUPANDTest} and CTS tested by
+ *   {@link android.multiuser.cts.UserVisibilityVisibleBackgroundUsersOnDefaultDisplayTest}.
  * </ul>
  *
+ * <p>When you make changes in this class, you should run at least the 3 unit tests and
+ * {@link android.multiuser.cts.UserVisibilityTest} (which actually applies for all modes); for
+ * example, by calling {@code atest UserVisibilityMediatorSUSDTest UserVisibilityMediatorMUMDTest
+ * UserVisibilityMediatorMUPANDTest UserVisibilityTest}. Ideally, you should run the other 2 CTS
+ * tests as well (you can emulate these modes using {@code adb} commands; their javadoc provides
+ * instructions on how to do so).
+ *
  * <p>This class is thread safe.
  */
 public final class UserVisibilityMediator implements Dumpable {
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 5d038dc..be7d9b6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -92,6 +92,7 @@
         } else {
             mInputWindowHandleWrapper.setInputConfigMasked(0, InputConfig.NOT_TOUCHABLE);
         }
+        mInputWindowHandleWrapper.setDisplayId(mActivityRecord.getDisplayId());
         return mInputWindowHandleWrapper;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c3c87af..8652914 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -135,7 +135,7 @@
      */
     static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
             | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE
-            | ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+            | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_DENSITY;
     static final int CONTROLLABLE_WINDOW_CONFIGS = WINDOW_CONFIG_BOUNDS
             | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index d72cfc7..0564a73 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.notification;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -51,6 +53,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.UiServiceTestCase;
+import com.android.server.notification.ValidateNotificationPeople.LookupResult;
+import com.android.server.notification.ValidateNotificationPeople.PeopleRankingReconsideration;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -60,6 +64,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -215,7 +220,7 @@
                 ContactsContract.Contacts.CONTENT_LOOKUP_URI,
                 ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX + contactId);
 
-        new ValidateNotificationPeople().searchContacts(mockContext, lookupUri);
+        PeopleRankingReconsideration.searchContacts(mockContext, lookupUri);
 
         ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class);
         verify(mockContentResolver).query(
@@ -242,7 +247,7 @@
         final Uri lookupUri = Uri.withAppendedPath(
                 ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId));
 
-        new ValidateNotificationPeople().searchContacts(mockContext, lookupUri);
+        PeopleRankingReconsideration.searchContacts(mockContext, lookupUri);
 
         ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class);
         verify(mockContentResolver).query(
@@ -277,7 +282,7 @@
 
         // call searchContacts and then mergePhoneNumbers, make sure we never actually
         // query the content resolver for a phone number
-        new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri);
+        PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri);
         verify(mockContentResolver, never()).query(
                 eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
                 eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION),
@@ -320,7 +325,7 @@
 
         // call searchContacts and then mergePhoneNumbers, and check that we query
         // once for the
-        new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri);
+        PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri);
         verify(mockContentResolver, times(1)).query(
                 eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
                 eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION),
@@ -339,7 +344,7 @@
 
         // Create validator with empty cache
         ValidateNotificationPeople vnp = new ValidateNotificationPeople();
-        LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5);
+        LruCache<String, LookupResult> cache = new LruCache<>(5);
         vnp.initForTests(mockContext, mockNotificationUsageStats, cache);
 
         NotificationRecord record = getNotificationRecord();
@@ -366,9 +371,8 @@
         float affinity = 0.7f;
 
         // Create a fake LookupResult for the data we'll pass in
-        LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5);
-        ValidateNotificationPeople.LookupResult lr =
-                mock(ValidateNotificationPeople.LookupResult.class);
+        LruCache<String, LookupResult> cache = new LruCache<>(5);
+        LookupResult lr = mock(LookupResult.class);
         when(lr.getAffinity()).thenReturn(affinity);
         when(lr.getPhoneNumbers()).thenReturn(new ArraySet<>(new String[]{lookupTel}));
         when(lr.isExpired()).thenReturn(false);
@@ -392,6 +396,23 @@
         assertTrue(record.getPhoneNumbers().contains(lookupTel));
     }
 
+    @Test
+    public void validatePeople_reconsiderationWillNotBeDelayed() {
+        final Context mockContext = mock(Context.class);
+        final ContentResolver mockContentResolver = mock(ContentResolver.class);
+        when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
+        ValidateNotificationPeople vnp = new ValidateNotificationPeople();
+        vnp.initForTests(mockContext, mock(NotificationUsageStats.class), new LruCache<>(5));
+        NotificationRecord record = getNotificationRecord();
+        String[] callNumber = new String[]{"tel:12345678910"};
+        setNotificationPeople(record, callNumber);
+
+        RankingReconsideration rr = vnp.validatePeople(mockContext, record);
+
+        assertThat(rr).isNotNull();
+        assertThat(rr.getDelay(TimeUnit.MILLISECONDS)).isEqualTo(0);
+    }
+
     // Creates a cursor that points to one item of Contacts data with the specified
     // columns.
     private Cursor makeMockCursor(int id, String lookupKey, int starred, int hasPhone) {