Merge "Make pending intents of uninstall failure immutable"
diff --git a/api/system-current.txt b/api/system-current.txt
index 5aec633..6a044508 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3674,8 +3674,13 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+    field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
     field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
     field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+    field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+    field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+    field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+    field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
     field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
     field public static final long FUNCTION_ADB = 1L; // 0x1L
     field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
diff --git a/api/test-current.txt b/api/test-current.txt
index 113d585..e11fcde 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -144,6 +144,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void stopSystemLockTaskMode();
     method public static boolean supportsMultiWindow(android.content.Context);
     method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
+    field public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440; // 0x1b8
     field public static final int INVALID_STACK_ID = -1; // 0xffffffff
     field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
     field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 0617eb6..124f815f 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -216,10 +216,6 @@
             //    address: true,
             //},
         },
-        debuggable: {
-            // Add a flag to enable stats log printing from statsd on debug builds.
-            cflags: ["-DVERY_VERBOSE_PRINTING"],
-        },
     },
 
     proto: {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index e7b32c5..05e9ec3 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -409,11 +409,9 @@
         onWatchdogRollbackOccurredLocked(event);
     }
 
-#ifdef VERY_VERBOSE_PRINTING
     if (mPrintAllLogs) {
         ALOGI("%s", event->ToString().c_str());
     }
-#endif
     resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
 
     // Hard-coded logic to update the isolated uid's in the uid-map.
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 23f2584..c0f54a0 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -139,10 +139,8 @@
     int64_t getLastReportTimeNs(const ConfigKey& key);
 
     inline void setPrintLogs(bool enabled) {
-#ifdef VERY_VERBOSE_PRINTING
         std::lock_guard<std::mutex> lock(mMetricsMutex);
         mPrintAllLogs = enabled;
-#endif
     }
 
     // Add a specific config key to the possible configs to dump ASAP.
@@ -276,9 +274,7 @@
     //Last time we wrote metadata to disk.
     int64_t mLastMetadataWriteNs = 0;
 
-#ifdef VERY_VERBOSE_PRINTING
     bool mPrintAllLogs = false;
-#endif
 
     FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
     FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 3226482..d5e3314 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -484,7 +484,8 @@
     dprintf(out, "  Clear cached puller data.\n");
     dprintf(out, "\n");
     dprintf(out, "usage: adb shell cmd stats print-logs\n");
-    dprintf(out, "      Only works on eng build\n");
+    dprintf(out, "  Requires root privileges.\n");
+    dprintf(out, "  Can be disabled by calling adb shell cmd stats print-logs 0\n");
 }
 
 status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
@@ -865,18 +866,19 @@
 }
 
 status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
-    VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
-         AIBinder_getCallingUid());
-    if (checkPermission(kPermissionDump)) {
-        bool enabled = true;
-        if (args.size() >= 2) {
-            enabled = atoi(args[1].c_str()) != 0;
-        }
-        mProcessor->setPrintLogs(enabled);
-        return NO_ERROR;
-    } else {
+    Status status = checkUid(AID_ROOT);
+    if (!status.isOk()) {
         return PERMISSION_DENIED;
     }
+
+    VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(),
+         AIBinder_getCallingUid());
+    bool enabled = true;
+    if (args.size() >= 2) {
+        enabled = atoi(args[1].c_str()) != 0;
+    }
+    mProcessor->setPrintLogs(enabled);
+    return NO_ERROR;
 }
 
 bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 0f31529..4283d7a 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -31,6 +31,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.DisplayMetrics;
 import android.util.Singleton;
 
 import java.util.List;
@@ -139,6 +140,8 @@
     public static final String EXTRA_IGNORE_TARGET_SECURITY =
             "android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
 
+    /** The minimal size of a display's long-edge needed to support split-screen multi-window. */
+    public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440;
 
     private static int sMaxRecentTasks = -1;
 
@@ -282,8 +285,23 @@
                 com.android.internal.R.bool.config_supportsMultiWindow);
     }
 
-    /** Returns true if the system supports split screen multi-window. */
+    /**
+     * Returns {@code true} if the display the context is associated with supports split screen
+     * multi-window.
+     *
+     * @throws UnsupportedOperationException if the supplied {@link Context} is not associated with
+     * a display.
+     */
     public static boolean supportsSplitScreenMultiWindow(Context context) {
+        DisplayMetrics dm = new DisplayMetrics();
+        context.getDisplay().getRealMetrics(dm);
+
+        int widthDp = (int) (dm.widthPixels / dm.density);
+        int heightDp = (int) (dm.heightPixels / dm.density);
+        if (Math.max(widthDp, heightDp) < DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP) {
+            return false;
+        }
+
         return supportsMultiWindow(context)
                 && Resources.getSystem().getBoolean(
                 com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 89e1f5a..a2947a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -86,6 +86,8 @@
 import android.graphics.HardwareRenderer;
 import android.hardware.display.DisplayManagerGlobal;
 import android.inputmethodservice.InputMethodService;
+import android.media.MediaFrameworkInitializer;
+import android.media.MediaServiceManager;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.Proxy;
@@ -7665,6 +7667,7 @@
     public static void initializeMainlineModules() {
         TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager());
         StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager());
+        MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
     }
 
     private void purgePendingResources() {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3410c92..f82ab7b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4801,7 +4801,6 @@
             contentView.setViewVisibility(R.id.time, View.GONE);
             contentView.setImageViewIcon(R.id.profile_badge, null);
             contentView.setViewVisibility(R.id.profile_badge, View.GONE);
-            contentView.setViewVisibility(R.id.alerted_icon, View.GONE);
             mN.mUsesStandardHeader = false;
         }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c20c11f..59997cc 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -101,11 +101,11 @@
 import android.location.ILocationManager;
 import android.location.LocationManager;
 import android.media.AudioManager;
+import android.media.MediaFrameworkInitializer;
 import android.media.MediaRouter;
 import android.media.midi.IMidiManager;
 import android.media.midi.MidiManager;
 import android.media.projection.MediaProjectionManager;
-import android.media.session.MediaSessionManager;
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
 import android.media.tv.TvInputManager;
@@ -855,13 +855,6 @@
                 return new ConsumerIrManager(ctx);
             }});
 
-        registerService(Context.MEDIA_SESSION_SERVICE, MediaSessionManager.class,
-                new CachedServiceFetcher<MediaSessionManager>() {
-            @Override
-            public MediaSessionManager createService(ContextImpl ctx) {
-                return new MediaSessionManager(ctx);
-            }});
-
         registerService(Context.TRUST_SERVICE, TrustManager.class,
                 new StaticServiceFetcher<TrustManager>() {
             @Override
@@ -1335,6 +1328,7 @@
             WifiFrameworkInitializer.registerServiceWrappers();
             StatsFrameworkInitializer.registerServiceWrappers();
             RollbackManagerFrameworkInitializer.initialize();
+            MediaFrameworkInitializer.registerServiceWrappers();
         } finally {
             // If any of the above code throws, we're in a pretty bad shape and the process
             // will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 940bf26..f533760 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4673,8 +4673,7 @@
      * Marks an application exempt from having its permissions be automatically revoked when
      * the app is unused for an extended period of time.
      *
-     * Only the installer on record that installed the given package, or a holder of
-     * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+     * Only the installer on record that installed the given package is allowed to call this.
      *
      * Packages start in whitelisted state, and it is the installer's responsibility to
      * un-whitelist the packages it installs, unless auto-revoking permissions from that package
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 58cb1e1..c7fc2ad 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -610,7 +610,7 @@
                                     serverCallback.sendResult(null /* data */);
                                 }
                             }
-                        });
+                        }, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index f4122fe..2f8c97f 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -90,7 +90,7 @@
     void resetLockout(int userId, in byte [] hardwareAuthToken);
 
     // Add a callback which gets notified when the face lockout period expired.
-    void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
+    void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
 
     void setFeature(IBinder token, int userId, int feature, boolean enabled,
             in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, String opPackageName);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index abe63d6..9dacca7 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -768,7 +768,7 @@
                             serverCallback.sendResult(null /* data */);
                         }
                     }
-                });
+                }, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 5adba75..ad58fea 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -94,7 +94,7 @@
     void resetLockout(int userId, in byte [] hardwareAuthToken);
 
     // Add a callback which gets notified when the fingerprint lockout period expired.
-    void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
+    void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
 
     // Check if a client request is currently being handled
     boolean isClientActive();
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index a94266b..f4cfc74 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -24,7 +24,6 @@
 import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
-import java.util.Objects;
 
 import java.util.Objects;
 
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index d16f070..ef305e2 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -160,6 +160,22 @@
             "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
 
     /**
+     * Broadcast Action:  A broadcast for USB accessory handshaking information delivery
+     *
+     * This intent is sent when a USB accessory connect attempt
+     *
+     * <p>For more information about communicating with USB accessory handshake, refer to
+     * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+     *
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public static final String ACTION_USB_ACCESSORY_HANDSHAKE =
+            "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
+
+    /**
      * Boolean extra indicating whether USB is connected or disconnected.
      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
      *
@@ -303,6 +319,52 @@
     public static final String EXTRA_ACCESSORY = "accessory";
 
     /**
+     * A long extra indicating ms from boot to get get_protocol UEvent
+     * This is obtained with SystemClock.elapsedRealtime()
+     * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+     *
+     * {@hide}
+     */
+    @SystemApi
+    public static final String EXTRA_ACCESSORY_UEVENT_TIME =
+            "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
+
+    /**
+     * An integer extra indicating numbers of send string during handshake
+     * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts
+     *
+     * <p>For more information about control request with identifying string information
+     * between communicating with USB accessory handshake, refer to
+     * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+     *
+     * {@hide}
+     */
+    @SystemApi
+    public static final String EXTRA_ACCESSORY_STRING_COUNT =
+            "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+
+    /**
+     * Boolean extra indicating whether got start accessory or not
+     * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+     *
+     * {@hide}
+     */
+    @SystemApi
+    public static final String EXTRA_ACCESSORY_START =
+            "android.hardware.usb.extra.ACCESSORY_START";
+
+    /**
+     * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
+     * This is obtained with SystemClock.elapsedRealtime()
+     * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+     *
+     * {@hide}
+     */
+    @SystemApi
+    public static final String EXTRA_ACCESSORY_HANDSHAKE_END =
+            "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+
+    /**
      * Name of extra added to the {@link android.app.PendingIntent}
      * passed into {@link #requestPermission(UsbDevice, PendingIntent)}
      * or {@link #requestPermission(UsbAccessory, PendingIntent)}
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0e10c42..0eb3c1e 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -38,7 +38,9 @@
  * Representation of a MAC address.
  *
  * This class only supports 48 bits long addresses and does not support 64 bits long addresses.
- * Instances of this class are immutable.
+ * Instances of this class are immutable. This class provides implementations of hashCode()
+ * and equals() that make it suitable for use as keys in standard implementations of
+ * {@link java.util.Map}.
  */
 public final class MacAddress implements Parcelable {
 
@@ -122,12 +124,22 @@
     }
 
     /**
+     * Convert this MacAddress to a byte array.
+     *
+     * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6,
+     * the returned array is [1, 2, 3, 4, 5, 6].
+     *
      * @return a byte array representation of this MacAddress.
      */
     public @NonNull byte[] toByteArray() {
         return byteAddrFromLongAddr(mAddr);
     }
 
+    /**
+     * Returns a human-readable representation of this MacAddress.
+     * The exact format is implementation-dependent and should not be assumed to have any
+     * particular format.
+     */
     @Override
     public @NonNull String toString() {
         return stringAddrFromLongAddr(mAddr);
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index d35ce5b..32c80e7 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -159,7 +159,7 @@
      *
      * @param fileSize - size of the signed file (APK)
      */
-    public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
+    public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo,
             SigningInfo signingInfo) {
         final int size =
                 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d44c7fd..feebb94 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7883,19 +7883,26 @@
         public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
 
         /**
+         * For user preference if swipe bottom to expand notification gesture enabled.
+         * @hide
+         */
+        public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
+                "swipe_bottom_to_notification_enabled";
+
+        /**
          * For user preference if One-Handed Mode enabled.
          * @hide
          */
         public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled";
 
         /**
-         * For user perference if One-Handed Mode timeout.
+         * For user preference if One-Handed Mode timeout.
          * @hide
          */
         public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout";
 
         /**
-         * For user tapps app to exit One-Handed Mode.
+         * For user taps app to exit One-Handed Mode.
          * @hide
          */
         public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 2677087..5d34c47 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1938,6 +1938,17 @@
         /**
          * @hide
          */
+        public @NonNull Ranking withAudiblyAlertedInfo(@Nullable Ranking previous) {
+            if (previous != null && previous.mLastAudiblyAlertedMs > 0
+                    && this.mLastAudiblyAlertedMs <= 0) {
+                this.mLastAudiblyAlertedMs = previous.mLastAudiblyAlertedMs;
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
         public void populate(Ranking other) {
             populate(other.mKey,
                     other.mRank,
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 346fe29..6e34666 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,7 +24,6 @@
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
 
 import android.util.ArrayMap;
@@ -213,11 +212,9 @@
                     verityDigest, apk.length(), signatureInfo);
         }
 
-        byte[] digest = pickBestDigestForV4(contentDigests);
-
         return new VerifiedSigner(
                 signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
-                verityRootHash, digest);
+                verityRootHash, contentDigests);
     }
 
     private static X509Certificate[] verifySigner(
@@ -339,8 +336,7 @@
             } catch (CertificateException e) {
                 throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
             }
-            certificate = new VerbatimX509Certificate(
-                    certificate, encodedCert);
+            certificate = new VerbatimX509Certificate(certificate, encodedCert);
             certs.add(certificate);
         }
 
@@ -434,12 +430,15 @@
         public final X509Certificate[][] certs;
 
         public final byte[] verityRootHash;
-        public final byte[] digest;
+        // Algorithm -> digest map of signed digests in the signature.
+        // All these are verified if requested.
+        public final Map<Integer, byte[]> contentDigests;
 
-        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
+        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash,
+                Map<Integer, byte[]> contentDigests) {
             this.certs = certs;
             this.verityRootHash = verityRootHash;
-            this.digest = digest;
+            this.contentDigests = contentDigests;
         }
 
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 4ab541b..9357285 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -24,7 +24,6 @@
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
 
 import android.os.Build;
@@ -161,7 +160,7 @@
             boolean doVerifyIntegrity) throws SecurityException, IOException {
         int signerCount = 0;
         Map<Integer, byte[]> contentDigests = new ArrayMap<>();
-        VerifiedSigner result = null;
+        Pair<X509Certificate[], VerifiedProofOfRotation> result = null;
         CertificateFactory certFactory;
         try {
             certFactory = CertificateFactory.getInstance("X.509");
@@ -206,18 +205,17 @@
             ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
         }
 
+        byte[] verityRootHash = null;
         if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
             byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
-            result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+            verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
                     verityDigest, apk.length(), signatureInfo);
         }
 
-        result.digest = pickBestDigestForV4(contentDigests);
-
-        return result;
+        return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests);
     }
 
-    private static VerifiedSigner verifySigner(
+    private static Pair<X509Certificate[], VerifiedProofOfRotation> verifySigner(
             ByteBuffer signerBlock,
             Map<Integer, byte[]> contentDigests,
             CertificateFactory certFactory)
@@ -349,8 +347,7 @@
             } catch (CertificateException e) {
                 throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
             }
-            certificate = new VerbatimX509Certificate(
-                    certificate, encodedCert);
+            certificate = new VerbatimX509Certificate(certificate, encodedCert);
             certs.add(certificate);
         }
 
@@ -382,8 +379,9 @@
 
     private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;
 
-    private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs,
-            List<X509Certificate> certs, CertificateFactory certFactory) throws IOException {
+    private static Pair<X509Certificate[], VerifiedProofOfRotation> verifyAdditionalAttributes(
+            ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory)
+            throws IOException {
         X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]);
         VerifiedProofOfRotation por = null;
 
@@ -421,7 +419,7 @@
                     break;
             }
         }
-        return new VerifiedSigner(certChain, por);
+        return Pair.create(certChain, por);
     }
 
     private static VerifiedProofOfRotation verifyProofOfRotationStruct(
@@ -570,12 +568,17 @@
         public final X509Certificate[] certs;
         public final VerifiedProofOfRotation por;
 
-        public byte[] verityRootHash;
-        public byte[] digest;
+        public final byte[] verityRootHash;
+        // Algorithm -> digest map of signed digests in the signature.
+        // All these are verified if requested.
+        public final Map<Integer, byte[]> contentDigests;
 
-        public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
+        public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por,
+                byte[] verityRootHash, Map<Integer, byte[]> contentDigests) {
             this.certs = certs;
             this.por = por;
+            this.verityRootHash = verityRootHash;
+            this.contentDigests = contentDigests;
         }
 
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index d40efce..844816c 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -16,12 +16,14 @@
 
 package android.util.apk;
 
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
 
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.V4Signature;
+import android.util.ArrayMap;
 import android.util.Pair;
 
 import java.io.ByteArrayInputStream;
@@ -42,6 +44,7 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
 import java.util.Arrays;
+import java.util.Map;
 
 /**
  * APK Signature Scheme v4 verifier.
@@ -79,13 +82,20 @@
             throw new SignatureNotFoundException("Failed to read V4 signature.", e);
         }
 
-        final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo,
+        // Verify signed data and extract certificates and apk digest.
+        final byte[] signedData = V4Signature.getSignedData(apk.length(), hashingInfo,
                 signingInfo);
+        final Pair<Certificate, byte[]> result = verifySigner(signingInfo, signedData);
 
-        return verifySigner(signingInfo, signedData);
+        // Populate digests enforced by IncFS driver.
+        Map<Integer, byte[]> contentDigests = new ArrayMap<>();
+        contentDigests.put(convertToContentDigestType(hashingInfo.hashAlgorithm),
+                hashingInfo.rawRootHash);
+
+        return new VerifiedSigner(new Certificate[]{result.first}, result.second, contentDigests);
     }
 
-    private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo,
+    private static Pair<Certificate, byte[]> verifySigner(V4Signature.SigningInfo signingInfo,
             final byte[] signedData) throws SecurityException {
         if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) {
             throw new SecurityException("No supported signatures found");
@@ -145,21 +155,34 @@
                     "Public key mismatch between certificate and signature record");
         }
 
-        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
+        return Pair.create(certificate, signingInfo.apkDigest);
+    }
+
+    private static int convertToContentDigestType(int hashAlgorithm) throws SecurityException {
+        if (hashAlgorithm == V4Signature.HASHING_ALGORITHM_SHA256) {
+            return CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
+        }
+        throw new SecurityException("Unsupported hashAlgorithm: " + hashAlgorithm);
     }
 
     /**
-     * Verified APK Signature Scheme v4 signer, including V3 digest.
+     * Verified APK Signature Scheme v4 signer, including V2/V3 digest.
      *
      * @hide for internal use only.
      */
     public static class VerifiedSigner {
         public final Certificate[] certs;
-        public byte[] apkDigest;
+        public final byte[] apkDigest;
 
-        public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
+        // Algorithm -> digest map of signed digests in the signature.
+        // These are continuously enforced by the IncFS driver.
+        public final Map<Integer, byte[]> contentDigests;
+
+        public VerifiedSigner(Certificate[] certs, byte[] apkDigest,
+                Map<Integer, byte[]> contentDigests) {
             this.certs = certs;
             this.apkDigest = apkDigest;
+            this.contentDigests = contentDigests;
         }
 
     }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index ab8f80d3..e0258f7 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -45,6 +45,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.ZipEntry;
 
@@ -184,21 +185,21 @@
             Signature[] signerSigs = convertToSignatures(signerCerts);
 
             if (verifyFull) {
-                byte[] nonstreamingDigest = null;
-                Certificate[][] nonstreamingCerts = null;
+                Map<Integer, byte[]> nonstreamingDigests;
+                Certificate[][] nonstreamingCerts;
 
                 try {
                     // v4 is an add-on and requires v2 or v3 signature to validate against its
                     // certificate and digest
                     ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
                             ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
-                    nonstreamingDigest = v3Signer.digest;
+                    nonstreamingDigests = v3Signer.contentDigests;
                     nonstreamingCerts = new Certificate[][]{v3Signer.certs};
                 } catch (SignatureNotFoundException e) {
                     try {
                         ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
                                 ApkSignatureSchemeV2Verifier.verify(apkPath, false);
-                        nonstreamingDigest = v2Signer.digest;
+                        nonstreamingDigests = v2Signer.contentDigests;
                         nonstreamingCerts = v2Signer.certs;
                     } catch (SignatureNotFoundException ee) {
                         throw new SecurityException(
@@ -220,8 +221,15 @@
                     }
                 }
 
-                if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
-                        vSigner.apkDigest.length)) {
+                boolean found = false;
+                for (byte[] nonstreamingDigest : nonstreamingDigests.values()) {
+                    if (ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+                            vSigner.apkDigest.length)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
                     throw new SecurityException("APK digest in V4 signature does not match V2/V3");
                 }
             }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 6efe95c..990092c 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -577,21 +577,6 @@
     }
 
     /**
-     * Returns the best digest from the map of available digests.
-     * similarly to compareContentDigestAlgorithm.
-     *
-     * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
-     */
-    static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
-        for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
-            if (contentDigests.containsKey(algo)) {
-                return contentDigests.get(algo);
-            }
-        }
-        return null;
-    }
-
-    /**
      * Returns new byte buffer whose content is a shared subsequence of this buffer's content
      * between the specified start (inclusive) and end (exclusive) positions. As opposed to
      * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index ff3ac073..9eb63087 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -174,12 +174,9 @@
                 int touchSlop = config.getScaledTouchSlop();
                 mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
                 if (mMovedEnoughForDrag) {
-                    // If the direction of the swipe motion is within 30 degrees of vertical, it is
-                    // considered a vertical drag. We don't actually have to compute the angle to
-                    // implement the check though. When the angle is exactly 30 degrees from
-                    // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
-                    // vertical, 2*deltaX < distance.
-                    mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
+                    // If the direction of the swipe motion is within 45 degrees of vertical, it is
+                    // considered a vertical drag.
+                    mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
                 }
             }
         } else if (action == MotionEvent.ACTION_CANCEL) {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 843700c..32c68bd 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -108,7 +108,7 @@
      *
      * @return the swap result, index 0 is the start index and index 1 is the end index.
      */
-    private static int[] sortSelctionIndices(int selectionStart, int selectionEnd) {
+    private static int[] sortSelectionIndices(int selectionStart, int selectionEnd) {
         if (selectionStart < selectionEnd) {
             return new int[]{selectionStart, selectionEnd};
         }
@@ -122,11 +122,11 @@
      * @param textView the selected TextView.
      * @return the swap result, index 0 is the start index and index 1 is the end index.
      */
-    private static int[] sortSelctionIndicesFromTextView(TextView textView) {
+    private static int[] sortSelectionIndicesFromTextView(TextView textView) {
         int selectionStart = textView.getSelectionStart();
         int selectionEnd = textView.getSelectionEnd();
 
-        return sortSelctionIndices(selectionStart, selectionEnd);
+        return sortSelectionIndices(selectionStart, selectionEnd);
     }
 
     /**
@@ -135,7 +135,7 @@
     public void startSelectionActionModeAsync(boolean adjustSelection) {
         // Check if the smart selection should run for editable text.
         adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
 
         mSelectionTracker.onOriginalSelection(
                 getText(mTextView),
@@ -165,7 +165,7 @@
      * Starts Link ActionMode.
      */
     public void startLinkActionModeAsync(int start, int end) {
-        int[] indexResult = sortSelctionIndices(start, end);
+        int[] indexResult = sortSelectionIndices(start, end);
         mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1],
                 true /*isLink*/);
         cancelAsyncTask();
@@ -201,21 +201,21 @@
 
     /** Reports a selection action event. */
     public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         mSelectionTracker.onSelectionAction(
                 sortedSelectionIndices[0], sortedSelectionIndices[1],
                 getActionType(menuItemId), actionLabel, mTextClassification);
     }
 
     public void onSelectionDrag() {
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         mSelectionTracker.onSelectionAction(
                 sortedSelectionIndices[0], sortedSelectionIndices[1],
                 SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
     }
 
     public void onTextChanged(int start, int end) {
-        int[] sortedSelectionIndices = sortSelctionIndices(start, end);
+        int[] sortedSelectionIndices = sortSelectionIndices(start, end);
         mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1],
                 mTextClassification);
     }
@@ -334,7 +334,7 @@
             startSelectionActionMode(startSelectionResult);
         };
         // TODO do not trigger the animation if the change included only non-printable characters
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         final boolean didSelectionChange =
                 result != null && (sortedSelectionIndices[0] != result.mStart
                         || sortedSelectionIndices[1] != result.mEnd);
@@ -486,7 +486,7 @@
         if (actionMode != null) {
             actionMode.invalidate();
         }
-        final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         mSelectionTracker.onSelectionUpdated(
                 sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification);
         mTextClassificationAsyncTask = null;
@@ -495,7 +495,7 @@
     private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
         if (selectionStart < 0 || selectionEnd < 0) {
             // Use selection indices
-            int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+            int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
             selectionStart = sortedSelectionIndices[0];
             selectionEnd = sortedSelectionIndices[1];
         }
@@ -637,7 +637,7 @@
                 mAllowReset = false;
                 boolean selected = editor.selectCurrentWord();
                 if (selected) {
-                    final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView);
+                    final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(textView);
                     mSelectionStart = sortedSelectionIndices[0];
                     mSelectionEnd = sortedSelectionIndices[1];
                     mLogger.logSelectionAction(
@@ -1215,7 +1215,7 @@
 
         SelectionResult(int start, int end,
                 @Nullable TextClassification classification, @Nullable TextSelection selection) {
-            int[] sortedIndices = sortSelctionIndices(start, end);
+            int[] sortedIndices = sortSelectionIndices(start, end);
             mStart = sortedIndices[0];
             mEnd = sortedIndices[1];
             mClassification = classification;
diff --git a/core/java/android/widget/TextViewRichContentReceiver.java b/core/java/android/widget/TextViewRichContentReceiver.java
index 5678a1e..4f2d954 100644
--- a/core/java/android/widget/TextViewRichContentReceiver.java
+++ b/core/java/android/widget/TextViewRichContentReceiver.java
@@ -116,9 +116,6 @@
     private static boolean onReceiveForAutofill(@NonNull TextView textView, @NonNull ClipData clip,
             @Flags int flags) {
         final CharSequence text = coerceToText(clip, textView.getContext(), flags);
-        if (text.length() == 0) {
-            return false;
-        }
         // First autofill it...
         textView.setText(text);
         // ...then move cursor to the end.
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 71edfd5..350f358 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -61,8 +61,8 @@
 
     mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
 
-    mInfo.dispatchingTimeout = env->GetLongField(obj,
-            gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
+    mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+            env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos));
 
     jobject tokenObj = env->GetObjectField(obj,
             gInputApplicationHandleClassInfo.token);
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 81569e0..792c005 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -117,8 +117,8 @@
             gInputWindowHandleClassInfo.layoutParamsFlags);
     mInfo.layoutParamsType = env->GetIntField(obj,
             gInputWindowHandleClassInfo.layoutParamsType);
-    mInfo.dispatchingTimeout = env->GetLongField(obj,
-            gInputWindowHandleClassInfo.dispatchingTimeoutNanos);
+    mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+            env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutNanos));
     mInfo.frameLeft = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameLeft);
     mInfo.frameTop = env->GetIntField(obj,
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a88f891..ff336ee 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -122,10 +122,18 @@
             std::stringstream ss;
             ss << "HwBinder Error: (" << err << ")";
 
-            jniThrowException(
-                    env,
-                    canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
-                    ss.str().c_str());
+            const char* exception = nullptr;
+            if (canThrowRemoteException) {
+                if (err == DEAD_OBJECT) {
+                    exception = "android/os/DeadObjectException";
+                } else {
+                    exception = "android/os/RemoteException";
+                }
+            } else {
+                exception = "java/lang/RuntimeException";
+            }
+
+            jniThrowException(env, exception, ss.str().c_str());
 
             break;
         }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 50b5ead..097af76 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -1722,7 +1722,7 @@
     // OPEN: Settings > System > Language & Region
     SETTINGS_LANGUAGE_CATEGORY = 750;
 
-    // OPEN: Settings > System > Input & Gesture > Swipe to notification gesture
+    // OPEN: Settings > System > Input & Gesture > Swipe fingerprint for notifications
     SETTINGS_GESTURE_SWIPE_TO_NOTIFICATION = 751;
 
     // OPEN: Settings > System > Input & Gesture > Double tap power button gesture
@@ -2688,4 +2688,9 @@
     // CATEGORY: SETTINGS
     // OS: R
     MEDIA_CONTROLS_SETTINGS = 1845;
+
+    // OPEN: Settings > System > Gestures > Swipe for notification
+    // CATEGORY: SETTINGS
+    // OS: R QPR
+    SETTINGS_SWIPE_BOTTOM_TO_NOTIFICATION = 1846;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 7627961..edafb0c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -381,6 +381,15 @@
     }
     optional Notification notification = 41;
 
+    message OneHanded {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional OneHanded onehanded = 80;
+
     message PackageVerifier {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -502,6 +511,7 @@
     // parent profile.
     optional SettingProto sync_parent_sounds = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto system_navigation_keys_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto theme_customization_overlay_packages = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto trust_agents_initialized = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
@@ -594,16 +604,7 @@
     }
     optional Zen zen = 71;
 
-    message OneHanded {
-        option (android.msg_privacy).dest = DEST_EXPLICIT;
-
-        optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    }
-    optional OneHanded onehanded = 80;
-
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 82;
+    // Next tag = 83;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 16797fd..0985fea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -263,6 +263,7 @@
     <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
 
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 00505ca..f5d7246 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1172,7 +1172,7 @@
     <string name="Midnight" msgid="8176019203622191377">"منتصف الليل"</string>
     <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
-    <string name="selectAll" msgid="1532369154488982046">"اختيار الكل"</string>
+    <string name="selectAll" msgid="1532369154488982046">"تحديد الكل"</string>
     <string name="cut" msgid="2561199725874745819">"قص"</string>
     <string name="copy" msgid="5472512047143665218">"نسخ"</string>
     <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"تعذّر النسخ في الحافظة"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 99665c4..4f3d890 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1548,7 +1548,7 @@
     <string name="launchBrowserDefault" msgid="6328349989932924119">"ब्राउज़र लॉन्च करें?"</string>
     <string name="SetupCallDefault" msgid="5581740063237175247">"कॉल स्वीकार करें?"</string>
     <string name="activity_resolver_use_always" msgid="5575222334666843269">"हमेशा"</string>
-    <string name="activity_resolver_use_once" msgid="948462794469672658">"केवल एक बार"</string>
+    <string name="activity_resolver_use_once" msgid="948462794469672658">"सिर्फ़ एक बार"</string>
     <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s वर्क प्रोफ़ाइल का समर्थन नहीं करता"</string>
     <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"टैबलेट"</string>
     <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टीवी"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index df8e33d..e250b50 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1111,7 +1111,7 @@
     <string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
-    <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
+    <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
     <string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> иштөөдө"</string>
     <string name="app_running_notification_text" msgid="5120815883400228566">"Көбүрөөк маалымат үчүн же колдонмону токтотуш үчүн таптап коюңуз."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index a24db66..b4982eb 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1547,7 +1547,7 @@
     <string name="sending" msgid="206925243621664438">"അയയ്‌ക്കുന്നു…"</string>
     <string name="launchBrowserDefault" msgid="6328349989932924119">"ബ്രൗസർ സമാരംഭിക്കണോ?"</string>
     <string name="SetupCallDefault" msgid="5581740063237175247">"കോൾ സ്വീകരിക്കണോ?"</string>
-    <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string>
+    <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്‌പ്പോഴും"</string>
     <string name="activity_resolver_use_once" msgid="948462794469672658">"ഒരിക്കൽ മാത്രം"</string>
     <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s, ഔദ്യോഗിക പ്രൊഫൈലിനെ പിന്തുണയ്‌ക്കുന്നില്ല"</string>
     <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"ടാബ്‌ലെറ്റ്"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 8eb8cef..be21f7b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1093,7 +1093,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="1532369154488982046">"Hammasini belgilash"</string>
-    <string name="cut" msgid="2561199725874745819">"Kesish"</string>
+    <string name="cut" msgid="2561199725874745819">"Kesib olish"</string>
     <string name="copy" msgid="5472512047143665218">"Nusxa olish"</string>
     <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Vaqtinchalik xotiraga nusxalab bo‘lmadi"</string>
     <string name="paste" msgid="461843306215520225">"Joylash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 8061ed8..33675da 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -538,7 +538,7 @@
     <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string>
     <string name="permlab_mediaLocation" msgid="7368098373378598066">"đọc vị trí từ bộ sưu tập phương tiện"</string>
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh đó là bạn"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh danh tính của bạn"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string>
     <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string>
     <string name="biometric_not_recognized" msgid="5106687642694635888">"Không nhận dạng được"</string>
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 89cc6e7..df2946c 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -37,7 +37,6 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
 import android.text.Layout;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -96,7 +95,6 @@
         mMotionEvents.clear();
     }
 
-    @Presubmit
     @Test
     public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable {
         String text = "Hello world!";
@@ -145,7 +143,7 @@
         // Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
         // the handle as the touch moves downwards (and because we have some slop to avoid jumping
         // across lines), the cursor position will end up higher than the finger position.
-        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
 
         // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
@@ -181,7 +179,7 @@
         // Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
         // the handle as the touch moves downwards (and because we have some slop to avoid jumping
         // across lines), the cursor position will end up higher than the finger position.
-        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
 
         // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index ec75e40..35fd4bd 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -326,9 +326,9 @@
         mTouchState.update(event1, mConfig);
         assertSingleTap(mTouchState, 0f, 0f, 0, 0);
 
-        // Simulate an ACTION_MOVE event that is > 30 deg from vertical.
+        // Simulate an ACTION_MOVE event that is > 45 deg from vertical.
         long event2Time = 1002;
-        MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f);
+        MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
         mTouchState.update(event2, mConfig);
         assertDrag(mTouchState, 0f, 0f, 0, 0, false);
 
diff --git a/data/keyboards/Vendor_1532_Product_1004.kl b/data/keyboards/Vendor_1532_Product_1004.kl
new file mode 100644
index 0000000..bfbfed5
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1004.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Ultimate Edition Controller with wired USB interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130    BUTTON_X
+# Cross
+key 0x131    BUTTON_A
+# Circle
+key 0x132    BUTTON_B
+# Triangle
+key 0x133    BUTTON_Y
+
+key 0x134    BUTTON_L1
+key 0x135    BUTTON_R1
+key 0x136    BUTTON_L2
+key 0x137    BUTTON_R2
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# L2 axis
+axis 0x09   RTRIGGER
+# R2 axis
+axis 0x0a   LTRIGGER
+
+# Left stick click
+key 0x13a    BUTTON_THUMBL
+# Right stick click
+key 0x13b    BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138    BUTTON_SELECT
+# Options
+key 0x139    BUTTON_START
+# PS key
+key 0x13c     BUTTON_MODE
+
+# Touchpad press
+key 0x13d   BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_1007.kl b/data/keyboards/Vendor_1532_Product_1007.kl
new file mode 100644
index 0000000..6f6c286
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1007.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Tournament Edition Controller with wired USB interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130    BUTTON_X
+# Cross
+key 0x131    BUTTON_A
+# Circle
+key 0x132    BUTTON_B
+# Triangle
+key 0x133    BUTTON_Y
+
+key 0x134    BUTTON_L1
+key 0x135    BUTTON_R1
+key 0x136    BUTTON_L2
+key 0x137    BUTTON_R2
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# L2 axis
+axis 0x09   RTRIGGER
+# R2 axis
+axis 0x0a   LTRIGGER
+
+# Left stick click
+key 0x13a    BUTTON_THUMBL
+# Right stick click
+key 0x13b    BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138    BUTTON_SELECT
+# Options
+key 0x139    BUTTON_START
+# PS key
+key 0x13c     BUTTON_MODE
+
+# Touchpad press
+key 0x13d   BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_1009.kl b/data/keyboards/Vendor_1532_Product_1009.kl
new file mode 100644
index 0000000..c380d5c3
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1009.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Ultimate Edition Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130    BUTTON_X
+# Cross
+key 0x131    BUTTON_A
+# Circle
+key 0x132    BUTTON_B
+# Triangle
+key 0x133    BUTTON_Y
+
+key 0x134    BUTTON_L1
+key 0x135    BUTTON_R1
+key 0x136    BUTTON_L2
+key 0x137    BUTTON_R2
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# L2 axis
+axis 0x09   RTRIGGER
+# R2 axis
+axis 0x0a   LTRIGGER
+
+# Left stick click
+key 0x13a    BUTTON_THUMBL
+# Right stick click
+key 0x13b    BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138    BUTTON_SELECT
+# Options
+key 0x139    BUTTON_START
+# PS key
+key 0x13c     BUTTON_MODE
+
+# Touchpad press
+key 0x13d   BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_100a.kl b/data/keyboards/Vendor_1532_Product_100a.kl
new file mode 100644
index 0000000..b0e966d
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_100a.kl
@@ -0,0 +1,65 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Razer Raiju Tournament Edition Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130    BUTTON_X
+# Cross
+key 0x131    BUTTON_A
+# Circle
+key 0x132    BUTTON_B
+# Triangle
+key 0x133    BUTTON_Y
+
+key 0x134    BUTTON_L1
+key 0x135    BUTTON_R1
+key 0x136    BUTTON_L2
+key 0x137    BUTTON_R2
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# L2 axis
+axis 0x09   RTRIGGER
+# R2 axis
+axis 0x0a   LTRIGGER
+
+# Left stick click
+key 0x13a    BUTTON_THUMBL
+# Right stick click
+key 0x13b    BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138    BUTTON_SELECT
+# Options
+key 0x139    BUTTON_START
+# PS key
+key 0x13c     BUTTON_MODE
+
+# Touchpad press
+key 0x13d   BUTTON_1
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 32c4173..c701353 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -208,8 +208,12 @@
     return config;
 }
 
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
 void EglManager::initExtensions() {
     auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+    auto extensionsAndroid =
+            StringUtils::split(eglQueryStringImplementationANDROID(mEglDisplay, EGL_EXTENSIONS));
 
     // For our purposes we don't care if EGL_BUFFER_AGE is a result of
     // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
@@ -228,9 +232,12 @@
     EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
     EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
     EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
-    EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync");
     EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
     EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync");
+
+    // EGL_ANDROID_native_fence_sync is not exposed to applications, so access
+    // this through the private Android-specific query instead.
+    EglExtensions.nativeFenceSync = extensionsAndroid.has("EGL_ANDROID_native_fence_sync");
 }
 
 bool EglManager::hasEglContext() {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 3b494e9..5e480a6 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,31 +24,10 @@
 
 #include <log/log.h>
 
+#include <memory>
+
 namespace android {
 
-// --- WeakLooperCallback ---
-
-class WeakLooperCallback: public LooperCallback {
-protected:
-    virtual ~WeakLooperCallback() { }
-
-public:
-    explicit WeakLooperCallback(const wp<LooperCallback>& callback) :
-        mCallback(callback) {
-    }
-
-    virtual int handleEvent(int fd, int events, void* data) {
-        sp<LooperCallback> callback = mCallback.promote();
-        if (callback != NULL) {
-            return callback->handleEvent(fd, events, data);
-        }
-        return 0; // the client is gone, remove the callback
-    }
-
-private:
-    wp<LooperCallback> mCallback;
-};
-
 // --- PointerController ---
 
 // Time to wait before starting the fade when the pointer is inactive.
@@ -64,29 +43,50 @@
 // The number of events to be read at once for DisplayEventReceiver.
 static const int EVENT_BUFFER_SIZE = 100;
 
-// --- PointerController ---
+std::shared_ptr<PointerController> PointerController::create(
+        const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController) {
+    std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
+            new PointerController(policy, looper, spriteController));
 
-PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
-        const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
-        mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
-    mHandler = new WeakMessageHandler(this);
-    mCallback = new WeakLooperCallback(this);
+    /*
+     * Now we need to hook up the constructed PointerController object to its callbacks.
+     *
+     * This must be executed after the constructor but before any other methods on PointerController
+     * in order to ensure that the fully constructed object is visible on the Looper thread, since
+     * that may be a different thread than where the PointerController is initially constructed.
+     *
+     * Unfortunately, this cannot be done as part of the constructor since we need to hand out
+     * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr.
+     */
 
-    if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
-        mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
-                       Looper::EVENT_INPUT, mCallback, nullptr);
+    controller->mHandler->pointerController = controller;
+    controller->mCallback->pointerController = controller;
+    if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) {
+        controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
+                                   Looper::EVENT_INPUT, controller->mCallback, nullptr);
     } else {
         ALOGE("Failed to initialize DisplayEventReceiver.");
     }
+    return controller;
+}
 
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+                                     const sp<Looper>& looper,
+                                     const sp<SpriteController>& spriteController)
+      : mPolicy(policy),
+        mLooper(looper),
+        mSpriteController(spriteController),
+        mHandler(new MessageHandler()),
+        mCallback(new LooperCallback()) {
     AutoMutex _l(mLock);
 
     mLocked.animationPending = false;
 
-    mLocked.presentation = PRESENTATION_POINTER;
+    mLocked.presentation = Presentation::POINTER;
     mLocked.presentationChanged = false;
 
-    mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
+    mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
 
     mLocked.pointerFadeDirection = 0;
     mLocked.pointerX = 0;
@@ -221,7 +221,7 @@
     removeInactivityTimeoutLocked();
 
     // Start fading.
-    if (transition == TRANSITION_IMMEDIATE) {
+    if (transition == Transition::IMMEDIATE) {
         mLocked.pointerFadeDirection = 0;
         mLocked.pointerAlpha = 0.0f;
         updatePointerLocked();
@@ -238,7 +238,7 @@
     resetInactivityTimeoutLocked();
 
     // Start unfading.
-    if (transition == TRANSITION_IMMEDIATE) {
+    if (transition == Transition::IMMEDIATE) {
         mLocked.pointerFadeDirection = 0;
         mLocked.pointerAlpha = 1.0f;
         updatePointerLocked();
@@ -262,7 +262,7 @@
         return;
     }
 
-    if (presentation == PRESENTATION_POINTER) {
+    if (presentation == Presentation::POINTER) {
         if (mLocked.additionalMouseResources.empty()) {
             mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
                                                   &mLocked.animationResources,
@@ -480,24 +480,35 @@
     updatePointerLocked();
 }
 
-void PointerController::handleMessage(const Message& message) {
+void PointerController::MessageHandler::handleMessage(const Message& message) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+
+    if (controller == nullptr) {
+        ALOGE("PointerController instance was released before processing message: what=%d",
+              message.what);
+        return;
+    }
     switch (message.what) {
     case MSG_INACTIVITY_TIMEOUT:
-        doInactivityTimeout();
+        controller->doInactivityTimeout();
         break;
     }
 }
 
-int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
+int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+    if (controller == nullptr) {
+        ALOGW("PointerController instance was released with pending callbacks.  events=0x%x",
+              events);
+        return 0; // Remove the callback, the PointerController is gone anyways
+    }
     if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
-        ALOGE("Display event receiver pipe was closed or an error occurred.  "
-              "events=0x%x", events);
+        ALOGE("Display event receiver pipe was closed or an error occurred.  events=0x%x", events);
         return 0; // remove the callback
     }
 
     if (!(events & Looper::EVENT_INPUT)) {
-        ALOGW("Received spurious callback for unhandled poll event.  "
-              "events=0x%x", events);
+        ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
         return 1; // keep the callback
     }
 
@@ -505,7 +516,7 @@
     ssize_t n;
     nsecs_t timestamp;
     DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+    while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
         for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
             if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                 timestamp = buf[i].header.timestamp;
@@ -514,7 +525,7 @@
         }
     }
     if (gotVsync) {
-        doAnimate(timestamp);
+        controller->doAnimate(timestamp);
     }
     return 1;  // keep the callback
 }
@@ -613,7 +624,7 @@
 }
 
 void PointerController::doInactivityTimeout() {
-    fade(TRANSITION_GRADUAL);
+    fade(Transition::GRADUAL);
 }
 
 void PointerController::startAnimationLocked() {
@@ -627,8 +638,9 @@
 void PointerController::resetInactivityTimeoutLocked() {
     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
 
-    nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
-            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+    nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
+            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
+            : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
     mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
 }
 
@@ -655,7 +667,7 @@
     }
 
     if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
-        if (mLocked.presentation == PRESENTATION_POINTER) {
+        if (mLocked.presentation == Presentation::POINTER) {
             if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
                 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
             } else {
@@ -731,7 +743,7 @@
             return spot;
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 void PointerController::releaseSpotLocked(Spot* spot) {
@@ -772,7 +784,7 @@
 
     mLocked.additionalMouseResources.clear();
     mLocked.animationResources.clear();
-    if (mLocked.presentation == PRESENTATION_POINTER) {
+    if (mLocked.presentation == Presentation::POINTER) {
         mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
                 &mLocked.animationResources, mLocked.viewport.displayId);
     }
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index ebc622b..14c0679 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -17,19 +17,20 @@
 #ifndef _UI_POINTER_CONTROLLER_H
 #define _UI_POINTER_CONTROLLER_H
 
-#include "SpriteController.h"
-
-#include <map>
-#include <vector>
-
-#include <ui/DisplayInfo.h>
+#include <PointerControllerInterface.h>
+#include <gui/DisplayEventReceiver.h>
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
-#include <PointerControllerInterface.h>
+#include <ui/DisplayInfo.h>
 #include <utils/BitSet.h>
-#include <utils/RefBase.h>
 #include <utils/Looper.h>
-#include <gui/DisplayEventReceiver.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "SpriteController.h"
 
 namespace android {
 
@@ -70,25 +71,22 @@
     virtual int32_t getCustomPointerIconId() = 0;
 };
 
-
 /*
  * Tracks pointer movements and draws the pointer sprite to a surface.
  *
  * Handles pointer acceleration and animation.
  */
-class PointerController : public PointerControllerInterface, public MessageHandler,
-                          public LooperCallback {
-protected:
-    virtual ~PointerController();
-
+class PointerController : public PointerControllerInterface {
 public:
-    enum InactivityTimeout {
-        INACTIVITY_TIMEOUT_NORMAL = 0,
-        INACTIVITY_TIMEOUT_SHORT = 1,
+    static std::shared_ptr<PointerController> create(
+            const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+            const sp<SpriteController>& spriteController);
+    enum class InactivityTimeout {
+        NORMAL = 0,
+        SHORT = 1,
     };
 
-    PointerController(const sp<PointerControllerPolicyInterface>& policy,
-            const sp<Looper>& looper, const sp<SpriteController>& spriteController);
+    virtual ~PointerController();
 
     virtual bool getBounds(float* outMinX, float* outMinY,
             float* outMaxX, float* outMaxY) const;
@@ -113,8 +111,8 @@
     void reloadPointerResources();
 
 private:
-    static const size_t MAX_RECYCLED_SPRITES = 12;
-    static const size_t MAX_SPOTS = 12;
+    static constexpr size_t MAX_RECYCLED_SPRITES = 12;
+    static constexpr size_t MAX_SPOTS = 12;
 
     enum {
         MSG_INACTIVITY_TIMEOUT,
@@ -130,8 +128,13 @@
         float x, y;
 
         inline Spot(uint32_t id, const sp<Sprite>& sprite)
-                : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
-                  x(0.0f), y(0.0f), lastIcon(NULL) { }
+              : id(id),
+                sprite(sprite),
+                alpha(1.0f),
+                scale(1.0f),
+                x(0.0f),
+                y(0.0f),
+                lastIcon(nullptr) {}
 
         void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
 
@@ -139,12 +142,24 @@
         const SpriteIcon* lastIcon;
     };
 
+    class MessageHandler : public virtual android::MessageHandler {
+    public:
+        void handleMessage(const Message& message) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
+    class LooperCallback : public virtual android::LooperCallback {
+    public:
+        int handleEvent(int fd, int events, void* data) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
     mutable Mutex mLock;
 
     sp<PointerControllerPolicyInterface> mPolicy;
     sp<Looper> mLooper;
     sp<SpriteController> mSpriteController;
-    sp<WeakMessageHandler> mHandler;
+    sp<MessageHandler> mHandler;
     sp<LooperCallback> mCallback;
 
     DisplayEventReceiver mDisplayEventReceiver;
@@ -181,14 +196,15 @@
         int32_t buttonState;
 
         std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
-        std::vector<sp<Sprite> > recycledSprites;
+        std::vector<sp<Sprite>> recycledSprites;
     } mLocked GUARDED_BY(mLock);
 
+    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+                      const sp<SpriteController>& spriteController);
+
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
 
-    void handleMessage(const Message& message);
-    int handleEvent(int fd, int events, void* data);
     void doAnimate(nsecs_t timestamp);
     bool doFadingAnimationLocked(nsecs_t timestamp);
     bool doBitmapAnimationLocked(nsecs_t timestamp);
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index a157426..6e129a0 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -136,7 +136,7 @@
     sp<MockSprite> mPointerSprite;
     sp<MockPointerControllerPolicyInterface> mPolicy;
     sp<MockSpriteController> mSpriteController;
-    sp<PointerController> mPointerController;
+    std::shared_ptr<PointerController> mPointerController;
 
 private:
     void loopThread();
@@ -160,7 +160,7 @@
     EXPECT_CALL(*mSpriteController, createSprite())
             .WillOnce(Return(mPointerSprite));
 
-    mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+    mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
 }
 
 PointerControllerTest::~PointerControllerTest() {
@@ -193,7 +193,7 @@
 
 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
     ensureDisplayViewportIsSet();
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
     EXPECT_CALL(*mPointerSprite, setVisible(true));
@@ -208,7 +208,7 @@
 
 TEST_F(PointerControllerTest, updatePointerIcon) {
     ensureDisplayViewportIsSet();
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     int32_t type = CURSOR_TYPE_ADDITIONAL;
     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
@@ -224,7 +224,7 @@
 
 TEST_F(PointerControllerTest, setCustomPointerIcon) {
     ensureDisplayViewportIsSet();
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     int32_t style = CURSOR_TYPE_CUSTOM;
     float hotSpotX = 15;
@@ -246,13 +246,13 @@
 }
 
 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
-    mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+    mPointerController->setPresentation(PointerController::Presentation::POINTER);
     mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
     mPointerController->clearSpots();
     mPointerController->setPosition(1.0f, 1.0f);
     mPointerController->move(1.0f, 1.0f);
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
-    mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
+    mPointerController->fade(PointerController::Transition::IMMEDIATE);
 
     EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
 
diff --git a/media/java/android/media/MediaFrameworkInitializer.java b/media/java/android/media/MediaFrameworkInitializer.java
new file mode 100644
index 0000000..577442e
--- /dev/null
+++ b/media/java/android/media/MediaFrameworkInitializer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.media.session.MediaSessionManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Class for performing registration for all media services
+ *
+ * TODO (b/160513103): Move this class when moving media service code to APEX
+ * @hide
+ */
+public class MediaFrameworkInitializer {
+    private MediaFrameworkInitializer() {
+    }
+
+    private static volatile MediaServiceManager sMediaServiceManager;
+
+    /**
+     * Sets an instance of {@link MediaServiceManager} that allows
+     * the media mainline module to register/obtain media binder services. This is called
+     * by the platform during the system initialization.
+     *
+     * @param mediaServiceManager instance of {@link MediaServiceManager} that allows
+     * the media mainline module to register/obtain media binder services.
+     */
+    public static void setMediaServiceManager(
+            @NonNull MediaServiceManager mediaServiceManager) {
+        Preconditions.checkState(sMediaServiceManager == null,
+                "setMediaServiceManager called twice!");
+        sMediaServiceManager = Objects.requireNonNull(mediaServiceManager);
+    }
+
+    /** @hide */
+    public static MediaServiceManager getMediaServiceManager() {
+        return sMediaServiceManager;
+    }
+
+    /**
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers all media
+     * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+     *
+     * @throws IllegalStateException if this is called from anywhere besides
+     * {@link SystemServiceRegistry}
+     */
+    public static void registerServiceWrappers() {
+        SystemServiceRegistry.registerContextAwareService(
+                Context.MEDIA_SESSION_SERVICE,
+                MediaSessionManager.class,
+                context -> new MediaSessionManager(context)
+        );
+    }
+}
diff --git a/media/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java
new file mode 100644
index 0000000..21e2d84
--- /dev/null
+++ b/media/java/android/media/MediaServiceManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the media
+ * service.
+ *
+ * <p> Only the media mainline module will be able to access an instance of this class.
+ * @hide
+ */
+public class MediaServiceManager {
+    /**
+     * @hide
+     */
+    public MediaServiceManager() {}
+
+    /**
+     * A class that exposes the methods to register and obtain each system service.
+     */
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Get the system server binding object for MediaServiceManager.
+         *
+         * <p> This blocks until the service instance is ready.
+         * or a timeout happens, in which case it returns null.
+         */
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "media_session" service.
+     */
+    @NonNull
+    public ServiceRegisterer getMediaSessionServiceRegisterer() {
+        return new ServiceRegisterer("media_session");
+    }
+}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 2fd721e..6976a35 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -28,14 +28,13 @@
 import android.content.pm.ParceledListSlice;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
+import android.media.MediaFrameworkInitializer;
 import android.media.MediaSession2;
 import android.media.Session2Token;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.service.media.MediaBrowserService;
 import android.service.notification.NotificationListenerService;
@@ -115,8 +114,10 @@
         // Consider rewriting like DisplayManagerGlobal
         // Decide if we need context
         mContext = context;
-        IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE);
-        mService = ISessionManager.Stub.asInterface(b);
+        mService = ISessionManager.Stub.asInterface(MediaFrameworkInitializer
+                .getMediaServiceManager()
+                .getMediaSessionServiceRegisterer()
+                .get());
     }
 
     /**
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
new file mode 100644
index 0000000..ee06720
--- /dev/null
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediatranscodingtest;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.MediaFormat;
+import android.media.MediaTranscodeManager;
+import android.media.MediaTranscodeManager.TranscodingJob;
+import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/*
+ * Benchmarking for MediaTranscodeManager in the media framework.
+ *
+ * Note: This benchmarking requires to push all the files from http://go/transcodingbenchmark
+ * to /data/user/0/com.android.mediatranscodingtest/cache/ directory after installing the apk.
+ *
+ * TODO(hkuang): Change it to download from server automatically instead of manually.
+ *
+ * To run this test suite:
+     make frameworks/base/media/tests/MediaTranscodingTest
+     make mediatranscodingtest
+
+     adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+      // Push the files to /data/user/0/com.android.mediatranscodingtest/cache/
+     adb push $DOWNLOADPATH/*.mp4 /data/user/0/com.android.mediatranscodingtest/cache/
+
+     adb shell am instrument -e class \
+     com.android.mediatranscodingtest.MediaTranscodingBenchmark \
+     -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+ *
+ */
+public class MediaTranscodingBenchmark
+        extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
+    private static final String TAG = "MediaTranscodingBenchmark";
+    // TODO(hkuang): Change this to query from MediaCodecInfo.CodecCapabilities for different
+    // resolution.
+    private static final int MINIMUM_TRANSCODING_FPS = 80;
+    private static final int LOOP_COUNT = 10;
+    // Default Setting for transcoding to H.264.
+    private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
+    private static final int BIT_RATE = 20000000;            // 20Mbps
+    private static final int WIDTH = 1920;
+    private static final int HEIGHT = 1080;
+    private Context mContext;
+    private MediaTranscodeManager mMediaTranscodeManager = null;
+
+    public MediaTranscodingBenchmark() {
+        super("com.android.MediaTranscodingBenchmark", MediaTranscodingTest.class);
+    }
+
+    /**
+     * Creates a MediaFormat with the basic set of values.
+     */
+    private static MediaFormat createMediaFormat() {
+        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+        return format;
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        Log.d(TAG, "setUp");
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+        mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /*
+     * Transcode the sourceFileName to destinationFileName with LOOP_COUNT.
+     */
+    private void transcode(final String sourceFileName, final String destinationFileName)
+            throws IOException, InterruptedException {
+        AtomicLong totalTimeMs = new AtomicLong();
+        AtomicLong transcodingTime = new AtomicLong();
+        Uri srcUri = getUri(sourceFileName);
+        Uri dstUri = getUri(destinationFileName);
+
+        MediaTranscodingTestUtil.VideoFileInfo info =
+                MediaTranscodingTestUtil.extractVideoFileInfo(mContext, getUri(sourceFileName));
+        int timeoutSeconds = calMaxTranscodingWaitTimeSeconds(info.mNumVideoFrames,
+                MINIMUM_TRANSCODING_FPS);
+        Log.d(TAG, "Start Transcoding " + info.toString() + " " + timeoutSeconds);
+
+        for (int loop = 0; loop < LOOP_COUNT; ++loop) {
+            Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+            TranscodingRequest request =
+                    new TranscodingRequest.Builder()
+                            .setSourceUri(srcUri)
+                            .setDestinationUri(dstUri)
+                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                            .setVideoTrackFormat(createMediaFormat())
+                            .build();
+            Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+            long startTimeMs = System.currentTimeMillis();
+            TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+                    transcodingJob -> {
+                        Log.d(TAG,
+                                "Transcoding completed with result: " + transcodingJob.getResult());
+                        assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+                        transcodeCompleteSemaphore.release();
+                        transcodingTime.set(System.currentTimeMillis() - startTimeMs);
+                        totalTimeMs.addAndGet(transcodingTime.get());
+                    });
+
+            if (job != null) {
+                Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+                boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+                        timeoutSeconds, TimeUnit.SECONDS);
+                assertTrue("Transcode failed to complete in time.", finishedOnTime);
+            }
+            Log.i(TAG, "Loop: " + loop + " take " + transcodingTime.get() + " ms ");
+        }
+
+        float fps = info.mNumVideoFrames * 1000 * LOOP_COUNT / totalTimeMs.get();
+        Log.i(TAG, "Transcoding " + info.toString() + " Transcoding fps: " + fps);
+    }
+
+    // Calculate the maximum wait time based on minimum transcoding throughput and frame number.
+    private int calMaxTranscodingWaitTimeSeconds(int numberFrames, int minTranscodingFps) {
+        int waitTimeSeconds =  numberFrames / minTranscodingFps;
+        // If waitTimeSeconds is 0, wait for 1 seconds at least.
+        return waitTimeSeconds == 0 ? 1 : waitTimeSeconds;
+    }
+
+    private Uri getUri(final String fileName) {
+        String path = mContext.getCacheDir().getAbsolutePath();
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_FILE).appendPath(path).appendPath(
+                fileName).build();
+    }
+
+    @Test
+    public void testBenchmarkingAVCToAVCWith66FramesWithoutAudio() throws Exception {
+        String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps";
+        String testVideoName = videoNameWithoutExtension + ".mp4";
+        String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+        transcode(testVideoName, transcodedVideoName);
+    }
+
+    @Test
+    public void testBenchmarkingAVCToAVCWith66FramesWithAudio() throws Exception {
+        String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps_aac";
+        String testVideoName = videoNameWithoutExtension + ".mp4";
+        String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+        transcode(testVideoName, transcodedVideoName);
+    }
+}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
index 3b044c7..53b2392 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
@@ -38,6 +38,7 @@
         TestSuite suite = new InstrumentationTestSuite(this);
         suite.addTestSuite(MediaTranscodeManagerTest.class);
         suite.addTestSuite(MediaTranscodeManagerWithMockServiceTest.class);
+        suite.addTestSuite(MediaTranscodingBenchmark.class);
         return suite;
     }
 
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
index a1c3251..69f124f 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
@@ -27,6 +27,7 @@
 import android.media.MediaCodecInfo;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
 import android.net.Uri;
 import android.util.Log;
 import android.util.Size;
@@ -42,6 +43,75 @@
 /* package */ class MediaTranscodingTestUtil {
     private static final String TAG = "MediaTranscodingTestUtil";
 
+    // Helper class to extract the information from source file and transcoded file.
+    static class VideoFileInfo {
+        String mUri;
+        int mNumVideoFrames = 0;
+        int mWidth = 0;
+        int mHeight = 0;
+        float mVideoFrameRate = 0.0f;
+        boolean mHasAudio = false;
+
+        public String toString() {
+            String str = mUri;
+            str += " Width:" + mWidth;
+            str += " Height:" + mHeight;
+            str += " FrameRate:" + mWidth;
+            str += " FrameCount:" + mNumVideoFrames;
+            str +=  " HasAudio:" + (mHasAudio ? "Yes" : "No");
+            return str;
+        }
+    }
+
+    static VideoFileInfo extractVideoFileInfo(Context ctx, Uri videoUri) throws IOException {
+        VideoFileInfo info = new VideoFileInfo();
+        AssetFileDescriptor afd =  null;
+        MediaMetadataRetriever retriever = null;
+
+        try {
+            afd = ctx.getContentResolver().openAssetFileDescriptor(videoUri, "r");
+            retriever = new MediaMetadataRetriever();
+            retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+
+            info.mUri = videoUri.getLastPathSegment();
+            Log.i(TAG, "Trying to transcode to " + info.mUri);
+            String width = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+            String height = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+            if (width != null && height != null) {
+                info.mWidth = Integer.parseInt(width);
+                info.mHeight = Integer.parseInt(height);
+            }
+
+            String frameRate = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE);
+            if (frameRate != null) {
+                info.mVideoFrameRate = Float.parseFloat(frameRate);
+            }
+
+            String frameCount = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT);
+            if (frameCount != null) {
+                info.mNumVideoFrames = Integer.parseInt(frameCount);
+            }
+
+            String hasAudio = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
+            if (hasAudio != null) {
+                info.mHasAudio = hasAudio.equals("yes");
+            }
+        } finally {
+            if (retriever != null) {
+                retriever.close();
+            }
+            if (afd != null) {
+                afd.close();
+            }
+        }
+        return info;
+    }
+
     static VideoTranscodingStatistics computeStats(final Context ctx, final Uri sourceMp4,
             final Uri transcodedMp4)
             throws Exception {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index e3aceec..b693b0e 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -3614,8 +3614,13 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+    field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
     field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
     field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+    field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+    field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+    field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+    field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
     field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
     field public static final long FUNCTION_ADB = 1L; // 0x1L
     field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
index 1782d25..5aab0a1 100644
--- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -29,13 +29,12 @@
         android:orientation="horizontal"
         app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
 
-    <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the
-         focus here when the user navigates to another window. This is also used to prevent
-         wrap-around which is why it must be first or last in Tab order. -->
+    <!-- Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+         when the user navigates to another window. This is also used to prevent wrap-around. -->
     <com.android.car.ui.FocusParkingView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"/>
 
     <View
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index d60bc41..adf8d4d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -148,10 +148,9 @@
         CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
                 /* root= */ null);
 
-        // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
-        // the user navigates to another window. This is also used to prevent wrap-around which is
-        // why it must be first or last in Tab order.
-        view.addView(new FocusParkingView(mContext));
+        // Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+        // when the user navigates to another window. This is also used to prevent wrap-around.
+        view.addView(new FocusParkingView(mContext), 0);
 
         mCachedViewMap.put(type, view);
         return mCachedViewMap.get(type);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
index a423003..13f2b7e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
@@ -23,6 +23,7 @@
 import com.android.systemui.car.window.SystemUIOverlayWindowController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
@@ -49,12 +50,14 @@
             DozeParameters dozeParameters,
             StatusBarStateController statusBarStateController,
             ConfigurationController configurationController,
+            KeyguardViewMediator keyguardViewMediator,
             KeyguardBypassController keyguardBypassController,
             SysuiColorExtractor colorExtractor,
             DumpManager dumpManager,
             SystemUIOverlayWindowController overlayWindowController) {
         super(context, windowManager, activityManager, dozeParameters, statusBarStateController,
-                configurationController, keyguardBypassController, colorExtractor, dumpManager);
+                configurationController, keyguardViewMediator, keyguardBypassController,
+                colorExtractor, dumpManager);
         mOverlayWindowController = overlayWindowController;
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index abdfad5..88b181f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -276,21 +276,23 @@
             intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
             intent.setData(Uri.fromParts("printjob", printJobId.flattenToString(), null));
         }
-        return PendingIntent.getActivity(mContext, 0, intent, 0);
+        return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
     }
 
     private PendingIntent createCancelIntent(PrintJobInfo printJob) {
         Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
         intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString());
         intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId());
-        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+        return PendingIntent.getBroadcast(mContext, 0, intent,
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
     }
 
     private PendingIntent createRestartIntent(PrintJobId printJobId) {
         Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
         intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + printJobId.flattenToString());
         intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId);
-        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+        return PendingIntent.getBroadcast(mContext, 0, intent,
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
     }
 
     private static boolean shouldNotifyForState(int state) {
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 3565b0e..e829974 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -16,9 +16,6 @@
 
 package com.android.settingslib.widget;
 
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_DEFAULT;
-
 import android.annotation.ColorInt;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -48,11 +45,12 @@
     private static final float ADVANCED_ICON_CENTER = 50f;
     private static final float ADVANCED_ICON_RADIUS = 48f;
 
+    public static final int ICON_TYPE_DEFAULT = 0;
+    public static final int ICON_TYPE_ADVANCED = 1;
+
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TYPE_DEFAULT, TYPE_ADVANCED})
+    @IntDef({ICON_TYPE_DEFAULT, ICON_TYPE_ADVANCED})
     public @interface AdaptiveOutlineIconType {
-        int TYPE_DEFAULT = 0;
-        int TYPE_ADVANCED = 1;
     }
 
     @VisibleForTesting
@@ -66,7 +64,7 @@
     public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
         super(new AdaptiveIconShapeDrawable(resources));
 
-        init(resources, bitmap, TYPE_DEFAULT);
+        init(resources, bitmap, ICON_TYPE_DEFAULT);
     }
 
     public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap,
@@ -96,10 +94,10 @@
     private @ColorInt int getColor(Resources resources, @AdaptiveOutlineIconType int type) {
         int resId;
         switch (type) {
-            case TYPE_ADVANCED:
+            case ICON_TYPE_ADVANCED:
                 resId = R.color.advanced_outline_color;
                 break;
-            case TYPE_DEFAULT:
+            case ICON_TYPE_DEFAULT:
             default:
                 resId = R.color.bt_outline_color;
                 break;
@@ -110,10 +108,10 @@
     private int getDimensionPixelSize(Resources resources, @AdaptiveOutlineIconType int type) {
         int resId;
         switch (type) {
-            case TYPE_ADVANCED:
+            case ICON_TYPE_ADVANCED:
                 resId = R.dimen.advanced_dashboard_tile_foreground_image_inset;
                 break;
-            case TYPE_DEFAULT:
+            case ICON_TYPE_DEFAULT:
             default:
                 resId = R.dimen.dashboard_tile_foreground_image_inset;
                 break;
@@ -133,7 +131,7 @@
         final int count = canvas.save();
         canvas.scale(scaleX, scaleY);
         // Draw outline
-        if (mType == TYPE_DEFAULT) {
+        if (mType == ICON_TYPE_DEFAULT) {
             canvas.drawPath(mPath, mOutlinePaint);
         } else {
             canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS,
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c27973f..287a1ac 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -22,7 +22,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="2333336097603822490">"No se pueden buscar las redes."</string>
     <string name="wifi_security_none" msgid="7392696451280611452">"Ninguna"</string>
-    <string name="wifi_remembered" msgid="3266709779723179188">"Guardada"</string>
+    <string name="wifi_remembered" msgid="3266709779723179188">"Guardado"</string>
     <string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
     <string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitada"</string>
     <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuración IP"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 3e8b1c1..f9d57c4 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -37,7 +37,7 @@
     <string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
-    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de rede"</string>
+    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
     <string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 08cf5f9..8407db6 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -211,8 +211,8 @@
     <string name="adb_wireless_error" msgid="721958772149779856">"Алдаа"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Wireless debugging"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Боломжтой төхөөрөмжүүдийг харах болох ашиглахын тулд wireless debugging-г асаана уу"</string>
-    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Хурдан хариу үйлдлийн кодоор төхөөрөмжийг хослуул"</string>
-    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Хурдан хариу үйлдлийн кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
+    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR кодоор төхөөрөмжийг хослуул"</string>
+    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
     <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Хослуулах кодоор төхөөрөмжийг хослуулна уу"</string>
     <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Зургаан оронтой кодыг ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
     <string name="adb_paired_devices_title" msgid="5268997341526217362">"Хослуулсан төхөөрөмжүүд"</string>
@@ -226,12 +226,12 @@
     <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi хослуулах код"</string>
     <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Хослуулалт амжилтгүй боллоо"</string>
     <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Төхөөрөмжийг ижил сүлжээнд холбосон эсэхийг шалгана уу."</string>
-    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Төхөөрөмжийг хослуулж байна…"</string>
-    <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. Хурдан хариу үйлдлийн код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
+    <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. QR код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP хаяг ба порт"</string>
-    <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Хурдан хариу үйлдлийн кодыг скан хийх"</string>
-    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+    <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодыг скан хийх"</string>
+    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi сүлжээнд холбогдоно уу"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, дебаг хийх, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Алдаа мэдээлэх товчлол"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index e33df4a..32cc39e 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -37,7 +37,7 @@
     <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
     <string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
-    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via netwerkbeoordelaar"</string>
+    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
     <string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 95e916b..68f7289 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,6 +1,6 @@
 package com.android.settingslib.bluetooth;
 
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
+import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
 
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
@@ -238,7 +238,7 @@
             final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
                     iconSize, false);
             bitmap.recycle();
-            return new AdaptiveOutlineDrawable(resources, resizedBitmap, TYPE_ADVANCED);
+            return new AdaptiveOutlineDrawable(resources, resizedBitmap, ICON_TYPE_ADVANCED);
         }
 
         return drawable;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
index 278b57d..e5ea446 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -41,7 +41,7 @@
 
     private final Bitmap mBitmap;
     private final int mSize;
-    private final Paint mPaint;
+    private Paint mIconPaint;
 
     private float mScale;
     private Rect mSrcRect;
@@ -75,18 +75,18 @@
         canvas.drawColor(0, PorterDuff.Mode.CLEAR);
 
         // opaque circle matte
-        mPaint = new Paint();
-        mPaint.setAntiAlias(true);
-        mPaint.setColor(Color.BLACK);
-        mPaint.setStyle(Paint.Style.FILL);
-        canvas.drawPath(fillPath, mPaint);
+        Paint paint = new Paint();
+        paint.setAntiAlias(true);
+        paint.setColor(Color.BLACK);
+        paint.setStyle(Paint.Style.FILL);
+        canvas.drawPath(fillPath, paint);
 
         // mask in the icon where the bitmap is opaque
-        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
-        canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+        canvas.drawBitmap(icon, cropRect, circleRect, paint);
 
         // prepare paint for frame drawing
-        mPaint.setXfermode(null);
+        paint.setXfermode(null);
 
         mScale = 1f;
 
@@ -100,7 +100,7 @@
         final float pad = (mSize - inside) / 2f;
 
         mDstRect.set(pad, pad, mSize - pad, mSize - pad);
-        canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+        canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, mIconPaint);
     }
 
     public void setScale(float scale) {
@@ -122,8 +122,12 @@
 
     @Override
     public void setColorFilter(ColorFilter cf) {
+        if (mIconPaint == null) {
+            mIconPaint = new Paint();
+        }
+        mIconPaint.setColorFilter(cf);
     }
-    
+
     @Override
     public int getIntrinsicWidth() {
         return mSize;
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2da84c2..482a381 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -172,5 +172,6 @@
         Settings.Secure.ONE_HANDED_MODE_ENABLED,
         Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
         Settings.Secure.TAPS_APP_TO_EXIT,
+        Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3fd543d..b337e60 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -259,5 +259,6 @@
         VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a946888..a994fda 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2573,6 +2573,10 @@
                 Settings.Secure.TAPS_APP_TO_EXIT,
                 SecureSettingsProto.OneHanded.TAPS_APP_TO_EXIT);
 
+        dumpSetting(s, p,
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+                SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
+
         // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index ee2d001..9d3620f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -25,6 +25,7 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.util.Log;
@@ -62,10 +63,12 @@
 
     public ThumbnailData(TaskSnapshot snapshot) {
         final HardwareBuffer buffer = snapshot.getHardwareBuffer();
-        if (buffer != null && (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+        if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
             // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
-            Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE");
-            thumbnail = Bitmap.createBitmap(buffer.getWidth(), buffer.getHeight(), ARGB_8888);
+            Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
+                    + buffer);
+            Point taskSize = snapshot.getTaskSize();
+            thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
             thumbnail.eraseColor(Color.BLACK);
         } else {
             thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index b966f93..255fffd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -114,6 +114,11 @@
         t.deferTransactionUntil(surfaceControl, barrier, frameNumber);
     }
 
+    public static void setRelativeLayer(Transaction t, SurfaceControl surfaceControl,
+            SurfaceControl relativeTo, int z) {
+        t.setRelativeLayer(surfaceControl, relativeTo, z);
+    }
+
     @Deprecated
     public static void setEarlyWakeup(Transaction t) {
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 10dcbd6..a84664c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -448,8 +448,8 @@
                                 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
                                         + " UpdateSim.onSimCheckResponse: "
                                         + " attemptsRemaining=" + result.getAttemptsRemaining());
-                                mStateMachine.reset();
                             }
+                            mStateMachine.reset();
                             mCheckSimPukThread = null;
                         }
                     });
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 127c5dd..e12b7dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -5,6 +5,7 @@
 import android.content.res.Configuration
 import android.graphics.Color
 import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
+import android.util.Log
 import android.util.MathUtils
 import android.view.LayoutInflater
 import android.view.View
@@ -25,6 +26,7 @@
 import javax.inject.Provider
 import javax.inject.Singleton
 
+private const val TAG = "MediaCarouselController"
 private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
 
 /**
@@ -236,7 +238,9 @@
         val oldData = mediaPlayers[oldKey]
         if (oldData != null) {
             val oldData = mediaPlayers.remove(oldKey)
-            mediaPlayers.put(key, oldData!!)
+            mediaPlayers.put(key, oldData!!)?.let {
+                Log.wtf(TAG, "new key $key already exists when migrating from $oldKey")
+            }
         }
         var existingPlayer = mediaPlayers[key]
         if (existingPlayer == null) {
@@ -271,6 +275,11 @@
         updatePageIndicator()
         mediaCarouselScrollHandler.onPlayersChanged()
         mediaCarousel.requiresRemeasuring = true
+        // Check postcondition: mediaContent should have the same number of children as there are
+        // elements in mediaPlayers.
+        if (mediaPlayers.size != mediaContent.childCount) {
+            Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
+        }
     }
 
     private fun removePlayer(key: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 8cb93bf..299ae5b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -517,22 +517,36 @@
 
     fun onNotificationRemoved(key: String) {
         Assert.isMainThread()
-        if (useMediaResumption && mediaEntries.get(key)?.resumeAction != null) {
+        val removed = mediaEntries.remove(key)
+        if (useMediaResumption && removed?.resumeAction != null) {
             Log.d(TAG, "Not removing $key because resumable")
-            // Move to resume key aka package name
-            val data = mediaEntries.remove(key)!!
-            val resumeAction = getResumeMediaAction(data.resumeAction!!)
-            val updated = data.copy(token = null, actions = listOf(resumeAction),
+            // Move to resume key (aka package name) if that key doesn't already exist.
+            val resumeAction = getResumeMediaAction(removed.resumeAction!!)
+            val updated = removed.copy(token = null, actions = listOf(resumeAction),
                     actionsToShowInCompact = listOf(0), active = false, resumption = true)
-            mediaEntries.put(data.packageName, updated)
-            // Notify listeners of "new" controls
+            val pkg = removed?.packageName
+            val migrate = mediaEntries.put(pkg, updated) == null
+            // Notify listeners of "new" controls when migrating or removed and update when not
             val listenersCopy = listeners.toSet()
-            listenersCopy.forEach {
-                it.onMediaDataLoaded(data.packageName, key, updated)
+            if (migrate) {
+                listenersCopy.forEach {
+                    it.onMediaDataLoaded(pkg, key, updated)
+                }
+            } else {
+                // Since packageName is used for the key of the resumption controls, it is
+                // possible that another notification has already been reused for the resumption
+                // controls of this package. In this case, rather than renaming this player as
+                // packageName, just remove it and then send a update to the existing resumption
+                // controls.
+                listenersCopy.forEach {
+                    it.onMediaDataRemoved(key)
+                }
+                listenersCopy.forEach {
+                    it.onMediaDataLoaded(pkg, pkg, updated)
+                }
             }
             return
         }
-        val removed = mediaEntries.remove(key)
         if (removed != null) {
             val listenersCopy = listeners.toSet()
             listenersCopy.forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
index 5d9439f..c581ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.onehanded;
 
+import android.view.SurfaceControl;
+
 /**
  * Additional callback interface for OneHanded animation
  */
@@ -30,7 +32,7 @@
     /**
      * Called when OneHanded animation is ended.
      */
-    default void onOneHandedAnimationEnd(
+    default void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
             OneHandedAnimationController.OneHandedTransitionAnimator animator) {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
index 08f08c5..20ab114 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
@@ -29,6 +29,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
 
 import javax.inject.Inject;
 
@@ -39,18 +40,6 @@
     private static final float FRACTION_START = 0f;
     private static final float FRACTION_END = 1f;
 
-    public static final int ANIM_TYPE_TRANSLATE = 0;
-    public static final int ANIM_TYPE_SCALE = 1;
-
-    // Note: ANIM_TYPE_SCALE reserve for the future development
-    @IntDef(prefix = {"ANIM_TYPE_"}, value = {
-            ANIM_TYPE_TRANSLATE,
-            ANIM_TYPE_SCALE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AnimationType {
-    }
-
     public static final int TRANSITION_DIRECTION_NONE = 0;
     public static final int TRANSITION_DIRECTION_TRIGGER = 1;
     public static final int TRANSITION_DIRECTION_EXIT = 2;
@@ -66,7 +55,8 @@
 
     private final Interpolator mFastOutSlowInInterpolator;
     private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
-    private OneHandedTransitionAnimator mCurrentAnimator;
+    private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap =
+            new HashMap<>();
 
     /**
      * Constructor of OneHandedAnimationController
@@ -82,18 +72,33 @@
     @SuppressWarnings("unchecked")
     OneHandedTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds,
             Rect endBounds) {
-        if (mCurrentAnimator == null) {
-            mCurrentAnimator = setupOneHandedTransitionAnimator(
-                    OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
-        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_TRANSLATE
-                && mCurrentAnimator.isRunning()) {
-            mCurrentAnimator.updateEndValue(endBounds);
+        final OneHandedTransitionAnimator animator = mAnimatorMap.get(leash);
+        if (animator == null) {
+            mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+                    OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
+        } else if (animator.isRunning()) {
+            animator.updateEndValue(endBounds);
         } else {
-            mCurrentAnimator.cancel();
-            mCurrentAnimator = setupOneHandedTransitionAnimator(
-                    OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+            animator.cancel();
+            mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+                    OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
         }
-        return mCurrentAnimator;
+        return mAnimatorMap.get(leash);
+    }
+
+    HashMap<SurfaceControl, OneHandedTransitionAnimator> getAnimatorMap() {
+        return mAnimatorMap;
+    }
+
+    boolean isAnimatorsConsumed() {
+        return mAnimatorMap.isEmpty();
+    }
+
+    void removeAnimator(SurfaceControl key) {
+        final OneHandedTransitionAnimator animator = mAnimatorMap.remove(key);
+        if (animator != null && animator.isRunning()) {
+            animator.cancel();
+        }
     }
 
     OneHandedTransitionAnimator setupOneHandedTransitionAnimator(
@@ -114,7 +119,6 @@
             ValueAnimator.AnimatorListener {
 
         private final SurfaceControl mLeash;
-        private final @AnimationType int mAnimationType;
         private T mStartValue;
         private T mEndValue;
         private T mCurrentValue;
@@ -127,10 +131,8 @@
         private @TransitionDirection int mTransitionDirection;
         private int mTransitionOffset;
 
-        private OneHandedTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
-                T startValue, T endValue) {
+        private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) {
             mLeash = leash;
-            mAnimationType = animationType;
             mStartValue = startValue;
             mEndValue = endValue;
             addListener(this);
@@ -150,8 +152,10 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             mCurrentValue = mEndValue;
+            final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+            onEndTransaction(mLeash, tx);
             if (mOneHandedAnimationCallback != null) {
-                mOneHandedAnimationCallback.onOneHandedAnimationEnd(this);
+                mOneHandedAnimationCallback.onOneHandedAnimationEnd(tx, this);
             }
         }
 
@@ -196,6 +200,10 @@
             return this;
         }
 
+        SurfaceControl getLeash() {
+            return mLeash;
+        }
+
         Rect getDestinationBounds() {
             return (Rect) mEndValue;
         }
@@ -227,11 +235,6 @@
             return mEndValue;
         }
 
-        @AnimationType
-        int getAnimationType() {
-            return mAnimationType;
-        }
-
         void setCurrentValue(T value) {
             mCurrentValue = value;
         }
@@ -250,11 +253,9 @@
         @VisibleForTesting
         static OneHandedTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
                 Rect startValue, Rect endValue) {
-            // At R, we only support translate type first.
-            final int animType = ANIM_TYPE_TRANSLATE;
 
-            return new OneHandedTransitionAnimator<Rect>(leash, animType,
-                    new Rect(startValue), new Rect(endValue)) {
+            return new OneHandedTransitionAnimator<Rect>(leash, new Rect(startValue),
+                    new Rect(endValue)) {
 
                 private final Rect mTmpRect = new Rect();
 
@@ -282,7 +283,7 @@
                 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
                     getSurfaceTransactionHelper()
                             .alpha(tx, leash, 1f)
-                            .crop(tx, leash, getStartValue())
+                            .translate(tx, leash, getEndValue().top - getStartValue().top)
                             .round(tx, leash);
                     tx.apply();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 063d7c8..28d7049 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -22,11 +22,14 @@
 import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaInfo;
 import android.window.DisplayAreaOrganizer;
@@ -39,10 +42,12 @@
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.Dumpable;
 import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayLayout;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 
@@ -68,22 +73,22 @@
     @VisibleForTesting
     static final int MSG_OFFSET_FINISH = 3;
 
-    private final Handler mUpdateHandler;
+    private final Rect mLastVisualDisplayBounds = new Rect();
+    private final Rect mDefaultDisplayBounds = new Rect();
 
+    private Handler mUpdateHandler;
     private boolean mIsInOneHanded;
     private int mEnterExitAnimationDurationMs;
 
     @VisibleForTesting
-    DisplayAreaInfo mDisplayAreaInfo;
+    HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
     private DisplayController mDisplayController;
+    private DisplayLayout mDisplayLayout;
+    private DisplayInfo mDisplayInfo = new DisplayInfo();
     private OneHandedAnimationController mAnimationController;
-    private SurfaceControl mLeash;
-    private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
-    private OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
     private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
-    private Rect mLastVisualDisplayBounds;
-    private Rect mDefaultDisplayBounds;
+    private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
 
     @VisibleForTesting
     OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -94,17 +99,23 @@
                 }
 
                 @Override
-                public void onOneHandedAnimationEnd(
+                public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
                         OneHandedAnimationController.OneHandedTransitionAnimator animator) {
-                    mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
-                            obtainArgsFromAnimator(animator)));
+                    mAnimationController.removeAnimator(animator.getLeash());
+                    if (mAnimationController.isAnimatorsConsumed()) {
+                        mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+                                obtainArgsFromAnimator(animator)));
+                    }
                 }
 
                 @Override
                 public void onOneHandedAnimationCancel(
                         OneHandedAnimationController.OneHandedTransitionAnimator animator) {
-                    mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
-                            obtainArgsFromAnimator(animator)));
+                    mAnimationController.removeAnimator(animator.getLeash());
+                    if (mAnimationController.isAnimatorsConsumed()) {
+                        mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+                                obtainArgsFromAnimator(animator)));
+                    }
                 }
             };
 
@@ -112,34 +123,24 @@
     private Handler.Callback mUpdateCallback = (msg) -> {
         SomeArgs args = (SomeArgs) msg.obj;
         final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds;
-        final int xOffset = args.argi1;
         final int yOffset = args.argi2;
         final int direction = args.argi3;
 
-        if (mLeash == null || !mLeash.isValid()) {
-            Log.w(TAG, "mLeash is null, abort transition");
-            return false;
-        }
         switch (msg.what) {
             case MSG_RESET_IMMEDIATE:
-                final OneHandedAnimationController.OneHandedTransitionAnimator animator =
-                        mAnimationController.getAnimator(mLeash, currentBounds,
-                                mDefaultDisplayBounds);
-                if (animator != null && animator.isRunning()) {
-                    animator.cancel();
-                }
-                final SurfaceControl.Transaction tx =
-                        mSurfaceControlTransactionFactory.getTransaction();
-                tx.setPosition(mLeash, 0, 0)
-                        .setWindowCrop(mLeash, -1/* reset */, -1/* reset */)
-                        .apply();
-                finishOffset(currentBounds, yOffset, direction);
+                resetWindowsOffset();
+                mLastVisualDisplayBounds.set(currentBounds);
+                finishOffset(0, TRANSITION_DIRECTION_EXIT);
                 break;
             case MSG_OFFSET_ANIMATE:
-                offsetWindows(currentBounds, 0, yOffset, direction, mEnterExitAnimationDurationMs);
+                final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
+                        mDefaultDisplayBounds.top + yOffset,
+                        mDefaultDisplayBounds.right,
+                        mDefaultDisplayBounds.bottom + yOffset);
+                offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs);
                 break;
             case MSG_OFFSET_FINISH:
-                finishOffset(currentBounds, yOffset, direction);
+                finishOffset(yOffset, direction);
                 break;
         }
         args.recycle();
@@ -152,17 +153,15 @@
     @Inject
     public OneHandedDisplayAreaOrganizer(Context context,
             DisplayController displayController,
-            OneHandedAnimationController animationController,
-            OneHandedSurfaceTransactionHelper surfaceTransactionHelper) {
-
+            OneHandedAnimationController animationController) {
         mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
         mAnimationController = animationController;
         mDisplayController = displayController;
+        mDisplayLayout = getDisplayLayout(context);
+        mDisplayLayout.getStableBounds(mDefaultDisplayBounds);
+        mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
         mEnterExitAnimationDurationMs = context.getResources().getInteger(
                 com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
-        mDefaultDisplayBounds = new Rect(0, 0, getDisplayBounds().x, getDisplayBounds().y);
-        mLastVisualDisplayBounds = new Rect(mDefaultDisplayBounds);
-        mSurfaceTransactionHelper = surfaceTransactionHelper;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
     }
 
@@ -171,40 +170,52 @@
             @NonNull SurfaceControl leash) {
         Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
         Objects.requireNonNull(leash, "leash must not be null");
-        mDisplayAreaInfo = displayAreaInfo;
-        mLeash = leash;
-    }
 
-    @Override
-    public void onDisplayAreaInfoChanged(@NonNull DisplayAreaInfo displayAreaInfo) {
-        Objects.requireNonNull(displayAreaInfo, "displayArea must not be null");
-        // Stop one handed without animation and reset cropped size immediately
-        if (displayAreaInfo.configuration.orientation
-                != mDisplayAreaInfo.configuration.orientation) {
-            final Rect newBounds = displayAreaInfo.configuration.windowConfiguration.getBounds();
-            resetImmediately(newBounds);
+        if (displayAreaInfo.featureId != FEATURE_ONE_HANDED) {
+            Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
+            return;
         }
-        mDisplayAreaInfo = displayAreaInfo;
+        mDisplayAreaMap.put(displayAreaInfo, leash);
     }
 
     @Override
     public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
         Objects.requireNonNull(displayAreaInfo,
                 "Requires valid displayArea, and displayArea must not be null");
-        if (displayAreaInfo.token.asBinder() != mDisplayAreaInfo.token.asBinder()) {
+
+        if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
             Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
             return;
         }
-        mDisplayAreaInfo = displayAreaInfo;
+        mDisplayAreaMap.remove(displayAreaInfo);
+    }
 
-        // Stop one handed immediately
-        if (isInOneHanded()) {
-            final Rect newBounds = mDisplayAreaInfo.configuration.windowConfiguration.getBounds();
-            resetImmediately(newBounds);
+    @Override
+    public void unregisterOrganizer() {
+        super.unregisterOrganizer();
+        if (mDisplayAreaMap != null) {
+            mDisplayAreaMap.clear();
         }
     }
 
     /**
+     * Handler for display rotation changes.
+     */
+    public void onRotateDisplay(Resources res, int toRotation) {
+        // Stop one handed without animation and reset cropped size immediately
+        final Rect newBounds = new Rect();
+        mDisplayLayout.rotateTo(res, toRotation);
+        mDisplayLayout.getStableBounds(newBounds);
+
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = newBounds;
+        args.argi1 = 0 /* xOffset */;
+        args.argi2 = 0 /* yOffset */;
+        args.argi3 = TRANSITION_DIRECTION_EXIT;
+        mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+    }
+
+    /**
      * Offset the windows by a given offset on Y-axis, triggered also from screen rotation.
      * Directly perform manipulation/offset on the leash.
      */
@@ -217,55 +228,43 @@
         mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
     }
 
-    /**
-     * Immediately resize/reset leash from previous cropped boundary to default.
-     * (i.e Screen rotation need to reset crop and position before applying new bounds)
-     */
-    public void resetImmediately(Rect newDisplayBounds) {
-        updateDisplayBounds(newDisplayBounds);
-        if (mDisplayAreaInfo != null && mLeash != null) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = newDisplayBounds;
-            args.argi1 = 0 /* xOffset */;
-            args.argi2 = 0 /* yOffset */;
-            args.argi3 = TRANSITION_DIRECTION_EXIT;
-            mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
-        }
-    }
-
-    private void offsetWindows(Rect currentbounds, int xOffset, int yOffset, int direction,
-            int durationMs) {
+    private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) {
         if (Looper.myLooper() != mUpdateHandler.getLooper()) {
             throw new RuntimeException("Callers should call scheduleOffset() instead of this "
                     + "directly");
         }
-        if (mDisplayAreaInfo == null || mLeash == null) {
-            Log.w(TAG, "mToken is not set");
-            return;
-        }
-
-        Rect toBounds = new Rect(mDefaultDisplayBounds.left,
-                mDefaultDisplayBounds.top + yOffset,
-                mDefaultDisplayBounds.right,
-                mDefaultDisplayBounds.bottom + yOffset);
-        animateWindows(currentbounds, toBounds, direction, durationMs);
+        mDisplayAreaMap.forEach(
+                (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction,
+                        durationMs));
     }
 
-    private void animateWindows(Rect fromBounds, Rect toBounds,
+    private void resetWindowsOffset() {
+        mUpdateHandler.post(() -> {
+            final SurfaceControl.Transaction tx =
+                    mSurfaceControlTransactionFactory.getTransaction();
+            mDisplayAreaMap.forEach(
+                    (key, leash) -> {
+                        final OneHandedAnimationController.OneHandedTransitionAnimator animator =
+                                mAnimationController.getAnimatorMap().remove(leash);
+                        if (animator != null && animator.isRunning()) {
+                            animator.cancel();
+                        }
+                        tx.setPosition(leash, 0, 0)
+                                .setWindowCrop(leash, -1/* reset */, -1/* reset */);
+                    });
+            tx.apply();
+        });
+    }
+
+    private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
             @OneHandedAnimationController.TransitionDirection int direction, int durationMs) {
         if (Looper.myLooper() != mUpdateHandler.getLooper()) {
             throw new RuntimeException("Callers should call scheduleOffset() instead of "
                     + "this directly");
         }
-        // Could happen when exit one handed
-        if (mDisplayAreaInfo == null || mLeash == null) {
-            Log.w(TAG, "Abort animation, invalid leash");
-            return;
-        }
-
         mUpdateHandler.post(() -> {
             final OneHandedAnimationController.OneHandedTransitionAnimator animator =
-                    mAnimationController.getAnimator(mLeash, fromBounds, toBounds);
+                    mAnimationController.getAnimator(leash, fromBounds, toBounds);
             if (animator != null) {
                 animator.setTransitionDirection(direction)
                         .setOneHandedAnimationCallback(mOneHandedAnimationCallback)
@@ -275,7 +274,7 @@
         });
     }
 
-    private void finishOffset(Rect currentBounds, int offset,
+    private void finishOffset(int offset,
             @OneHandedAnimationController.TransitionDirection int direction) {
         if (Looper.myLooper() != mUpdateHandler.getLooper()) {
             throw new RuntimeException(
@@ -284,18 +283,14 @@
         // Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence,
         // the flag *MUST* be updated before dispatch mTransitionCallbacks
         mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER);
-        mLastVisualDisplayBounds.set(currentBounds);
-        if (direction == TRANSITION_DIRECTION_TRIGGER) {
-            mLastVisualDisplayBounds.offsetTo(0, offset);
-            for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
-                final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
-                callback.onStartFinished(mLastVisualDisplayBounds);
-            }
-        } else {
-            mLastVisualDisplayBounds.offsetTo(0, 0);
-            for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
-                final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
-                callback.onStopFinished(mLastVisualDisplayBounds);
+        mLastVisualDisplayBounds.offsetTo(0,
+                direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
+        for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
+            final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
+            if (direction == TRANSITION_DIRECTION_TRIGGER) {
+                callback.onStartFinished(getLastVisualDisplayBounds());
+            } else {
+                callback.onStopFinished(getLastVisualDisplayBounds());
             }
         }
     }
@@ -332,6 +327,16 @@
         return mUpdateHandler;
     }
 
+    private DisplayLayout getDisplayLayout(Context context) {
+        final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
+        if (display != null) {
+            display.getDisplayInfo(mDisplayInfo);
+        } else {
+            Log.w(TAG, "get DEFAULT_DISPLAY return null");
+        }
+        return new DisplayLayout(mDisplayInfo, context.getResources(), false, false);
+    }
+
     /**
      * Register transition callback
      */
@@ -349,29 +354,23 @@
         return args;
     }
 
-    private void updateDisplayBounds(Rect newDisplayBounds) {
-        if (newDisplayBounds == null) {
-            mDefaultDisplayBounds.set(0, 0, getDisplayBounds().x, getDisplayBounds().y);
-        } else {
-            mDefaultDisplayBounds.set(newDisplayBounds);
-        }
-    }
-
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         final String innerPrefix = "  ";
         pw.println(TAG + "states: ");
         pw.print(innerPrefix + "mIsInOneHanded=");
         pw.println(mIsInOneHanded);
-        pw.print(innerPrefix + "mDisplayAreaInfo=");
-        pw.println(mDisplayAreaInfo);
-        pw.print(innerPrefix + "mLeash=");
-        pw.println(mLeash);
+        pw.print(innerPrefix + "mDisplayAreaMap=");
+        pw.println(mDisplayAreaMap);
         pw.print(innerPrefix + "mDefaultDisplayBounds=");
         pw.println(mDefaultDisplayBounds);
         pw.print(innerPrefix + "mLastVisualDisplayBounds=");
         pw.println(mLastVisualDisplayBounds);
         pw.print(innerPrefix + "getDisplayBounds()=");
         pw.println(getDisplayBounds());
+        if (mDisplayLayout != null) {
+            pw.print(innerPrefix + "mDisplayLayout(w, h)=");
+            pw.println(mDisplayLayout.width() + ", " + mDisplayLayout.height());
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index d60b43f..4dacdf3 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -27,11 +27,13 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.wm.DisplayChangeController;
 import com.android.systemui.wm.DisplayController;
 
 import java.io.FileDescriptor;
@@ -50,15 +52,18 @@
     private boolean mIsOneHandedEnabled;
     private boolean mTaskChangeToExit;
     private float mOffSetFraction;
-    private DisplayController mDisplayController;
+
+    private final DisplayController mDisplayController;
+    private final OneHandedGestureHandler mGestureHandler;
+    private final OneHandedTimeoutHandler mTimeoutHandler;
+    private final OneHandedTouchHandler mTouchHandler;
+    private final SysUiState mSysUiFlagContainer;
+
+    private Context mContext;
     private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
-    private OneHandedGestureHandler mGestureHandler;
     private OneHandedGestureHandler.OneHandedGestureEventCallback mGestureEventCallback;
-    private OneHandedTimeoutHandler mTimeoutHandler;
-    private OneHandedTouchHandler mTouchHandler;
     private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback;
     private OneHandedTransitionCallback mTransitionCallback;
-    private SysUiState mSysUiFlagContainer;
 
     /**
      * Handler for system task stack changes, exit when user lunch new task or bring task to front
@@ -84,6 +89,17 @@
     };
 
     /**
+     * Handle rotation based on OnDisplayChangingListener callback
+     */
+    @VisibleForTesting
+    private final DisplayChangeController.OnDisplayChangingListener mRotationController =
+            (display, fromRotation, toRotation, wct) -> {
+                if (mDisplayAreaOrganizer != null) {
+                    mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation);
+                }
+            };
+
+    /**
      * Constructor of OneHandedManager
      */
     @Inject
@@ -93,9 +109,10 @@
             OneHandedTouchHandler touchHandler,
             OneHandedGestureHandler gestureHandler,
             SysUiState sysUiState) {
-
+        mContext = context;
         mDisplayAreaOrganizer = displayAreaOrganizer;
         mDisplayController = displayController;
+        mDisplayController.addDisplayChangingController(mRotationController);
         mSysUiFlagContainer = sysUiState;
         mOffSetFraction =
                 context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index b6e4e16..a8130a1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -171,7 +171,6 @@
     private float mSavedSnapFraction = -1f;
     private boolean mSendingHoverAccessibilityEvents;
     private boolean mMovementWithinDismiss;
-    private boolean mHideMenuAfterShown = false;
     private PipAccessibilityInteractionConnection mConnection;
 
     // Touch state
@@ -242,7 +241,8 @@
                         this::updateMovementBounds, sysUiState);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
-                        true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()));
+                        true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
+                        menuController::hideMenu);
 
         Resources res = context.getResources();
         mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
@@ -708,6 +708,7 @@
                 // on and changing MotionEvents into HoverEvents.
                 // Let's not enable menu show/hide for a11y services.
                 if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+                    mTouchState.removeHoverExitTimeoutCallback();
                     mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
                             false /* allowMenuTimeout */, false /* willResizeMenu */,
                             shouldShowResizeHandle());
@@ -724,8 +725,7 @@
                 // on and changing MotionEvents into HoverEvents.
                 // Let's not enable menu show/hide for a11y services.
                 if (!mAccessibilityManager.isTouchExplorationEnabled()) {
-                    mHideMenuAfterShown = true;
-                    mMenuController.hideMenu();
+                    mTouchState.scheduleHoverExitTimeoutCallback();
                 }
                 if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
                     sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
@@ -811,9 +811,6 @@
                 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
                         mMovementBounds, mExpandedMovementBounds, callback);
             }
-            if (mHideMenuAfterShown) {
-                mMenuController.hideMenu();
-            }
         } else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
             // Try and restore the PiP to the closest edge, using the saved snap fraction
             // if possible
@@ -851,7 +848,6 @@
             }
         }
         mMenuState = menuState;
-        mHideMenuAfterShown = false;
         updateMovementBounds();
         // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
         // as well, or it can't handle a11y focus and pip menu can't perform any action.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 132c04d..ecd1128a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -36,10 +36,12 @@
 
     @VisibleForTesting
     static final long DOUBLE_TAP_TIMEOUT = 200;
+    static final long HOVER_EXIT_TIMEOUT = 50;
 
     private final Handler mHandler;
     private final ViewConfiguration mViewConfig;
     private final Runnable mDoubleTapTimeoutCallback;
+    private final Runnable mHoverExitTimeoutCallback;
 
     private VelocityTracker mVelocityTracker;
     private long mDownTouchTime = 0;
@@ -64,10 +66,11 @@
     private int mActivePointerId;
 
     public PipTouchState(ViewConfiguration viewConfig, Handler handler,
-            Runnable doubleTapTimeoutCallback) {
+            Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) {
         mViewConfig = viewConfig;
         mHandler = handler;
         mDoubleTapTimeoutCallback = doubleTapTimeoutCallback;
+        mHoverExitTimeoutCallback = hoverExitTimeoutCallback;
     }
 
     /**
@@ -197,6 +200,10 @@
                 recycleVelocityTracker();
                 break;
             }
+            case MotionEvent.ACTION_BUTTON_PRESS: {
+                removeHoverExitTimeoutCallback();
+                break;
+            }
         }
     }
 
@@ -326,6 +333,15 @@
         mHandler.removeCallbacks(mDoubleTapTimeoutCallback);
     }
 
+    void scheduleHoverExitTimeoutCallback() {
+        mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+        mHandler.postDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT);
+    }
+
+    void removeHoverExitTimeoutCallback() {
+        mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+    }
+
     void addMovementToVelocityTracker(MotionEvent event) {
         if (mVelocityTracker == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index fba7a22..2ef6934 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -21,7 +21,6 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
@@ -33,6 +32,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 import com.android.systemui.R;
 import com.android.systemui.qs.PseudoGridView;
 import com.android.systemui.qs.QSUserSwitcherEvent;
@@ -100,7 +100,8 @@
             if (item.picture == null) {
                 v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
             } else {
-                Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+                int avatarSize = (int) v.getResources().getDimension(R.dimen.qs_framed_avatar_size);
+                Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
                 drawable.setColorFilter(
                         item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
                 v.bind(name, drawable, item.info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 6f554e6..b6c6afd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -675,6 +675,9 @@
     }
 
     private void notifySplitScreenBoundsChanged() {
+        if (mSplitLayout.mPrimary == null || mSplitLayout.mSecondary == null) {
+            return;
+        }
         mOtherTaskRect.set(mSplitLayout.mSecondary);
 
         mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 423f85f..bd65ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -282,7 +282,7 @@
                     + " doesn't match existing key " + mKey);
         }
 
-        mRanking = ranking;
+        mRanking = ranking.withAudiblyAlertedInfo(mRanking);
     }
 
     /*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 304fe00..539158c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -614,8 +614,10 @@
         // Bubble controller will give us a valid display id if it should get the back event
         BubbleController bubbleController = Dependency.get(BubbleController.class);
         int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
-        if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
+        if (bubbleDisplayId != INVALID_DISPLAY) {
             ev.setDisplayId(bubbleDisplayId);
+        } else {
+            ev.setDisplayId(mContext.getDisplay().getDisplayId());
         }
         InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 2e4a929d..5164440 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -84,6 +84,7 @@
     private final boolean mKeyguardScreenRotation;
     private final long mLockScreenDisplayTimeout;
     private final Display.Mode mKeyguardDisplayMode;
+    private final KeyguardViewMediator mKeyguardViewMediator;
     private final KeyguardBypassController mKeyguardBypassController;
     private ViewGroup mNotificationShadeView;
     private LayoutParams mLp;
@@ -104,6 +105,7 @@
             IActivityManager activityManager, DozeParameters dozeParameters,
             StatusBarStateController statusBarStateController,
             ConfigurationController configurationController,
+            KeyguardViewMediator keyguardViewMediator,
             KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor,
             DumpManager dumpManager) {
         mContext = context;
@@ -113,6 +115,7 @@
         mDozeParameters = dozeParameters;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
         mLpChanged = new LayoutParams();
+        mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardBypassController = keyguardBypassController;
         mColorExtractor = colorExtractor;
         dumpManager.registerDumpable(getClass().getName(), this);
@@ -202,6 +205,11 @@
         mWindowManager.addView(mNotificationShadeView, mLp);
         mLpChanged.copyFrom(mLp);
         onThemeChanged();
+
+        // Make the state consistent with KeyguardViewMediator#setupLocked during initialization.
+        if (mKeyguardViewMediator.isShowingAndNotOccluded()) {
+            setKeyguardShowing(true);
+        }
     }
 
     public void setNotificationShadeView(ViewGroup view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 60fc17d..686b871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -348,10 +348,8 @@
         }
 
         if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
-            // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
-            // with too many things at this case, in order to not skip the initial frames.
-            mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
             mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+            scheduleUpdate();
         } else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
                 || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
             // Scheduling a frame isn't enough when:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 07de388..c43ad36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -106,9 +106,9 @@
 
     public void updateNotification(@NonNull String key, boolean alert) {
         super.updateNotification(key, alert);
-        AlertEntry alertEntry = getHeadsUpEntry(key);
-        if (alert && alertEntry != null) {
-            setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(alertEntry.mEntry));
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+        if (alert && headsUpEntry != null) {
+            setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 512d0f3..5ec5ec6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -24,7 +24,6 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.database.DataSetObserver;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
@@ -36,6 +35,7 @@
 import android.widget.FrameLayout;
 
 import com.android.settingslib.animation.AppearAnimationUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -288,7 +288,8 @@
             if (item.picture == null) {
                 v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
             } else {
-                Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+                int avatarSize = (int) v.getResources().getDimension(R.dimen.kg_framed_avatar_size);
+                Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
                 drawable.setColorFilter(
                         item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
                 v.bind(name, drawable, item.info.id);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index f29f042..3d679de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -26,11 +26,14 @@
 import android.util.Log;
 import android.view.Display;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.Set;
 
 public class SysuiTestableContext extends TestableContext {
 
-    private Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
+    @GuardedBy("mRegisteredReceivers")
+    private final Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
 
     public SysuiTestableContext(Context base) {
         super(base);
@@ -54,7 +57,11 @@
     }
 
     public void cleanUpReceivers(String testName) {
-        Set<BroadcastReceiver> copy = new ArraySet<>(mRegisteredReceivers);
+        Set<BroadcastReceiver> copy;
+        synchronized (mRegisteredReceivers) {
+            copy = new ArraySet<>(mRegisteredReceivers);
+            mRegisteredReceivers.clear();
+        }
         for (BroadcastReceiver r : copy) {
             try {
                 unregisterReceiver(r);
@@ -68,7 +75,9 @@
     @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
         if (receiver != null) {
-            mRegisteredReceivers.add(receiver);
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
         }
         return super.registerReceiver(receiver, filter);
     }
@@ -77,7 +86,9 @@
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
             String broadcastPermission, Handler scheduler) {
         if (receiver != null) {
-            mRegisteredReceivers.add(receiver);
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
         }
         return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
     }
@@ -86,7 +97,9 @@
     public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
             IntentFilter filter, String broadcastPermission, Handler scheduler) {
         if (receiver != null) {
-            mRegisteredReceivers.add(receiver);
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
         }
         return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler);
     }
@@ -94,7 +107,9 @@
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
         if (receiver != null) {
-            mRegisteredReceivers.remove(receiver);
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.remove(receiver);
+            }
         }
         super.unregisterReceiver(receiver);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5b46b7f..15828b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -63,6 +63,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -135,6 +136,8 @@
     @Mock
     private SysuiStatusBarStateController mStatusBarStateController;
     @Mock
+    private KeyguardViewMediator mKeyguardViewMediator;
+    @Mock
     private KeyguardBypassController mKeyguardBypassController;
     @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -198,8 +201,8 @@
 
         mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
-                mConfigurationController, mKeyguardBypassController, mColorExtractor,
-                mDumpManager);
+                mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+                mColorExtractor, mDumpManager);
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowController.attach();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 058478f..686a094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -60,6 +60,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
@@ -132,6 +133,8 @@
     @Mock
     private SysuiStatusBarStateController mStatusBarStateController;
     @Mock
+    private KeyguardViewMediator mKeyguardViewMediator;
+    @Mock
     private KeyguardBypassController mKeyguardBypassController;
     @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -194,8 +197,8 @@
         // Bubbles get added to status bar window view
         mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
-                mConfigurationController, mKeyguardBypassController, mColorExtractor,
-                mDumpManager);
+                mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+                mColorExtractor, mDumpManager);
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowController.attach();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 6761b28..59c2d0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -31,6 +31,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 private const val KEY = "KEY"
+private const val KEY_2 = "KEY_2"
 private const val PACKAGE_NAME = "com.android.systemui"
 private const val APP_NAME = "SystemUI"
 private const val SESSION_ARTIST = "artist"
@@ -156,8 +157,43 @@
         mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
         // WHEN the notification is removed
         mediaDataManager.onNotificationRemoved(KEY)
-        // THEN the media data indicates that it is
+        // THEN the media data indicates that it is for resumption
         assertThat(listener.data!!.resumption).isTrue()
+        // AND the new key is the package name
+        assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+        assertThat(listener.oldKey!!).isEqualTo(KEY)
+        assertThat(listener.removedKey).isNull()
+    }
+
+    @Test
+    fun testOnNotificationRemoved_twoWithResumption() {
+        // GIVEN that the manager has two notifications with resume actions
+        val listener = TestListener()
+        mediaDataManager.addListener(listener)
+        whenever(controller.metadata).thenReturn(metadataBuilder.build())
+        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+        mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+        val data = listener.data!!
+        assertThat(data.resumption).isFalse()
+        val resumableData = data.copy(resumeAction = Runnable {})
+        mediaDataManager.onMediaDataLoaded(KEY, null, resumableData)
+        mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData)
+        // WHEN the first is removed
+        mediaDataManager.onNotificationRemoved(KEY)
+        // THEN the data is for resumption and the key is migrated to the package name
+        assertThat(listener.data!!.resumption).isTrue()
+        assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+        assertThat(listener.oldKey!!).isEqualTo(KEY)
+        assertThat(listener.removedKey).isNull()
+        // WHEN the second is removed
+        mediaDataManager.onNotificationRemoved(KEY_2)
+        // THEN the data is for resumption and the second key is removed
+        assertThat(listener.data!!.resumption).isTrue()
+        assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+        assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME)
+        assertThat(listener.removedKey!!).isEqualTo(KEY_2)
     }
 
     @Test
@@ -190,6 +226,7 @@
         var data: MediaData? = null
         var key: String? = null
         var oldKey: String? = null
+        var removedKey: String? = null
 
         override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
             this.key = key
@@ -198,9 +235,7 @@
         }
 
         override fun onMediaDataRemoved(key: String) {
-            this.key = key
-            oldKey = null
-            data = null
+            removedKey = key
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
index 2d9fa1b..86f4414 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.onehanded;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
@@ -57,7 +57,7 @@
     }
 
     @Test
-    public void testGetAnimator_withSameBounds_returnTranslateAnimator() {
+    public void testGetAnimator_withSameBounds_returnAnimator() {
         final Rect originalBounds = new Rect(0, 0, TEST_BOUNDS_WIDTH, TEST_BOUNDS_HEIGHT);
         final Rect destinationBounds = originalBounds;
         destinationBounds.offset(0, 300);
@@ -65,7 +65,6 @@
                 mOneHandedAnimationController
                         .getAnimator(mMockLeash, originalBounds, destinationBounds);
 
-        assertEquals("Expected translate animation",
-                OneHandedAnimationController.ANIM_TYPE_TRANSLATE, animator.getAnimationType());
+        assertNotNull(animator);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 28dad14..07a32a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -29,11 +29,11 @@
 import static org.mockito.Mockito.when;
 
 import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.DisplayInfo;
+import android.view.Display;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaInfo;
 import android.window.IWindowContainerToken;
@@ -58,7 +58,7 @@
     static final int DISPLAY_HEIGHT = 1000;
 
     DisplayAreaInfo mDisplayAreaInfo;
-    DisplayInfo mDisplayInfo;
+    Display mDisplay;
     Handler mUpdateHandler;
     OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
     OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
@@ -80,20 +80,13 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
-                mMockDisplayController,
-                mMockAnimationController,
-                mMockSurfaceTransactionHelper);
-        mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
         mToken = new WindowContainerToken(mMockRealToken);
         mLeash = new SurfaceControl();
+        mDisplay = mContext.getDisplay();
         mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED);
         mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
-        mDisplayInfo = new DisplayInfo();
-        mDisplayInfo.logicalWidth = DISPLAY_WIDTH;
-        mDisplayInfo.logicalHeight = DISPLAY_HEIGHT;
-
         when(mMockAnimationController.getAnimator(any(), any(), any())).thenReturn(null);
+        when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn(
                 mMockSurfaceTransactionHelper);
         when(mMockSurfaceTransactionHelper.crop(any(), any(), any())).thenReturn(
@@ -106,8 +99,11 @@
         when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
         when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
         when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
-        when(mMockDisplayController.getDisplay(anyInt())).thenReturn(null);
 
+        mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
+                mMockDisplayController,
+                mMockAnimationController);
+        mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
     }
 
     @Test
@@ -135,7 +131,7 @@
         mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
         mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
 
-        assertThat(mDisplayAreaOrganizer.mDisplayAreaInfo).isEqualTo(newDisplayAreaInfo);
+        assertThat(mDisplayAreaOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo)).isTrue();
     }
 
     @Test
@@ -154,13 +150,7 @@
     public void testResetImmediately() {
         // To prevent mNativeObject of Surface is null in the test flow
         when(mMockLeash.isValid()).thenReturn(false);
-        final Rect newBounds = new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
-        final DisplayAreaInfo newDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
-                FEATURE_ONE_HANDED);
-        newDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE;
-
-        mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mMockLeash);
-        mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
+        mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), Surface.ROTATION_90);
 
         assertThat(mUpdateHandler.hasMessages(
                 OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index f4fc7ea..7d4700f5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -28,6 +28,7 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 
@@ -45,6 +46,7 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedManagerImplTest extends OneHandedTestCase {
+    Display mDisplay;
     OneHandedManagerImpl mOneHandedManagerImpl;
     OneHandedTimeoutHandler mTimeoutHandler;
 
@@ -53,8 +55,6 @@
     @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
     @Mock
-    OneHandedSurfaceTransactionHelper mMockSurfaceTransactionHelper;
-    @Mock
     OneHandedTouchHandler mMockTouchHandler;
     @Mock
     OneHandedGestureHandler mMockGestureHandler;
@@ -64,6 +64,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mDisplay = mContext.getDisplay();
         mOneHandedManagerImpl = new OneHandedManagerImpl(getContext(),
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
@@ -72,10 +73,10 @@
                 mMockSysUiState);
         mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
 
+        when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
     }
 
-
     @Test
     public void testDefaultShouldNotInOneHanded() {
         final OneHandedSurfaceTransactionHelper transactionHelper =
@@ -83,8 +84,7 @@
         final OneHandedAnimationController animationController = new OneHandedAnimationController(
                 mContext, transactionHelper);
         OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
-                mContext, mMockDisplayController, animationController,
-                mMockSurfaceTransactionHelper);
+                mContext, mMockDisplayController, animationController);
 
         assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
index 3155e57..17b2e32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.pip.phone;
 
+import static android.view.MotionEvent.ACTION_BUTTON_PRESS;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
@@ -49,13 +50,17 @@
 
     private PipTouchState mTouchState;
     private CountDownLatch mDoubleTapCallbackTriggeredLatch;
+    private CountDownLatch mHoverExitCallbackTriggeredLatch;
 
     @Before
     public void setUp() throws Exception {
         mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1);
+        mHoverExitCallbackTriggeredLatch = new CountDownLatch(1);
         mTouchState = new PipTouchState(ViewConfiguration.get(getContext()),
                 Handler.createAsync(Looper.myLooper()), () -> {
             mDoubleTapCallbackTriggeredLatch.countDown();
+        }, () -> {
+            mHoverExitCallbackTriggeredLatch.countDown();
         });
         assertFalse(mTouchState.isDoubleTap());
         assertFalse(mTouchState.isWaitingForDoubleTap());
@@ -120,6 +125,35 @@
         assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1);
     }
 
+    @Test
+    public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception {
+        mTouchState.scheduleHoverExitTimeoutCallback();
+
+        // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+        Thread.sleep(50);
+        TestableLooper.get(this).processAllMessages();
+        assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0);
+    }
+
+    @Test
+    public void testHoverExitTimeout_timeoutCallbackNotCalled() throws Exception {
+        mTouchState.scheduleHoverExitTimeoutCallback();
+        TestableLooper.get(this).processAllMessages();
+        assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+    }
+
+    @Test
+    public void testHoverExitTimeout_timeoutCallbackNotCalled_ifButtonPress() throws Exception {
+        mTouchState.scheduleHoverExitTimeoutCallback();
+        mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(),
+                0, 0));
+
+        // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+        Thread.sleep(50);
+        TestableLooper.get(this).processAllMessages();
+        assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+    }
+
     private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) {
         return MotionEvent.obtain(0, eventTime, action, x, y, 0);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
index ca6c16f..8c37cf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -41,6 +42,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
@@ -63,6 +65,7 @@
     @Mock private IActivityManager mActivityManager;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
     @Mock private ConfigurationController mConfigurationController;
+    @Mock private KeyguardViewMediator mKeyguardViewMediator;
     @Mock private KeyguardBypassController mKeyguardBypassController;
     @Mock private SysuiColorExtractor mColorExtractor;
     @Mock ColorExtractor.GradientColors mGradientColors;
@@ -79,8 +82,8 @@
 
         mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
                 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
-                mConfigurationController, mKeyguardBypassController, mColorExtractor,
-                mDumpManager);
+                mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+                mColorExtractor, mDumpManager);
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
 
         mNotificationShadeWindowController.attach();
@@ -120,6 +123,17 @@
     }
 
     @Test
+    public void attach_visibleWithWallpaper() {
+        clearInvocations(mWindowManager);
+        when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true);
+        mNotificationShadeWindowController.attach();
+
+        verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
+        verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+        assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue();
+    }
+
+    @Test
     public void setBackgroundBlurRadius_expandedWithBlurs() {
         mNotificationShadeWindowController.setBackgroundBlurRadius(10);
         verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 74df113..e10bab4 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -42,6 +42,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.system.Os;
 import android.util.Log;
 
@@ -224,9 +225,19 @@
 
     }
 
+    private boolean isAdbOverNetwork() {
+        // If adb TCP port opened, this test may running by adb over network.
+        return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
+                || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
+    }
+
     @Test
     public void testPhysicalEthernet() throws Exception {
         assumeTrue(mEm.isAvailable());
+        // Do not run this test if adb is over network and ethernet is connected.
+        // It is likely the adb run over ethernet, the adb would break when ethernet is switching
+        // from client mode to server mode. See b/160389275.
+        assumeFalse(isAdbOverNetwork());
 
         // Get an interface to use.
         final String iface = mTetheredInterfaceRequester.getInterface();
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index ba61d1c0..7c7e742 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,12 @@
 # Bug component: 656484
 
+aabhinav@google.com
 alsutton@google.com
 bryanmawhinney@google.com
+jstemmer@google.com
 nathch@google.com
+niagra@google.com
+niamhfw@google.com
+philippov@google.com
 rthakohov@google.com
 tobiast@google.com
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7334c86..18fb7a3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1808,7 +1808,7 @@
         // different
         mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
         mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
-                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
         mContext = context;
         mResolver = mContext.getContentResolver();
         mCallbacks = new Callbacks(FgThread.get().getLooper());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 90f87ce..ce66b2c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3318,6 +3318,9 @@
     @Override
     public boolean setProcessMemoryTrimLevel(String process, int userId, int level)
             throws RemoteException {
+        if (!isCallerShell()) {
+            throw new SecurityException("Only shell can call it");
+        }
         synchronized (this) {
             final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel");
             if (app == null) {
@@ -6356,6 +6359,9 @@
 
     int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
             int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
+        if (mInternal.isPendingTopUid(uid)) {
+            return ActivityManager.APP_START_MODE_NORMAL;
+        }
         UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
         if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
                 + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 04089e3..5a0ea75 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -721,7 +721,7 @@
                 SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
         boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false);
         boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean(
-                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
         if (!fuseEnabled && voldAppDataIsolationEnabled) {
             Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled");
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 466465e..68cc884 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -47,12 +47,17 @@
     private final VibrationEffect mSuccessVibrationEffect;
     private final VibrationEffect mErrorVibrationEffect;
 
-    AcquisitionClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
+    /**
+     * Stops the HAL operation specific to the ClientMonitor subclass.
+     */
+    protected abstract void stopHalOperation();
+
+    AcquisitionClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int cookie, int sensorId, int statsModality,
             int statsAction, int statsClient) {
-        super(context, token, listener, userId, owner, cookie, sensorId, statsModality, statsAction,
-                statsClient);
+        super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality,
+                statsAction, statsClient);
         mPowerManager = context.getSystemService(PowerManager.class);
         mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
         mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
@@ -78,7 +83,7 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to invoke sendError", e);
         }
-        mFinishCallback.onClientFinished(this);
+        mFinishCallback.onClientFinished(this, false /* success */);
     }
 
     /**
@@ -110,7 +115,7 @@
             }
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to invoke sendAcquired", e);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index a9cefba..df836d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -52,12 +52,13 @@
 
     protected boolean mAuthAttempted;
 
-    public AuthenticationClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
-            boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
-            int sensorId, boolean isStrongBiometric, int statsModality, int statsClient,
-            @NonNull TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker) {
-        super(context, token, listener, targetUserId, owner, cookie, sensorId,
+    public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+            int targetUserId, long operationId, boolean restricted, @NonNull String owner,
+            int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
+            int statsModality, int statsClient, @NonNull TaskStackListener taskStackListener,
+            @NonNull LockoutTracker lockoutTracker) {
+        super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
                 statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
         mIsStrongBiometric = isStrongBiometric;
         mOperationId = operationId;
@@ -179,7 +180,7 @@
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to notify listener, finishing", e);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 
@@ -187,8 +188,8 @@
      * Start authentication
      */
     @Override
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         final @LockoutTracker.LockoutMode int lockoutMode =
                 mLockoutTracker.getLockoutModeForUser(getTargetUserId());
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
index a784b4b..9f5abd8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
@@ -34,17 +34,12 @@
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricService;
-import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.Binder;
-import android.os.Bundle;
-import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IHwBinder;
-import android.os.IRemoteCallback;
 import android.os.Looper;
-import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -82,11 +77,9 @@
     private final Context mContext;
     private final String mKeyguardPackage;
     protected final IActivityTaskManager mActivityTaskManager;
-    private final PowerManager mPowerManager;
     protected final BiometricTaskStackListener mTaskStackListener =
             new BiometricTaskStackListener();
     private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
-    private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
 
     protected final IStatusBarService mStatusBarService;
     protected final Map<Integer, Long> mAuthenticatorIds =
@@ -109,21 +102,14 @@
         }
     };
 
-    protected final ClientMonitor.FinishCallback mClientFinishCallback = clientMonitor -> {
-        if (clientMonitor instanceof RemovalConsumer) {
-            // When the last biometric of a group is removed, update the authenticator id.
-            // Note that 1) multiple ClientMonitors may be cause onRemoved (e.g. internal cleanup),
-            // and 2) updateActiveGroup updates/relies on global state, so there's no good way to
-            // compartmentalize this yet.
-            final int userId = clientMonitor.getTargetUserId();
-            if (!hasEnrolledBiometrics(userId)) {
-                Slog.d(getTag(), "Last biometric removed for user: " + userId
-                        + ", updating active group");
-                updateActiveGroup(userId);
-            }
-        }
-
+    protected final ClientMonitor.FinishCallback mClientFinishCallback =
+            (clientMonitor, success) -> {
         removeClient(clientMonitor);
+        // When enrollment finishes, update this group's authenticator id, as the HAL has
+        // already generated a new authenticator id when the new biometric is enrolled.
+        if (clientMonitor instanceof EnrollClient) {
+            updateActiveGroup(clientMonitor.getTargetUserId());
+        }
     };
 
     private IBiometricService mBiometricService;
@@ -263,65 +249,6 @@
         }
     }
 
-
-
-    private final class LockoutResetMonitor implements IBinder.DeathRecipient {
-        private static final long WAKELOCK_TIMEOUT_MS = 2000;
-        private final IBiometricServiceLockoutResetCallback mCallback;
-        private final PowerManager.WakeLock mWakeLock;
-
-        public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
-            mCallback = callback;
-            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                    "lockout reset callback");
-            try {
-                mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
-            } catch (RemoteException e) {
-                Slog.w(getTag(), "caught remote exception in linkToDeath", e);
-            }
-        }
-
-        public void sendLockoutReset() {
-            if (mCallback != null) {
-                try {
-                    mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
-                    mCallback.onLockoutReset(new IRemoteCallback.Stub() {
-                        @Override
-                        public void sendResult(Bundle data) throws RemoteException {
-                            releaseWakelock();
-                        }
-                    });
-                } catch (DeadObjectException e) {
-                    Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
-                    mHandler.post(mRemoveCallbackRunnable);
-                } catch (RemoteException e) {
-                    Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
-                    releaseWakelock();
-                }
-            }
-        }
-
-        private final Runnable mRemoveCallbackRunnable = new Runnable() {
-            @Override
-            public void run() {
-                releaseWakelock();
-                removeLockoutResetCallback(LockoutResetMonitor.this);
-            }
-        };
-
-        @Override
-        public void binderDied() {
-            Slog.e(getTag(), "Lockout reset callback binder died");
-            mHandler.post(mRemoveCallbackRunnable);
-        }
-
-        private void releaseWakelock() {
-            if (mWakeLock.isHeld()) {
-                mWakeLock.release();
-            }
-        }
-    }
-
     /**
      * Initializes the system service.
      * <p>
@@ -341,7 +268,6 @@
         mKeyguardPackage = keyguardComponent != null ? keyguardComponent.getPackageName() : null;
         mAppOps = context.getSystemService(AppOpsManager.class);
         mActivityTaskManager = ActivityTaskManager.getService();
-        mPowerManager = mContext.getSystemService(PowerManager.class);
         mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId());
     }
 
@@ -452,15 +378,7 @@
         }
 
         final EnrollClient enrollClient = (EnrollClient) client;
-
-        if (enrollClient.onEnrollResult(identifier, remaining)) {
-            removeClient(enrollClient);
-            // When enrollment finishes, update this group's authenticator id, as the HAL has
-            // already generated a new authenticator id when the new biometric is enrolled.
-            if (identifier instanceof Fingerprint) {
-                updateActiveGroup(((Fingerprint)identifier).getGroupId());
-            }
-        }
+        enrollClient.onEnrollResult(identifier, remaining);
     }
 
     protected void handleError(int error, int vendorCode) {
@@ -630,7 +548,8 @@
         });
     }
 
-    protected void cleanupInternal(InternalCleanupClient<T> client) {
+    protected void cleanupInternal(
+            InternalCleanupClient<? extends BiometricAuthenticator.Identifier, T> client) {
         mHandler.post(() -> {
             if (DEBUG) {
                 Slog.v(getTag(), "Cleaning up templates for user("
@@ -647,19 +566,6 @@
         startClient(client, true /* initiatedByClient */);
     }
 
-    protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
-        if (callback == null) {
-            Slog.w(getTag(), "Null LockoutResetCallback");
-            return;
-        }
-        mHandler.post(() -> {
-           final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
-           if (!mLockoutMonitors.contains(monitor)) {
-               mLockoutMonitors.add(monitor);
-           }
-        });
-    }
-
     /**
      * Helper methods.
      */
@@ -816,7 +722,7 @@
             return;
         }
 
-        final T daemon = getDaemon();
+        final T daemon = mCurrentClient.getFreshDaemon();
         if (daemon == null) {
             Slog.e(getTag(), "Daemon null, unable to start: "
                     + mCurrentClient.getClass().getSimpleName());
@@ -825,7 +731,7 @@
             return;
         }
 
-        mCurrentClient.start(daemon, mClientFinishCallback);
+        mCurrentClient.start(mClientFinishCallback);
         notifyClientActiveCallbacks(true);
     }
 
@@ -931,12 +837,6 @@
         doTemplateCleanupForUser(userId);
     }
 
-    protected void notifyLockoutResetMonitors() {
-        for (int i = 0; i < mLockoutMonitors.size(); i++) {
-            mLockoutMonitors.get(i).sendLockoutReset();
-        }
-    }
-
     private void listenForUserSwitches() {
         try {
             ActivityManager.getService().registerUserSwitchObserver(
@@ -951,8 +851,4 @@
             Slog.w(getTag(), "Failed to listen for user switching event" ,e);
         }
     }
-
-    private void removeLockoutResetCallback(LockoutResetMonitor monitor) {
-        mLockoutMonitors.remove(monitor);
-    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 5a2d63d..3f301cc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -47,11 +47,23 @@
          * implementation.
          *
          * @param clientMonitor Reference of the ClientMonitor that finished.
+         * @param success True if the operation completed successfully.
          */
-        void onClientFinished(ClientMonitor clientMonitor);
+        void onClientFinished(ClientMonitor clientMonitor, boolean success);
+    }
+
+    /**
+     * Interface that allows ClientMonitor subclasses to retrieve a fresh instance to the HAL.
+     */
+    public interface LazyDaemon<T> {
+        /**
+         * @return A fresh instance to the biometric HAL
+         */
+        T getDaemon();
     }
 
     @NonNull private final Context mContext;
+    @NonNull protected final LazyDaemon<T> mLazyDaemon;
     private final int mTargetUserId;
     @NonNull private final String mOwner;
     private final int mSensorId; // sensorId as configured by the framework
@@ -63,11 +75,11 @@
     private final int mCookie;
     boolean mAlreadyDone;
 
-    @NonNull protected T mDaemon;
     @NonNull protected FinishCallback mFinishCallback;
 
     /**
      * @param context    system_server context
+     * @param lazyDaemon pointer for lazy retrieval of the HAL
      * @param token      a unique token for the client
      * @param listener   recipient of related events (e.g. authentication)
      * @param userId     target user id for operation
@@ -78,12 +90,13 @@
      * @param statsAction   One of {@link BiometricsProtoEnums} ACTION_* constants
      * @param statsClient   One of {@link BiometricsProtoEnums} CLIENT_* constants
      */
-    public ClientMonitor(@NonNull Context context, @Nullable IBinder token,
-            @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
-            int cookie, int sensorId, int statsModality, int statsAction,
+    public ClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
+            @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
             int statsClient) {
         super(statsModality, statsAction, statsClient);
         mContext = context;
+        mLazyDaemon = lazyDaemon;
         mToken = token;
         mListener = listener;
         mTargetUserId = userId;
@@ -107,18 +120,16 @@
     /**
      * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
      * If such a problem is detected, the scheduler will not invoke
-     * {@link #start(Object, FinishCallback)}.
+     * {@link #start(FinishCallback)}.
      */
     public abstract void unableToStart();
 
     /**
      * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
      * keeping is complete.
-     * @param daemon reference to the HAL
      * @param finishCallback invoked when the operation is complete (succeeds, fails, etc)
      */
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        mDaemon = daemon;
+    public void start(@NonNull FinishCallback finishCallback) {
         mFinishCallback = finishCallback;
     }
 
@@ -127,11 +138,6 @@
      */
     protected abstract void startHalOperation();
 
-    /**
-     * Stops the HAL operation specific to the ClientMonitor subclass.
-     */
-    protected abstract void stopHalOperation();
-
     public boolean isAlreadyDone() {
         return mAlreadyDone;
     }
@@ -190,4 +196,8 @@
     public final int getSensorId() {
         return mSensorId;
     }
+
+    public final T getFreshDaemon() {
+        return mLazyDaemon.getDaemon();
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 6637ede..a3d9677 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -41,54 +41,46 @@
     private long mEnrollmentStartTimeMs;
     private boolean mAlreadyCancelled;
 
-    public EnrollClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
+    public EnrollClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
             int timeoutSec, int statsModality, int sensorId,
             boolean shouldVibrate) {
-        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality,
-                BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN);
+        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+                statsModality, BiometricsProtoEnums.ACTION_ENROLL,
+                BiometricsProtoEnums.CLIENT_UNKNOWN);
         mBiometricUtils = utils;
         mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
         mTimeoutSec = timeoutSec;
         mShouldVibrate = shouldVibrate;
     }
 
-    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
-            int remaining) {
-        if (remaining == 0) {
-            mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
-            logOnEnrolled(getTargetUserId(),
-                    System.currentTimeMillis() - mEnrollmentStartTimeMs,
-                    true /* enrollSuccessful */);
-        }
-        notifyUserActivity();
-        return sendEnrollResult(identifier, remaining);
-    }
-
-    /*
-     * @return true if we're done.
-     */
-    private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
+    public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
         if (mShouldVibrate) {
             vibrateSuccess();
         }
 
+        final ClientMonitorCallbackConverter listener = getListener();
         try {
-            final ClientMonitorCallbackConverter listener = getListener();
             if (listener != null) {
                 listener.onEnrollResult(identifier, remaining);
             }
-            return remaining == 0;
         } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to notify EnrollResult:", e);
-            return true;
+            Slog.e(TAG, "Remote exception", e);
         }
+
+        if (remaining == 0) {
+            mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
+            logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+                    true /* enrollSuccessful */);
+            mFinishCallback.onClientFinished(this, true /* success */);
+        }
+        notifyUserActivity();
     }
 
     @Override
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         mEnrollmentStartTimeMs = System.currentTimeMillis();
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 12584cf..dad5cad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -29,9 +29,10 @@
 
     protected long mChallenge;
 
-    public GenerateChallengeClient(Context context, IBinder token,
-            ClientMonitorCallbackConverter listener, String owner, int sensorId) {
-        super(context, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
+    public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull String owner, int sensorId) {
+        super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
                 BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
                 BiometricsProtoEnums.CLIENT_UNKNOWN);
     }
@@ -46,20 +47,16 @@
     }
 
     @Override
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         startHalOperation();
         try {
             getListener().onChallengeGenerated(mChallenge);
+            mFinishCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
-        mFinishCallback.onClientFinished(this);
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        // Not supported for GenerateChallenge
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index f3ade65..6d7b0fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -27,6 +27,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Wraps {@link InternalEnumerateClient} and {@link RemovalClient}. Keeps track of all the
@@ -37,8 +38,8 @@
  * 2) The HAL and Framework are not in sync, and
  * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/
  */
-public abstract class InternalCleanupClient<T> extends ClientMonitor<T>
-        implements EnumerateConsumer, RemovalConsumer {
+public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T>
+        extends ClientMonitor<T> implements EnumerateConsumer, RemovalConsumer {
 
     private static final String TAG = "Biometrics/InternalCleanupClient";
 
@@ -57,10 +58,11 @@
 
     private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
     private final BiometricUtils mBiometricUtils;
-    private final List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
+    private final Map<Integer, Long> mAuthenticatorIds;
+    private final List<S> mEnrolledList;
     private ClientMonitor<T> mCurrentTask;
 
-    private final FinishCallback mEnumerateFinishCallback = clientMonitor -> {
+    private final FinishCallback mEnumerateFinishCallback = (clientMonitor, success) -> {
         final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
                 ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
 
@@ -75,45 +77,46 @@
         if (mUnknownHALTemplates.isEmpty()) {
             // No unknown HAL templates. Unknown framework templates are already cleaned up in
             // InternalEnumerateClient. Finish this client.
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, success);
         } else {
             startCleanupUnknownHalTemplates();
         }
     };
 
-    private final FinishCallback mRemoveFinishCallback = clientMonitor -> {
-        mFinishCallback.onClientFinished(this);
+    private final FinishCallback mRemoveFinishCallback = (clientMonitor, success) -> {
+        mFinishCallback.onClientFinished(this, success);
     };
 
-    protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, IBinder token,
-            int userId, String owner,
-            List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils,
-            int sensorId);
+    protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
+            LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner,
+            List<S> enrolledList, BiometricUtils utils, int sensorId);
 
-    protected abstract RemovalClient<T> getRemovalClient(Context context, IBinder token,
-            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId);
+    protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon,
+            IBinder token, int biometricId, int userId, String owner, BiometricUtils utils,
+            int sensorId, Map<Integer, Long> authenticatorIds);
 
-    protected InternalCleanupClient(@NonNull Context context, int userId,
-            @NonNull String owner, int sensorId, int statsModality,
-            @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            @NonNull BiometricUtils utils) {
-        super(context, null /* token */, null /* ClientMonitorCallbackConverter */,
+    protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            int userId, @NonNull String owner, int sensorId, int statsModality,
+            @NonNull List<S> enrolledList, @NonNull BiometricUtils utils,
+            @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
                 userId, owner, 0 /* cookie */, sensorId, statsModality,
                 BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
         mBiometricUtils = utils;
+        mAuthenticatorIds = authenticatorIds;
         mEnrolledList = enrolledList;
     }
 
     private void startCleanupUnknownHalTemplates() {
         UserTemplate template = mUnknownHALTemplates.get(0);
         mUnknownHALTemplates.remove(template);
-        mCurrentTask = getRemovalClient(getContext(), getToken(),
+        mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
                 template.mIdentifier.getBiometricId(), template.mUserId,
-                getContext().getPackageName(), mBiometricUtils, getSensorId());
+                getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                 mStatsModality,
                 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
-        mCurrentTask.start(mDaemon, mRemoveFinishCallback);
+        mCurrentTask.start(mRemoveFinishCallback);
     }
 
     @Override
@@ -122,13 +125,13 @@
     }
 
     @Override
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         // Start enumeration. Removal will start if necessary, when enumeration is completed.
-        mCurrentTask = getEnumerateClient(getContext(), getToken(), getTargetUserId(),
+        mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
                 getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
-        mCurrentTask.start(daemon, mEnumerateFinishCallback);
+        mCurrentTask.start(mEnumerateFinishCallback);
     }
 
     @Override
@@ -138,11 +141,6 @@
     }
 
     @Override
-    protected void stopHalOperation() {
-        // Internal cleanup's cannot be stopped.
-    }
-
-    @Override
     public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
         if (!(mCurrentTask instanceof RemovalClient)) {
             Slog.e(TAG, "onRemoved received during client: "
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 9ce271c..3f73cd5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -43,13 +43,13 @@
     // List of templates to remove from the HAL
     private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
 
-    protected InternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
-            @NonNull String owner,
+    protected InternalEnumerateClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @NonNull IBinder token, int userId, @NonNull String owner,
             @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
             @NonNull BiometricUtils utils, int sensorId, int statsModality) {
         // Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
-        super(context, token, null /* ClientMonitorCallbackConverter */, userId, owner,
+        super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
                 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
                 BiometricsProtoEnums.CLIENT_UNKNOWN);
         mEnrolledList = enrolledList;
@@ -62,7 +62,7 @@
         handleEnumeratedTemplate(identifier);
         if (remaining == 0) {
             doTemplateCleanup();
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, true /* success */);
         }
     }
 
@@ -72,8 +72,8 @@
     }
 
     @Override
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         // The biometric template ids will be removed when we get confirmation from the HAL
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
new file mode 100644
index 0000000..9bb5c6e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Allows clients (such as keyguard) to register for notifications on when biometric lockout
+ * ends.
+ */
+public class LockoutResetTracker implements IBinder.DeathRecipient {
+
+    private static final String TAG = "LockoutResetTracker";
+
+    private final Context mContext;
+    private final ArrayList<ClientCallback> mClientCallbacks;
+
+    private static class ClientCallback {
+        private static final long WAKELOCK_TIMEOUT_MS = 2000;
+
+        private final String mOpPackageName;
+        private final IBiometricServiceLockoutResetCallback mCallback;
+        private final PowerManager.WakeLock mWakeLock;
+
+        ClientCallback(Context context, IBiometricServiceLockoutResetCallback callback,
+                String opPackageName) {
+            final PowerManager pm = context.getSystemService(PowerManager.class);
+            mOpPackageName = opPackageName;
+            mCallback = callback;
+            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "LockoutResetMonitor:SendLockoutReset");
+        }
+
+        void sendLockoutReset() {
+            if (mCallback != null) {
+                try {
+                    mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+                    mCallback.onLockoutReset(new IRemoteCallback.Stub() {
+                        @Override
+                        public void sendResult(Bundle data) {
+                            releaseWakelock();
+                        }
+                    });
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+                    releaseWakelock();
+                }
+            }
+        }
+
+        private void releaseWakelock() {
+            if (mWakeLock.isHeld()) {
+                mWakeLock.release();
+            }
+        }
+    }
+
+    public LockoutResetTracker(Context context) {
+        mContext = context;
+        mClientCallbacks = new ArrayList<>();
+    }
+
+    public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) {
+        if (callback == null) {
+            Slog.w(TAG, "Callback from : " + opPackageName + " is null");
+            return;
+        }
+
+        mClientCallbacks.add(new ClientCallback(mContext, callback, opPackageName));
+        try {
+            callback.asBinder().linkToDeath(this, 0 /* flags */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to link to death", e);
+        }
+    }
+
+    @Override
+    public void binderDied() {
+        // Do nothing, handled below
+    }
+
+    @Override
+    public void binderDied(IBinder who) {
+        Slog.e(TAG, "Callback binder died: " + who);
+        for (ClientCallback callback : mClientCallbacks) {
+            if (callback.mCallback.asBinder().equals(who)) {
+                Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName);
+                callback.releaseWakelock();
+                mClientCallbacks.remove(callback);
+            }
+        }
+    }
+
+    public void notifyLockoutResetCallbacks() {
+        for (ClientCallback callback : mClientCallbacks) {
+            callback.sendLockoutReset();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index b734516..1c49bcdb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -24,6 +24,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.util.Map;
+
 /**
  * A class to keep track of the remove state for a given client.
  */
@@ -33,14 +35,18 @@
 
     protected final int mBiometricId;
     private final BiometricUtils mBiometricUtils;
+    private final Map<Integer, Long> mAuthenticatorIds;
 
-    public RemovalClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId, int statsModality) {
-        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality,
-                BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN);
+    public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+            int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
+        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+                statsModality, BiometricsProtoEnums.ACTION_REMOVE,
+                BiometricsProtoEnums.CLIENT_UNKNOWN);
         mBiometricId = biometricId;
         mBiometricUtils = utils;
+        mAuthenticatorIds = authenticatorIds;
     }
 
     @Override
@@ -49,8 +55,8 @@
     }
 
     @Override
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         // The biometric template ids will be removed when we get confirmation from the HAL
         startHalOperation();
@@ -72,7 +78,14 @@
         }
 
         if (remaining == 0) {
-            mFinishCallback.onClientFinished(this);
+            if (mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty()) {
+                Slog.d(TAG, "Last biometric removed for user: " + getTargetUserId());
+                // When the last biometric of a group is removed, update the authenticator id.
+                // Note that multiple ClientMonitors may be cause onRemoved (e.g. internal
+                // cleanup).
+                mAuthenticatorIds.put(getTargetUserId(), 0L);
+            }
+            mFinishCallback.onClientFinished(this, true /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index e00396b..b78ee49 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -23,10 +23,11 @@
 
 public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> {
 
-    public RevokeChallengeClient(Context context, IBinder token, String owner, int sensorId) {
-        super(context, token, null /* listener */, 0 /* userId */, owner, 0 /* cookie */, sensorId,
-                BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
-                BiometricsProtoEnums.CLIENT_UNKNOWN);
+    public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+            @NonNull IBinder token, @NonNull String owner, int sensorId) {
+        super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner,
+                0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
     }
 
     @Override
@@ -35,15 +36,10 @@
     }
 
     @Override
-    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         startHalOperation();
-        mFinishCallback.onClientFinished(this);
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        // Not supported for RevokeChallenge
+        mFinishCallback.onClientFinished(this, true /* success */);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
index a54357e..118cadc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
@@ -62,13 +62,14 @@
 
     private int mLastAcquire;
 
-    FaceAuthenticationClient(@NonNull Context context, @NonNull IBinder token,
+    FaceAuthenticationClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
             boolean isStrongBiometric, int statsClient,
             @NonNull TaskStackListener taskStackListener,
             @NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats) {
-        super(context, token, listener, targetUserId, operationId, restricted,
+        super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
                 owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
                 BiometricsProtoEnums.MODALITY_FACE, statsClient, taskStackListener,
                 lockoutTracker);
@@ -89,22 +90,22 @@
     @Override
     protected void startHalOperation() {
         try {
-            mDaemon.authenticate(mOperationId);
+            getFreshDaemon().authenticate(mOperationId);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting auth", e);
             onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 
     @Override
     protected void stopHalOperation() {
         try {
-            mDaemon.cancel();
+            getFreshDaemon().cancel();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting cancel", e);
             onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 
@@ -132,7 +133,7 @@
         // 1) Authenticated == true
         // 2) Error occurred
         // 3) Authenticated == false
-        mFinishCallback.onClientFinished(this);
+        mFinishCallback.onClientFinished(this, true /* success */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index b63b39e..4f9f46a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -51,13 +51,14 @@
     @NonNull private final int[] mEnrollIgnoreList;
     @NonNull private final int[] mEnrollIgnoreListVendor;
 
-    FaceEnrollClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
+    FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
             @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle,
             int sensorId) {
-        super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec,
-                BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */);
+        super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+                timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
+                false /* shouldVibrate */);
         mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
         mSurfaceHandle = surfaceHandle;
         mEnrollIgnoreList = getContext().getResources()
@@ -89,36 +90,36 @@
         }
 
         android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
-                android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(mDaemon);
+                android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(getFreshDaemon());
         try {
             final int status;
             if (daemon11 != null) {
                 status = daemon11.enroll_1_1(token, mTimeoutSec, disabledFeatures, mSurfaceHandle);
             } else if (mSurfaceHandle == null) {
-                status = mDaemon.enroll(token, mTimeoutSec, disabledFeatures);
+                status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures);
             } else {
                 Slog.e(TAG, "enroll(): surface is only supported in @1.1 HAL");
                 status = BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
             }
             if (status != Status.OK) {
                 onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
-                mFinishCallback.onClientFinished(this);
+                mFinishCallback.onClientFinished(this, false /* success */);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enroll", e);
             onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 
     @Override
     protected void stopHalOperation() {
         try {
-            mDaemon.cancel();
+            getFreshDaemon().cancel();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting cancel", e);
             onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
index 72188a5..67f2712 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
@@ -36,15 +36,16 @@
     private static final String TAG = "FaceGenerateChallengeClient";
     private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
 
-    FaceGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token,
+    FaceGenerateChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
-        super(context, token, listener, owner, sensorId);
+        super(context, lazyDaemon, token, listener, owner, sensorId);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mChallenge = mDaemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+            mChallenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
         } catch (RemoteException e) {
             Slog.e(TAG, "generateChallenge failed", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
index 227d817..ce57cb7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
@@ -40,10 +40,10 @@
     private final int mFeature;
     private final int mFaceId;
 
-    FaceGetFeatureClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
-            int sensorId, int feature, int faceId) {
-        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId,
+    FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull String owner, int sensorId, int feature, int faceId) {
+        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
                 BiometricsProtoEnums.CLIENT_UNKNOWN);
         mFeature = feature;
@@ -61,24 +61,20 @@
     }
 
     @Override
-    public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
         startHalOperation();
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            final OptionalBool result = mDaemon.getFeature(mFeature, mFaceId);
+            final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
             getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value);
+            mFinishCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to getFeature", e);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
-        mFinishCallback.onClientFinished(this);
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        // Not supported for GetFeature
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
index 388baa2..93f35f4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
 import android.os.IBinder;
 
 import com.android.server.biometrics.sensors.BiometricUtils;
@@ -29,36 +29,40 @@
 import com.android.server.biometrics.sensors.RemovalClient;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Face-specific internal cleanup client supporting the
  * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
  * HIDL interfaces.
  */
-class FaceInternalCleanupClient extends InternalCleanupClient<IBiometricsFace> {
+class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsFace> {
 
-    FaceInternalCleanupClient(@NonNull Context context, int userId, @NonNull String owner,
-            int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            @NonNull BiometricUtils utils) {
-        super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, enrolledList,
-                utils);
+    FaceInternalCleanupClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+            int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+            @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+                enrolledList, utils, authenticatorIds);
     }
 
     @Override
     protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
-            IBinder token, int userId, String owner,
-            List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            BiometricUtils utils, int sensorId) {
-        return new FaceInternalEnumerateClient(context, token, userId, owner, enrolledList, utils,
-                sensorId);
+            LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
+            List<Face> enrolledList, BiometricUtils utils, int sensorId) {
+        return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+                enrolledList, utils, sensorId);
     }
 
     @Override
-    protected RemovalClient<IBiometricsFace> getRemovalClient(Context context, IBinder token,
-            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) {
+    protected RemovalClient<IBiometricsFace> getRemovalClient(Context context,
+            LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token,
+            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+            Map<Integer, Long> authenticatorIds) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
-        return new FaceRemovalClient(context, token, null /* ClientMonitorCallbackConverter */,
-                biometricId, userId, owner, utils, sensorId);
+        return new FaceRemovalClient(context, lazyDaemon, token,
+                null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+                sensorId, authenticatorIds);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
index c6749c5..f25242e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -38,31 +38,21 @@
 class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFace> {
     private static final String TAG = "FaceInternalEnumerateClient";
 
-    FaceInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
-            @NonNull String owner,
-            @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            @NonNull BiometricUtils utils, int sensorId) {
-        super(context, token, userId, owner, enrolledList, utils, sensorId,
+    FaceInternalEnumerateClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
+            @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+            int sensorId) {
+        super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
                 BiometricsProtoEnums.MODALITY_FACE);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mDaemon.enumerate();
+            getFreshDaemon().enumerate();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enumerate", e);
-            mFinishCallback.onClientFinished(this);
-        }
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        try {
-            mDaemon.cancel();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Remote exception when requesting cancel", e);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
index b0ee981..00d5f50 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -28,6 +28,8 @@
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.RemovalClient;
 
+import java.util.Map;
+
 /**
  * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
  * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
@@ -35,30 +37,21 @@
 class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
     private static final String TAG = "FaceRemovalClient";
 
-    FaceRemovalClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) {
-        super(context, token, listener, biometricId, userId, owner, utils, sensorId,
-                BiometricsProtoEnums.MODALITY_FACE);
+    FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+            int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+            int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+                authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mDaemon.remove(mBiometricId);
+            getFreshDaemon().remove(mBiometricId);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting remove", e);
-            mFinishCallback.onClientFinished(this);
-        }
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        try {
-            mDaemon.cancel();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Remote exception when requesting cancel", e);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
index 441cb14..69070da 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
@@ -37,10 +37,11 @@
 
     private final ArrayList<Byte> mHardwareAuthToken;
 
-    FaceResetLockoutClient(@NonNull Context context, int userId, String owner, int sensorId,
-            byte[] hardwareAuthToken) {
-        super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
-                sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+    FaceResetLockoutClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId,
+            @NonNull byte[] hardwareAuthToken) {
+        super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+                0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
                 BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
 
         mHardwareAuthToken = new ArrayList<>();
@@ -55,24 +56,19 @@
     }
 
     @Override
-    public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
-
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
         startHalOperation();
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mDaemon.resetLockout(mHardwareAuthToken);
+            getFreshDaemon().resetLockout(mHardwareAuthToken);
+            mFinishCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to reset lockout", e);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
-        mFinishCallback.onClientFinished(this);
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        // Not supported for resetLockout
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
index 992a678d..a10c573 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
@@ -33,15 +33,16 @@
 
     private static final String TAG = "FaceRevokeChallengeClient";
 
-    FaceRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token,
+    FaceRevokeChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
             @NonNull String owner, int sensorId) {
-        super(context, token, owner, sensorId);
+        super(context, lazyDaemon, token, owner, sensorId);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mDaemon.revokeChallenge();
+            getFreshDaemon().revokeChallenge();
         } catch (RemoteException e) {
             Slog.e(TAG, "revokeChallenge failed", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 050fba2..a7f8220 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -25,7 +25,6 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -60,6 +59,7 @@
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import com.android.server.biometrics.sensors.LockoutResetTracker;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.PerformanceTracker;
 import com.android.server.biometrics.sensors.RemovalClient;
@@ -97,6 +97,9 @@
     static final String NOTIFICATION_TAG = "FaceService";
     static final int NOTIFICATION_ID = 1;
 
+    private final LockoutResetTracker mLockoutResetTracker;
+    private final ClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
+
     /**
      * Receives the incoming binder calls from FaceManager.
      */
@@ -113,7 +116,7 @@
             checkPermission(MANAGE_BIOMETRIC);
 
             final GenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(),
-                    token, new ClientMonitorCallbackConverter(receiver), opPackageName,
+                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
                     getSensorId());
             generateChallengeInternal(client);
         }
@@ -122,8 +125,8 @@
         public void revokeChallenge(IBinder token, String owner) {
             checkPermission(MANAGE_BIOMETRIC);
 
-            final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(), token,
-                    owner, getSensorId());
+            final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(),
+                    mLazyDaemon, token, owner, getSensorId());
 
             // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
             if (getCurrentClient() == null) {
@@ -149,7 +152,7 @@
                         UserHandle.CURRENT);
             });
 
-            final EnrollClient client = new FaceEnrollClient(getContext(), token,
+            final EnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
                     opPackageName, getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC,
                     convertSurfaceToNativeHandle(surface), getSensorId());
@@ -180,11 +183,11 @@
             final boolean restricted = isRestricted();
             final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
                     : BiometricsProtoEnums.CLIENT_UNKNOWN;
-            final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token,
-                    new ClientMonitorCallbackConverter(receiver), userId, opId, restricted,
-                    opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
-                    isStrongBiometric(), statsClient, mTaskStackListener, mLockoutTracker,
-                    mUsageStats);
+            final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
+                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
+                    restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
+                    getSensorId(), isStrongBiometric(), statsClient, mTaskStackListener,
+                    mLockoutTracker, mUsageStats);
             authenticateInternal(client, opPackageName);
         }
 
@@ -197,11 +200,11 @@
             updateActiveGroup(userId);
 
             final boolean restricted = true; // BiometricPrompt is always restricted
-            final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token,
-                    new ClientMonitorCallbackConverter(sensorReceiver), userId, opId, restricted,
-                    opPackageName, cookie, requireConfirmation, getSensorId(), isStrongBiometric(),
-                    BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
-                    mLockoutTracker, mUsageStats);
+            final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
+                    mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
+                    opId, restricted, opPackageName, cookie, requireConfirmation, getSensorId(),
+                    isStrongBiometric(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+                    mTaskStackListener, mLockoutTracker, mUsageStats);
             authenticateInternal(client, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -238,16 +241,19 @@
                 return;
             }
 
-            final RemovalClient client = new FaceRemovalClient(getContext(), token,
+            final RemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token,
                     new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
-                    getBiometricUtils(), getSensorId());
+                    getBiometricUtils(), getSensorId(), mAuthenticatorIds);
             removeInternal(client);
         }
 
         @Override
-        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback) {
+        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+                final String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            FaceService.super.addLockoutResetCallback(callback);
+            mHandler.post(() -> {
+                mLockoutResetTracker.addCallback(callback, opPackageName);
+            });
         }
 
         @Override // Binder call
@@ -340,7 +346,8 @@
 
                 updateActiveGroup(userId);
                 final FaceResetLockoutClient client = new FaceResetLockoutClient(getContext(),
-                        userId, getContext().getOpPackageName(), getSensorId(), hardwareAuthToken);
+                        mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(),
+                        hardwareAuthToken);
                 startClient(client, true /* initiatedByClient */);
             });
         }
@@ -364,8 +371,8 @@
                 final int faceId = getFirstTemplateForUser(mCurrentUserId);
 
                 final FaceSetFeatureClient client = new FaceSetFeatureClient(getContext(),
-                        token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
-                        getSensorId(), feature, enabled, hardwareAuthToken, faceId);
+                        mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+                        opPackageName, getSensorId(), feature, enabled, hardwareAuthToken, faceId);
                 startClient(client, true /* initiatedByClient */);
             });
 
@@ -391,9 +398,9 @@
                 // TODO: Support multiple faces
                 final int faceId = getFirstTemplateForUser(mCurrentUserId);
 
-                final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(), token,
-                        new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
-                        getSensorId(), feature, faceId);
+                final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(),
+                        mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+                        opPackageName, getSensorId(), feature, faceId);
                 startClient(client, true /* initiatedByClient */);
             });
 
@@ -539,7 +546,7 @@
 
             mHandler.post(() -> {
                 if (duration == 0) {
-                    notifyLockoutResetMonitors();
+                    mLockoutResetTracker.notifyLockoutResetCallbacks();
                 }
             });
         }
@@ -547,6 +554,8 @@
 
     public FaceService(Context context) {
         super(context);
+        mLazyDaemon = FaceService.this::getFaceDaemon;
+        mLockoutResetTracker = new LockoutResetTracker(context);
         mLockoutTracker = new LockoutHalImpl();
         mUsageStats = new UsageStats(context);
         mNotificationManager = getContext().getSystemService(NotificationManager.class);
@@ -673,7 +682,7 @@
 
     @Override
     protected List<Face> getEnrolledTemplates(int userId) {
-        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+        return FaceUtils.getInstance().getBiometricsForUser(getContext(), userId);
     }
 
     @Override
@@ -693,10 +702,10 @@
 
     @Override
     protected void doTemplateCleanupForUser(int userId) {
-        final List<? extends BiometricAuthenticator.Identifier> enrolledList =
-                getEnrolledTemplates(userId);
-        final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(), userId,
-                getContext().getOpPackageName(), getSensorId(), enrolledList, getBiometricUtils());
+        final List<Face> enrolledList = getEnrolledTemplates(userId);
+        final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(),
+                mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), enrolledList,
+                getBiometricUtils(), mAuthenticatorIds);
         cleanupInternal(client);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
index 91f63e1..e7d041a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
@@ -43,11 +43,11 @@
     private final ArrayList<Byte> mHardwareAuthToken;
     private final int mFaceId;
 
-    FaceSetFeatureClient(@NonNull Context context, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
+    FaceSetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int sensorId, int feature, boolean enabled,
             byte[] hardwareAuthToken, int faceId) {
-        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId,
+        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
                 BiometricsProtoEnums.CLIENT_UNKNOWN);
         mFeature = feature;
@@ -70,25 +70,22 @@
     }
 
     @Override
-    public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
-        super.start(daemon, finishCallback);
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
 
         startHalOperation();
-        mFinishCallback.onClientFinished(this);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            final int result = mDaemon.setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
+            final int result = getFreshDaemon()
+                    .setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
             getListener().onFeatureSet(result == Status.OK, mFeature);
+            mFinishCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
-
-    @Override
-    protected void stopHalOperation() {
-        // Not supported for SetFeature
-    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
index ba89d51..b01b856 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
@@ -47,13 +47,14 @@
 
     private final LockoutFrameworkImpl mLockoutFrameworkImpl;
 
-    FingerprintAuthenticationClient(@NonNull Context context, @NonNull IBinder token,
+    FingerprintAuthenticationClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
             int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient,
             @NonNull TaskStackListener taskStackListener,
             @NonNull LockoutFrameworkImpl lockoutTracker) {
-        super(context, token, listener, targetUserId, operationId, restricted,
+        super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
                 owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
                 BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
                 lockoutTracker);
@@ -72,7 +73,7 @@
 
         if (authenticated) {
             resetFailedAttempts(getTargetUserId());
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, true /* success */);
         } else {
             final @LockoutTracker.LockoutMode int lockoutMode =
                     mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
@@ -83,7 +84,7 @@
                         ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
                         : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
                 onError(errorCode, 0 /* vendorCode */);
-                mFinishCallback.onClientFinished(this);
+                mFinishCallback.onClientFinished(this, true /* success */);
             }
         }
     }
@@ -102,24 +103,24 @@
     protected void startHalOperation() {
         try {
             // GroupId was never used. In fact, groupId is always the same as userId.
-            mDaemon.authenticate(mOperationId, getTargetUserId());
+            getFreshDaemon().authenticate(mOperationId, getTargetUserId());
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting auth", e);
             onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 
     @Override
     protected void stopHalOperation() {
         try {
-            mDaemon.cancel();
+            getFreshDaemon().cancel();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting cancel", e);
             onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
index 34681c3..1d56882 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
@@ -38,36 +38,38 @@
 
     private static final String TAG = "FingerprintEnrollClient";
 
-    FingerprintEnrollClient(@NonNull Context context, @NonNull IBinder token,
+    FingerprintEnrollClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
             int timeoutSec, int sensorId) {
-        super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec,
-                BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */);
+        super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+                timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+                true /* shouldVibrate */);
     }
 
     @Override
     protected void startHalOperation() {
         try {
             // GroupId was never used. In fact, groupId is always the same as userId.
-            mDaemon.enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
+            getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enroll", e);
             onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 
     @Override
     protected void stopHalOperation() {
         try {
-            mDaemon.cancel();
+            getFreshDaemon().cancel();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting cancel", e);
             onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
                     0 /* vendorCode */);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
index 2fa4333..8fb8c99 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
@@ -36,15 +36,16 @@
 
     private static final String TAG = "FingerprintGenerateChallengeClient";
 
-    FingerprintGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token,
+    FingerprintGenerateChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
-        super(context, token, listener, owner, sensorId);
+        super(context, lazyDaemon, token, listener, owner, sensorId);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mChallenge = mDaemon.preEnroll();
+            mChallenge = getFreshDaemon().preEnroll();
         } catch (RemoteException e) {
             Slog.e(TAG, "preEnroll failed", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
index 71e7670..1d72c2e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
@@ -21,6 +21,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
 
 import com.android.server.biometrics.sensors.BiometricUtils;
@@ -29,37 +30,43 @@
 import com.android.server.biometrics.sensors.RemovalClient;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Fingerprint-specific internal cleanup client supporting the
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintInternalCleanupClient extends InternalCleanupClient<IBiometricsFingerprint> {
+class FingerprintInternalCleanupClient
+        extends InternalCleanupClient<Fingerprint, IBiometricsFingerprint> {
 
-    FingerprintInternalCleanupClient(@NonNull Context context,int userId, @NonNull String owner,
-            int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            @NonNull BiometricUtils utils) {
-        super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
-                enrolledList, utils);
+    FingerprintInternalCleanupClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
+            @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
+            @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, userId, owner, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
     }
 
     @Override
     protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
-            Context context, IBinder token, int userId, String owner,
-            List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils,
+            Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+            int userId, String owner,
+            List<Fingerprint> enrolledList, BiometricUtils utils,
             int sensorId) {
-        return new FingerprintInternalEnumerateClient(context, token, userId, owner, enrolledList,
-                utils, sensorId);
+        return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+                enrolledList, utils, sensorId);
     }
 
     @Override
-    protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context, IBinder token,
-            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) {
+    protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context,
+            LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+            Map<Integer, Long> authenticatorIds) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
-        return new FingerprintRemovalClient(context, token,
+        return new FingerprintRemovalClient(context, lazyDaemon, token,
                 null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
-                sensorId);
+                sensorId, authenticatorIds);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
index ba412e3..240c3c5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -38,31 +38,21 @@
 class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFingerprint> {
     private static final String TAG = "FingerprintInternalEnumerateClient";
 
-    FingerprintInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
-            @NonNull String owner,
-            @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
+    FingerprintInternalEnumerateClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+            int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
             @NonNull BiometricUtils utils, int sensorId) {
-        super(context, token, userId, owner, enrolledList, utils, sensorId,
+        super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
                 BiometricsProtoEnums.MODALITY_FINGERPRINT);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mDaemon.enumerate();
+            getFreshDaemon().enumerate();
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting enumerate", e);
-            mFinishCallback.onClientFinished(this);
-        }
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        try {
-            mDaemon.cancel();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Remote exception when requesting cancel", e);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
index 6d7e761..a9336ef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
@@ -28,6 +28,8 @@
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.RemovalClient;
 
+import java.util.Map;
+
 /**
  * Fingerprint-specific removal client supporting the
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
@@ -36,31 +38,23 @@
 class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
     private static final String TAG = "FingerprintRemovalClient";
 
-    FingerprintRemovalClient(@NonNull Context context, @NonNull IBinder token,
+    FingerprintRemovalClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) {
-        super(context, token, listener, biometricId, userId, owner, utils, sensorId,
-                BiometricsProtoEnums.MODALITY_FINGERPRINT);
+            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
+            @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+                authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
     }
 
     @Override
     protected void startHalOperation() {
         try {
             // GroupId was never used. In fact, groupId is always the same as userId.
-            mDaemon.remove(getTargetUserId(), mBiometricId);
+            getFreshDaemon().remove(getTargetUserId(), mBiometricId);
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting remove", e);
-            mFinishCallback.onClientFinished(this);
-        }
-    }
-
-    @Override
-    protected void stopHalOperation() {
-        try {
-            mDaemon.cancel();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Remote exception when requesting cancel", e);
-            mFinishCallback.onClientFinished(this);
+            mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
index ccbea31d..882660e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
@@ -35,15 +35,16 @@
 
     private static final String TAG = "FingerprintRevokeChallengeClient";
 
-    FingerprintRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token,
+    FingerprintRevokeChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
             @NonNull String owner, int sensorId) {
-        super(context, token, owner, sensorId);
+        super(context, lazyDaemon, token, owner, sensorId);
     }
 
     @Override
     protected void startHalOperation() {
         try {
-            mDaemon.postEnroll();
+            getFreshDaemon().postEnroll();
         } catch (RemoteException e) {
             Slog.e(TAG, "revokeChallenge/postEnroll failed", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index d2a25db..b05400a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -64,9 +63,11 @@
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricServiceBase;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitor;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import com.android.server.biometrics.sensors.LockoutResetTracker;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.PerformanceTracker;
 import com.android.server.biometrics.sensors.RemovalClient;
@@ -82,7 +83,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -97,6 +97,10 @@
     private static final boolean DEBUG = true;
     private static final String FP_DATA_DIR = "fpdata";
 
+    private final LockoutResetTracker mLockoutResetTracker;
+    private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
+    private final GestureAvailabilityTracker mGestureAvailabilityTracker;
+
     /**
      * Receives the incoming binder calls from FingerprintManager.
      */
@@ -113,7 +117,7 @@
             checkPermission(MANAGE_FINGERPRINT);
 
             final GenerateChallengeClient client = new FingerprintGenerateChallengeClient(
-                    getContext(), token, new ClientMonitorCallbackConverter(receiver),
+                    getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
                     opPackageName, getSensorId());
             generateChallengeInternal(client);
         }
@@ -123,7 +127,7 @@
             checkPermission(MANAGE_FINGERPRINT);
 
             final RevokeChallengeClient client = new FingerprintRevokeChallengeClient(getContext(),
-                    token, owner, getSensorId());
+                    mLazyDaemon, token, owner, getSensorId());
             revokeChallengeInternal(client);
         }
 
@@ -134,8 +138,8 @@
             checkPermission(MANAGE_FINGERPRINT);
             updateActiveGroup(userId);
 
-            final EnrollClient client = new FingerprintEnrollClient(getContext(), token,
-                    new ClientMonitorCallbackConverter(receiver), userId, cryptoToken,
+            final EnrollClient client = new FingerprintEnrollClient(getContext(), mLazyDaemon,
+                    token, new ClientMonitorCallbackConverter(receiver), userId, cryptoToken,
                     opPackageName, getBiometricUtils(), ENROLL_TIMEOUT_SEC, getSensorId());
 
             enrollInternal(client, userId);
@@ -165,9 +169,10 @@
             final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
                     : BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
             final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
-                    token, new ClientMonitorCallbackConverter(receiver), userId, opId, restricted,
-                    opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
-                    isStrongBiometric, surface, statsClient, mTaskStackListener, mLockoutTracker);
+                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
+                    restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
+                    getSensorId(), isStrongBiometric, surface, statsClient, mTaskStackListener,
+                    mLockoutTracker);
             authenticateInternal(client, opPackageName);
         }
 
@@ -175,14 +180,14 @@
         public void prepareForAuthentication(IBinder token, long opId, int userId,
                 IBiometricSensorReceiver sensorReceiver, String opPackageName,
                 int cookie, int callingUid, int callingPid, int callingUserId,
-                Surface surface) throws RemoteException {
+                Surface surface) {
             checkPermission(MANAGE_BIOMETRIC);
             updateActiveGroup(userId);
 
             final boolean restricted = true; // BiometricPrompt is always restricted
             final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
-                    token, new ClientMonitorCallbackConverter(sensorReceiver), userId, opId,
-                    restricted, opPackageName, cookie, false /* requireConfirmation */,
+                    mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
+                    opId, restricted, opPackageName, cookie, false /* requireConfirmation */,
                     getSensorId(), isStrongBiometric(), surface,
                     BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
                     mLockoutTracker);
@@ -223,17 +228,19 @@
                 return;
             }
 
-            final RemovalClient client = new FingerprintRemovalClient(getContext(), token,
-                    new ClientMonitorCallbackConverter(receiver), fingerId, userId, opPackageName,
-                    getBiometricUtils(), getSensorId());
+            final RemovalClient client = new FingerprintRemovalClient(getContext(), mLazyDaemon,
+                    token, new ClientMonitorCallbackConverter(receiver), fingerId, userId,
+                    opPackageName, getBiometricUtils(), getSensorId(), mAuthenticatorIds);
             removeInternal(client);
         }
 
         @Override
-        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
-                throws RemoteException {
+        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+                final String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            FingerprintService.super.addLockoutResetCallback(callback);
+            mHandler.post(() -> {
+                mLockoutResetTracker.addCallback(callback, opPackageName);
+            });
         }
 
         @Override // Binder call
@@ -350,13 +357,13 @@
         @Override
         public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
             checkPermission(MANAGE_FINGERPRINT);
-            mClientActiveCallbacks.add(callback);
+            mGestureAvailabilityTracker.registerCallback(callback);
         }
 
         @Override
         public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
             checkPermission(MANAGE_FINGERPRINT);
-            mClientActiveCallbacks.remove(callback);
+            mGestureAvailabilityTracker.removeCallback(callback);
         }
 
         @Override // Binder call
@@ -464,17 +471,11 @@
     }
 
     private final LockoutFrameworkImpl mLockoutTracker;
-    private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
-            new CopyOnWriteArrayList<>();
     private IUdfpsOverlayController mUdfpsOverlayController;
 
     @GuardedBy("this")
     private IBiometricsFingerprint mDaemon;
 
-    private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback = userId -> {
-        notifyLockoutResetMonitors();
-    };
-
     /**
      * Receives callbacks from the HAL.
      */
@@ -550,7 +551,13 @@
 
     public FingerprintService(Context context) {
         super(context);
-        mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
+        mLazyDaemon = FingerprintService.this::getFingerprintDaemon;
+        mGestureAvailabilityTracker = new GestureAvailabilityTracker();
+        mLockoutResetTracker = new LockoutResetTracker(context);
+        final LockoutFrameworkImpl.LockoutResetCallback lockoutResetCallback = userId -> {
+            mLockoutResetTracker.notifyLockoutResetCallbacks();
+        };
+        mLockoutTracker = new LockoutFrameworkImpl(context, lockoutResetCallback);
     }
 
     @Override
@@ -677,20 +684,12 @@
         if (userId != UserHandle.getCallingUserId()) {
             checkPermission(INTERACT_ACROSS_USERS);
         }
-        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+        return FingerprintUtils.getInstance().getBiometricsForUser(getContext(), userId);
     }
 
     @Override
     protected void notifyClientActiveCallbacks(boolean isActive) {
-        List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
-        for (int i = 0; i < callbacks.size(); i++) {
-            try {
-                callbacks.get(i).onClientActiveChanged(isActive);
-            } catch (RemoteException re) {
-                // If the remote is dead, stop notifying it
-                mClientActiveCallbacks.remove(callbacks.get(i));
-            }
-        }
+        mGestureAvailabilityTracker.notifyClientActiveCallbacks(isActive);
     }
 
     @Override
@@ -705,11 +704,10 @@
 
     @Override
     protected void doTemplateCleanupForUser(int userId) {
-        final List<? extends BiometricAuthenticator.Identifier> enrolledList =
-                getEnrolledTemplates(userId);
+        final List<Fingerprint> enrolledList = getEnrolledTemplates(userId);
         final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
-                getContext(), userId, getContext().getOpPackageName(), getSensorId(), enrolledList,
-                getBiometricUtils());
+                getContext(), mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(),
+                enrolledList, getBiometricUtils(), mAuthenticatorIds);
         cleanupInternal(client);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
new file mode 100644
index 0000000..6292ecf
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.os.RemoteException;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its
+ * availability changes
+ */
+class GestureAvailabilityTracker {
+    private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
+            new CopyOnWriteArrayList<>();
+
+    void registerCallback(IFingerprintClientActiveCallback callback) {
+        mClientActiveCallbacks.add(callback);
+    }
+
+    void removeCallback(IFingerprintClientActiveCallback callback) {
+        mClientActiveCallbacks.remove(callback);
+    }
+
+    void notifyClientActiveCallbacks(boolean isActive) {
+        for (IFingerprintClientActiveCallback callback : mClientActiveCallbacks) {
+            try {
+                callback.onClientActiveChanged(isActive);
+            } catch (RemoteException re) {
+                // If the remote is dead, stop notifying it
+                mClientActiveCallbacks.remove(callback);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index c5aa8d5..a75a80a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -82,6 +82,7 @@
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
     private final INetd mNetd;
+    private final Dependencies mDeps;
 
     // Values are User IDs.
     @GuardedBy("this")
@@ -102,10 +103,30 @@
     @GuardedBy("this")
     private final Set<Integer> mAllApps = new HashSet<>();
 
-    public PermissionMonitor(Context context, INetd netd) {
+    /**
+     * Dependencies of PermissionMonitor, for injection in tests.
+     */
+    @VisibleForTesting
+    public static class Dependencies {
+        /**
+         * Get device first sdk version.
+         */
+        public int getDeviceFirstSdkInt() {
+            return Build.VERSION.FIRST_SDK_INT;
+        }
+    }
+
+    public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
+        this(context, netd, new Dependencies());
+    }
+
+    @VisibleForTesting
+    PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
+            @NonNull final Dependencies deps) {
         mPackageManager = context.getPackageManager();
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mNetd = netd;
+        mDeps = deps;
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -186,11 +207,6 @@
     }
 
     @VisibleForTesting
-    protected int getDeviceFirstSdkInt() {
-        return Build.VERSION.FIRST_SDK_INT;
-    }
-
-    @VisibleForTesting
     boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
         if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
             return false;
@@ -212,7 +228,7 @@
         if (app.applicationInfo != null) {
             // Backward compatibility for b/114245686, on devices that launched before Q daemons
             // and apps running as the system UID are exempted from this check.
-            if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+            if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) {
                 return true;
             }
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 7b767b8..c4581c8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -155,7 +155,7 @@
             pw.println("  set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
             pw.println("    Sets the lock screen as PIN, using the given PIN to unlock.");
             pw.println("");
-            pw.println("  set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
+            pw.println("  set-password [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
             pw.println("    Sets the lock screen as password, using the given PASSOWRD to unlock.");
             pw.println("");
             pw.println("  sp [--old <CREDENTIAL>] [--user USER_ID]");
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index 55e0795..b9822fcb 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -26,12 +26,12 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.IntArray;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -104,7 +104,7 @@
     // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
     @GuardedBy("mLock")
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final IntArray mSortedAudioPlaybackClientUids = new IntArray();
+    final List<Integer> mSortedAudioPlaybackClientUids = new ArrayList<>();
 
     static AudioPlayerStateMonitor getInstance(Context context) {
         synchronized (AudioPlayerStateMonitor.class) {
@@ -145,8 +145,8 @@
      * audio/video) The UID whose audio is currently playing comes first, then the UID whose audio
      * playback becomes active at the last comes next.
      */
-    public IntArray getSortedAudioPlaybackClientUids() {
-        IntArray sortedAudioPlaybackClientUids = new IntArray();
+    public List<Integer> getSortedAudioPlaybackClientUids() {
+        List<Integer> sortedAudioPlaybackClientUids = new ArrayList();
         synchronized (mLock) {
             sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);
         }
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index b678c89..953aae4 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -22,7 +22,6 @@
 import android.media.session.MediaSession;
 import android.os.Debug;
 import android.os.UserHandle;
-import android.util.IntArray;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -190,7 +189,8 @@
         if (DEBUG) {
             Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
         }
-        IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
+        List<Integer> audioPlaybackUids =
+                mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
         for (int i = 0; i < audioPlaybackUids.size(); i++) {
             int audioPlaybackUid = audioPlaybackUids.get(i);
             MediaSessionRecordImpl mediaButtonSession = findMediaButtonSession(audioPlaybackUid);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5f48871..ca16d57 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -405,7 +405,7 @@
 
     private boolean mDataLoaderFinished = false;
 
-    // TODO(b/159663586): should be protected by mLock
+    @GuardedBy("mLock")
     private IncrementalFileStorages mIncrementalFileStorages;
 
     private static final FileFilter sAddedApkFilter = new FileFilter() {
@@ -1125,13 +1125,18 @@
             return;
         }
         if (isMultiPackage()) {
-            final SparseIntArray remainingSessions = mChildSessionIds.clone();
+            final SparseIntArray remainingSessions;
+            final int[] childSessionIds;
+            synchronized (mLock) {
+                remainingSessions = mChildSessionIds.clone();
+                childSessionIds = mChildSessionIds.copyKeys();
+            }
             final IntentSender childIntentSender =
                     new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
                             .getIntentSender();
             boolean sealFailed = false;
-            for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
-                final int childSessionId = mChildSessionIds.keyAt(i);
+            for (int i = childSessionIds.length - 1; i >= 0; --i) {
+                final int childSessionId = childSessionIds[i];
                 // seal all children, regardless if any of them fail; we'll throw/return
                 // as appropriate once all children have been processed
                 if (!mSessionProvider.getSession(childSessionId)
@@ -1163,13 +1168,17 @@
         }
 
         if (isMultiPackage()) {
-            int childCount = mChildSessionIds.size();
+            final int[] childSessionIds;
+            synchronized (mLock) {
+                childSessionIds = mChildSessionIds.copyKeys();
+            }
+            int childCount = childSessionIds.length;
 
             // This will contain all child sessions that do not encounter an unrecoverable failure
             ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);
 
             for (int i = childCount - 1; i >= 0; --i) {
-                final int childSessionId = mChildSessionIds.keyAt(i);
+                final int childSessionId = childSessionIds[i];
                 // commit all children, regardless if any of them fail; we'll throw/return
                 // as appropriate once all children have been processed
                 try {
@@ -2588,7 +2597,8 @@
      * Adds a child session ID without any safety / sanity checks. This should only be used to
      * build a session from XML or similar.
      */
-    void addChildSessionIdInternal(int sessionId) {
+    @GuardedBy("mLock")
+    void addChildSessionIdLocked(int sessionId) {
         mChildSessionIds.put(sessionId, 0);
     }
 
@@ -2964,7 +2974,10 @@
 
     @Override
     public int[] getChildSessionIds() {
-        final int[] childSessionIds = mChildSessionIds.copyKeys();
+        final int[] childSessionIds;
+        synchronized (mLock) {
+            childSessionIds = mChildSessionIds.copyKeys();
+        }
         if (childSessionIds != null) {
             return childSessionIds;
         }
@@ -2990,7 +3003,7 @@
                 return;
             }
             childSession.setParentSessionId(this.sessionId);
-            addChildSessionIdInternal(childSessionId);
+            addChildSessionIdLocked(childSessionId);
         }
     }
 
@@ -3157,10 +3170,10 @@
             for (FileBridge bridge : mBridges) {
                 bridge.forceClose();
             }
-        }
-        if (mIncrementalFileStorages != null) {
-            mIncrementalFileStorages.cleanUp();
-            mIncrementalFileStorages = null;
+            if (mIncrementalFileStorages != null) {
+                mIncrementalFileStorages.cleanUp();
+                mIncrementalFileStorages = null;
+            }
         }
         // For staged sessions, we don't delete the directory where the packages have been copied,
         // since these packages are supposed to be read on reboot.
@@ -3197,9 +3210,11 @@
     }
 
     private void cleanStageDir() {
-        if (mIncrementalFileStorages != null) {
-            mIncrementalFileStorages.cleanUp();
-            mIncrementalFileStorages = null;
+        synchronized (mLock) {
+            if (mIncrementalFileStorages != null) {
+                mIncrementalFileStorages.cleanUp();
+                mIncrementalFileStorages = null;
+            }
         }
         try {
             mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 734b718..de06c92 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -49,11 +49,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.EventLogTags;
 import com.android.server.SystemService;
-import com.android.server.pm.InstructionSets;
 import com.android.server.pm.PackageManagerService;
 
-import dalvik.system.VMRuntime;
-
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -214,7 +211,7 @@
                 newLevel = State.LEVEL_FULL;
             } else if (usableBytes <= lowBytes) {
                 newLevel = State.LEVEL_LOW;
-            } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
+            } else if (StorageManager.UUID_DEFAULT.equals(uuid)
                     && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
                 newLevel = State.LEVEL_LOW;
             } else {
@@ -261,15 +258,6 @@
         };
     }
 
-    private static boolean isBootImageOnDisk() {
-        for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
-            if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     @Override
     public void onStart() {
         final Context context = getContext();
@@ -481,15 +469,8 @@
             final CharSequence title = context.getText(
                     com.android.internal.R.string.low_internal_storage_view_title);
 
-            final CharSequence details;
-            if (StorageManager.UUID_DEFAULT.equals(uuid)) {
-                details = context.getText(isBootImageOnDisk()
-                        ? com.android.internal.R.string.low_internal_storage_view_text
-                        : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
-            } else {
-                details = context.getText(
-                        com.android.internal.R.string.low_internal_storage_view_text);
-            }
+            final CharSequence details = context.getText(
+                    com.android.internal.R.string.low_internal_storage_view_text);
 
             PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
                     null, UserHandle.CURRENT);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 61c127c..601d64b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -925,8 +925,6 @@
     void awakeFromSleepingLocked() {
         // Ensure activities are no longer sleeping.
         forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
-        ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */);
         if (mPausingActivity != null) {
             Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
             mPausingActivity.activityPaused(true);
@@ -2416,7 +2414,7 @@
         forAllActivities(ActivityRecord::removeLaunchTickRunnable);
     }
 
-    private void updateTransitLocked(int transit, ActivityOptions options) {
+    private void updateTransitLocked(int transit, ActivityOptions options, boolean forceOverride) {
         if (options != null) {
             ActivityRecord r = topRunningActivity();
             if (r != null && !r.isState(RESUMED)) {
@@ -2425,7 +2423,8 @@
                 ActivityOptions.abort(options);
             }
         }
-        getDisplay().mDisplayContent.prepareAppTransition(transit, false);
+        getDisplay().mDisplayContent.prepareAppTransition(transit, false,
+                0 /* flags */, forceOverride);
     }
 
     final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -2445,8 +2444,17 @@
             // nothing to do!
             if (noAnimation) {
                 ActivityOptions.abort(options);
+            } else if (isSingleTaskInstance()) {
+                // When a task is moved front on the display which can only contain one task, start
+                // a special transition.
+                // {@link AppTransitionController#handleAppTransitionReady} later picks up the
+                // transition, and schedules
+                // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered
+                // after contents are drawn on the display.
+                updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+                        true /* forceOverride */);
             } else {
-                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
             }
             return;
         }
@@ -2492,9 +2500,13 @@
                     mStackSupervisor.mNoAnimActivities.add(r);
                 }
                 ActivityOptions.abort(options);
+            } else if (isSingleTaskInstance()) {
+                updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+                        true /* forceOverride */);
             } else {
-                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
             }
+
             // If a new task is moved to the front, then mark the existing top activity as
             // supporting
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 63e14bb..9852208 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -252,6 +252,7 @@
                     req = mLastKeyguardForcedOrientation;
                 }
             }
+            mLastOrientationSource = win;
             return req;
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9c330ec..4f25609 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1170,6 +1170,8 @@
 
         activity.onRemovedFromDisplay();
         if (activity == mFixedRotationLaunchingApp) {
+            // Make sure the states of associated tokens are also cleared.
+            activity.finishFixedRotationTransform();
             setFixedRotationLaunchingAppUnchecked(null);
         }
     }
@@ -1468,6 +1470,12 @@
                 // window was transferred ({@link #mSkipAppTransitionAnimation}).
                 return false;
             }
+            if ((mAppTransition.getTransitFlags()
+                    & WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) {
+                // The transition may be finished before keyguard hidden. In order to avoid the
+                // intermediate orientation change, it is more stable to freeze the display.
+                return false;
+            }
         } else if (r != topRunningActivity()) {
             // If the transition has not started yet, the activity must be the top.
             return false;
@@ -2291,12 +2299,23 @@
     void onAppTransitionDone() {
         super.onAppTransitionDone();
         mWmService.mWindowsChanged = true;
+        // If the transition finished callback cannot match the token for some reason, make sure the
+        // rotated state is cleared if it is already invisible.
+        if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.mVisibleRequested
+                && !mFixedRotationLaunchingApp.isVisible()
+                && !mDisplayRotation.isRotatingSeamlessly()) {
+            clearFixedRotationLaunchingApp();
+        }
     }
 
     @Override
     public void setWindowingMode(int windowingMode) {
-        super.setWindowingMode(windowingMode);
-        super.setDisplayWindowingMode(windowingMode);
+        // Intentionally call onRequestedOverrideConfigurationChanged() directly to change windowing
+        // mode and display windowing mode atomically.
+        mTmpConfiguration.setTo(getRequestedOverrideConfiguration());
+        mTmpConfiguration.windowConfiguration.setWindowingMode(windowingMode);
+        mTmpConfiguration.windowConfiguration.setDisplayWindowingMode(windowingMode);
+        onRequestedOverrideConfigurationChanged(mTmpConfiguration);
     }
 
     @Override
@@ -2952,11 +2971,9 @@
 
         final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
         if (rotationAnimation != null) {
-            pw.print(subPrefix);
             pw.println("  mScreenRotationAnimation:");
-            rotationAnimation.printTo("  ", pw);
+            rotationAnimation.printTo(subPrefix, pw);
         } else if (dumpAll) {
-            pw.print(subPrefix);
             pw.println("  no ScreenRotationAnimation ");
         }
 
@@ -3647,15 +3664,15 @@
         drawnWindowTypes.put(TYPE_NOTIFICATION_SHADE, true);
 
         final WindowState visibleNotDrawnWindow = getWindow(w -> {
-            boolean isVisible = w.mViewVisibility == View.VISIBLE && !w.mObscured;
-            boolean hasDrawn = w.isDrawnLw() && w.hasDrawnLw();
-            if (isVisible && !hasDrawn) {
+            final boolean isVisible = w.isVisible() && !w.mObscured;
+            final boolean isDrawn = w.isDrawnLw();
+            if (isVisible && !isDrawn) {
                 ProtoLog.d(WM_DEBUG_BOOT,
                         "DisplayContent: boot is waiting for window of type %d to be drawn",
                         w.mAttrs.type);
                 return true;
             }
-            if (hasDrawn) {
+            if (isDrawn) {
                 switch (w.mAttrs.type) {
                     case TYPE_BOOT_PROGRESS:
                     case TYPE_BASE_APPLICATION:
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 3e88566..8734b5e 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,13 +19,10 @@
 import static android.os.Process.myPid;
 import static android.os.Process.myUid;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
@@ -480,11 +477,12 @@
                     mService.getRecentsAnimationController();
             final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
                     && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
-            if (inputWindowHandle == null || w.mRemoved) {
+            if (inputChannel == null || inputWindowHandle == null || w.mRemoved
+                    || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
                 if (w.mWinAnimator.hasSurface()) {
                     mInputTransaction.setInputWindowInfo(
-                            w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
-                            mInvalidInputWindow);
+                        w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
+                        mInvalidInputWindow);
                 }
                 // Skip this window because it cannot possibly receive input.
                 return;
@@ -493,23 +491,9 @@
             final int flags = w.mAttrs.flags;
             final int privateFlags = w.mAttrs.privateFlags;
             final int type = w.mAttrs.type;
+            final boolean hasFocus = w.isFocused();
             final boolean isVisible = w.isVisibleLw();
 
-            // Assign an InputInfo with type to the overlay window which can't receive input event.
-            // This is used to omit Surfaces from occlusion detection.
-            if (inputChannel == null
-                    || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer))  {
-                if (!w.mWinAnimator.hasSurface()) {
-                    return;
-                }
-                populateOverlayInputInfo(inputWindowHandle, w.getName(), type, isVisible);
-                mInputTransaction.setInputWindowInfo(
-                        w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
-                        inputWindowHandle);
-                return;
-            }
-
-            final boolean hasFocus = w.isFocused();
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
                         mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
@@ -571,28 +555,6 @@
         }
     }
 
-    // This would reset InputWindowHandle fields to prevent it could be found by input event.
-    // We need to check if any new field of InputWindowHandle could impact the result.
-    private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle,
-            final String name, final int type, final boolean isVisible) {
-        inputWindowHandle.name = name;
-        inputWindowHandle.layoutParamsType = type;
-        inputWindowHandle.dispatchingTimeoutNanos =
-                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
-        inputWindowHandle.visible = isVisible;
-        inputWindowHandle.canReceiveKeys = false;
-        inputWindowHandle.hasFocus = false;
-        inputWindowHandle.ownerPid = myPid();
-        inputWindowHandle.ownerUid = myUid();
-        inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
-        inputWindowHandle.scaleFactor = 1;
-        inputWindowHandle.layoutParamsFlags =
-                FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE;
-        inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
-        inputWindowHandle.touchableRegion.setEmpty();
-        inputWindowHandle.setTouchableRegionCrop(null);
-    }
-
     /**
      * Helper function to generate an InputInfo with type SECURE_SYSTEM_OVERLAY. This input
      * info will not have an input channel or be touchable, but is used to omit Surfaces
@@ -602,7 +564,16 @@
     static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t,
             int displayId, String name) {
         InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId);
-        populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
+        inputWindowHandle.name = name;
+        inputWindowHandle.layoutParamsType = TYPE_SECURE_SYSTEM_OVERLAY;
+        inputWindowHandle.dispatchingTimeoutNanos = -1;
+        inputWindowHandle.visible = true;
+        inputWindowHandle.canReceiveKeys = false;
+        inputWindowHandle.hasFocus = false;
+        inputWindowHandle.ownerPid = myPid();
+        inputWindowHandle.ownerUid = myUid();
+        inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
+        inputWindowHandle.scaleFactor = 1;
         t.setInputWindowInfo(sc, inputWindowHandle);
     }
 }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c7f7834..c255a18 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -359,17 +359,20 @@
         RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
                 Rect endBounds, Rect startBounds) {
             mWindowContainer = windowContainer;
-            mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
             if (startBounds != null) {
                 mStartBounds = new Rect(startBounds);
+                mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+                        mStartBounds);
                 mTmpRect.set(startBounds);
                 mTmpRect.offsetTo(0, 0);
                 if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
                     mThumbnailAdapter =
                             new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
-                                    mTmpRect);
+                                    mTmpRect, new Rect());
                 }
             } else {
+                mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+                        new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom));
                 mStartBounds = null;
             }
         }
@@ -407,13 +410,15 @@
         final Point mPosition = new Point();
         final Rect mLocalBounds;
         final Rect mStackBounds = new Rect();
+        final Rect mStartBounds = new Rect();
 
         RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
-                Rect localBounds, Rect stackBounds) {
+                Rect localBounds, Rect stackBounds, Rect startBounds) {
             mRecord = record;
             mPosition.set(position.x, position.y);
             mLocalBounds = localBounds;
             mStackBounds.set(stackBounds);
+            mStartBounds.set(startBounds);
         }
 
         @Override
@@ -427,13 +432,12 @@
             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
 
             // Restore position and stack crop until client has a chance to modify it.
-            if (mRecord.mStartBounds != null) {
-                t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
-                t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
-                        mRecord.mStartBounds.height());
+            if (mStartBounds.isEmpty()) {
+                t.setPosition(animationLeash, 0, 0);
+                t.setWindowCrop(animationLeash, -1, -1);
             } else {
-                t.setPosition(animationLeash, mPosition.x, mPosition.y);
-                t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());
+                t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top);
+                t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
             }
             mCapturedLeash = animationLeash;
             mCapturedFinishCallback = finishCallback;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e787aac..a795867 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2375,7 +2375,8 @@
                         // triggered after contents are drawn on the display.
                         if (display.isSingleTaskInstance()) {
                             display.mDisplayContent.prepareAppTransition(
-                                    TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+                                    TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false,
+                                    0 /* flags */, true /* forceOverride*/);
                         }
                         stack.awakeFromSleepingLocked();
                         if (display.isSingleTaskInstance()) {
@@ -2391,6 +2392,12 @@
                             // activity here.
                             resumeFocusedStacksTopActivities();
                         }
+                        // The visibility update must not be called before resuming the top, so the
+                        // display orientation can be updated first if needed. Otherwise there may
+                        // have redundant configuration changes due to apply outdated display
+                        // orientation (from keyguard) to activity.
+                        stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                                false /* preserveWindows */);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 83b7543..bcd71c9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1419,7 +1419,7 @@
     void removeChild(WindowContainer r, String reason) {
         // A rootable child task that is now being removed from an organized task. Making sure
         // the stack references is keep updated.
-        if (mTaskOrganizer != null && mCreatedByOrganizer && r.asTask() != null) {
+        if (mCreatedByOrganizer && r.asTask() != null) {
             getDisplayArea().removeStackReferenceIfNeeded((ActivityStack) r);
         }
         if (!mChildren.contains(r)) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 67673dc..24cf751 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1859,14 +1859,14 @@
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         pw.println(prefix + "TaskDisplayArea " + getName());
-        super.dump(pw, prefix, dumpAll);
+        final String doublePrefix = prefix + "  ";
+        super.dump(pw, doublePrefix, dumpAll);
         if (mPreferredTopFocusableStack != null) {
-            pw.println(prefix + "  mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+            pw.println(doublePrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
         }
         if (mLastFocusedStack != null) {
-            pw.println(prefix + "  mLastFocusedStack=" + mLastFocusedStack);
+            pw.println(doublePrefix + "mLastFocusedStack=" + mLastFocusedStack);
         }
-        final String doublePrefix = prefix + "  ";
         final String triplePrefix = doublePrefix + "  ";
         pw.println(doublePrefix + "Application tokens in top down Z order:");
         for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e3d6530..4de7772 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2557,6 +2557,9 @@
             pw.print(prefix); pw.println("ContainerAnimator:");
             mSurfaceAnimator.dump(pw, prefix + "  ");
         }
+        if (mLastOrientationSource != null) {
+            pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
+        }
     }
 
     final void updateSurfacePosition() {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 75ec224..7bd455a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -194,7 +194,7 @@
 public:
     NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);
 
-    inline sp<InputManager> getInputManager() const { return mInputManager; }
+    inline sp<InputManagerInterface> getInputManager() const { return mInputManager; }
 
     void dump(std::string& dump);
 
@@ -225,7 +225,7 @@
     /* --- InputReaderPolicyInterface implementation --- */
 
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
-    virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+    virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
     virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
@@ -236,29 +236,26 @@
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
-    virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
-                              uint32_t policyFlags) override;
-    virtual void notifyConfigurationChanged(nsecs_t when);
-    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
-                              const sp<IBinder>& token, const std::string& reason) override;
-    virtual void notifyInputChannelBroken(const sp<IBinder>& token);
-    virtual void notifyFocusChanged(const sp<IBinder>& oldToken,
-                                    const sp<IBinder>& newToken) override;
-    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
-    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
-    virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
-                                            uint32_t& policyFlags) override;
-    virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
-                                               uint32_t& policyFlags) override;
-    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
-                                                  const KeyEvent* keyEvent,
-                                                  uint32_t policyFlags) override;
-    virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
-                                      uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
-    virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
-                                                         int32_t injectorUid) override;
-    virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
+    void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+                      uint32_t policyFlags) override;
+    void notifyConfigurationChanged(nsecs_t when) override;
+    std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+                                       const sp<IBinder>& token,
+                                       const std::string& reason) override;
+    void notifyInputChannelBroken(const sp<IBinder>& token) override;
+    void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
+    bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
+    void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
+    void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) override;
+    void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+                                       uint32_t& policyFlags) override;
+    nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent* keyEvent,
+                                          uint32_t policyFlags) override;
+    bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+                              uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
+    void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
+    bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
+    void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
 
     /* --- PointerControllerPolicyInterface implementation --- */
 
@@ -270,7 +267,7 @@
     virtual int32_t getCustomPointerIconId();
 
 private:
-    sp<InputManager> mInputManager;
+    sp<InputManagerInterface> mInputManager;
 
     jobject mServiceObj;
     sp<Looper> mLooper;
@@ -299,7 +296,7 @@
         sp<SpriteController> spriteController;
 
         // Pointer controller singleton, created and destroyed as needed.
-        wp<PointerController> pointerController;
+        std::weak_ptr<PointerController> pointerController;
 
         // Input devices to be disabled
         std::set<int32_t> disabledInputDevices;
@@ -342,9 +339,9 @@
     }
     mInteractive = true;
 
-    mInputManager = new InputManager(this, this);
-    defaultServiceManager()->addService(String16("inputflinger"),
-            mInputManager, false);
+    InputManager* im = new InputManager(this, this);
+    mInputManager = im;
+    defaultServiceManager()->addService(String16("inputflinger"), im);
 }
 
 NativeInputManager::~NativeInputManager() {
@@ -544,15 +541,16 @@
     } // release lock
 }
 
-sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
+std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController(
+        int32_t /* deviceId */) {
     ATRACE_CALL();
     AutoMutex _l(mLock);
 
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller == nullptr) {
         ensureSpriteControllerLocked();
 
-        controller = new PointerController(this, mLooper, mLocked.spriteController);
+        controller = PointerController::create(this, mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
         updateInactivityTimeoutLocked();
     }
@@ -694,8 +692,9 @@
     return handle->getInputApplicationHandleObjLocalRef(env);
 }
 
-nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
-                                      const sp<IBinder>& token, const std::string& reason) {
+std::chrono::nanoseconds NativeInputManager::notifyAnr(
+        const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token,
+        const std::string& reason) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     ALOGD("notifyANR");
 #endif
@@ -718,7 +717,7 @@
     } else {
         assert(newTimeout >= 0);
     }
-    return newTimeout;
+    return std::chrono::nanoseconds(newTimeout);
 }
 
 void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
@@ -803,15 +802,14 @@
 }
 
 void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller == nullptr) {
         return;
     }
 
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
-    controller->setInactivityTimeout(lightsOut
-            ? PointerController::INACTIVITY_TIMEOUT_SHORT
-            : PointerController::INACTIVITY_TIMEOUT_NORMAL);
+    controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT
+                                               : PointerController::InactivityTimeout::NORMAL);
 }
 
 void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -891,7 +889,7 @@
 
 void NativeInputManager::setPointerIconType(int32_t iconId) {
     AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller != nullptr) {
         controller->updatePointerIcon(iconId);
     }
@@ -899,7 +897,7 @@
 
 void NativeInputManager::reloadPointerIcons() {
     AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller != nullptr) {
         controller->reloadPointerResources();
     }
@@ -907,7 +905,7 @@
 
 void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
     AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller != nullptr) {
         controller->setCustomPointerIcon(icon);
     }
@@ -1253,7 +1251,7 @@
 }
 
 void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
-    mInputManager->setMotionClassifierEnabled(enabled);
+    mInputManager->getClassifier()->setMotionClassifierEnabled(enabled);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 24da81e..a2e310a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -618,12 +618,6 @@
             }
         }
 
-        // Diagnostic to ensure that the system is in a base healthy state. Done here as a common
-        // non-zygote process.
-        if (!VMRuntime.hasBootImageSpaces()) {
-            Slog.wtf(TAG, "Runtime is not running with a boot image!");
-        }
-
         // Loop forever.
         Looper.loop();
         throw new RuntimeException("Main thread loop unexpectedly exited");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 668f047..c7b45ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -127,8 +127,7 @@
         mTask = mStack.getBottomMostTask();
         mActivity = mTask.getTopNonFinishingActivity();
 
-        doReturn(false).when(mService).isBooting();
-        doReturn(true).when(mService).isBooted();
+        setBooted(mService);
     }
 
     @Test
@@ -1535,7 +1534,7 @@
      * Sets orientation without notifying the parent to simulate that the display has not applied
      * the requested orientation yet.
      */
-    private static void setRotatedScreenOrientationSilently(ActivityRecord r) {
+    static void setRotatedScreenOrientationSilently(ActivityRecord r) {
         final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT
                 ? SCREEN_ORIENTATION_LANDSCAPE
                 : SCREEN_ORIENTATION_PORTRAIT;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 37882bb..1b42a04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1392,8 +1392,7 @@
         }
         mSupervisor.endDeferResume();
 
-        doReturn(false).when(mService).isBooting();
-        doReturn(true).when(mService).isBooted();
+        setBooted(mService);
         // 2 activities are started while keyguard is locked, so they are waiting to be resolved.
         assertFalse(unknownAppVisibilityController.allResolved());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 64b5eca..f65d6e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -67,8 +67,7 @@
 
     @Before
     public void setUp() throws Exception {
-        doReturn(false).when(mService).isBooting();
-        doReturn(true).when(mService).isBooted();
+        setBooted(mService);
     }
 
     /** Verify that activity is finished correctly upon request. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4466a66..f4f199c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -482,12 +482,17 @@
         final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
         final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
                 TYPE_WALLPAPER, TYPE_APPLICATION);
+        final WindowState wallpaper = windows[0];
+        assertTrue(wallpaper.mIsWallpaper);
+        // By default WindowState#mWallpaperVisible is false.
+        assertFalse(wallpaper.isVisible());
 
         // Verify waiting for windows to be drawn.
         assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
 
-        // Verify not waiting for drawn windows.
-        makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+        // Verify not waiting for drawn window and invisible wallpaper.
+        setDrawnState(WindowStateAnimator.READY_TO_SHOW, wallpaper);
+        setDrawnState(WindowStateAnimator.HAS_DRAWN, windows[1]);
         assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
     }
 
@@ -508,26 +513,10 @@
         assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
 
         // Verify not waiting for drawn windows on display with system decorations.
-        makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+        setDrawnState(WindowStateAnimator.HAS_DRAWN, windows);
         assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
     }
 
-    @Test
-    public void testShouldWaitForSystemDecorWindowsOnBoot_OnWindowReadyToShowAndDrawn() {
-        mWm.mSystemBooted = true;
-        final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
-        final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
-                TYPE_WALLPAPER, TYPE_APPLICATION);
-
-        // Verify waiting for windows to be drawn.
-        makeWindowsDrawnState(windows, WindowStateAnimator.READY_TO_SHOW);
-        assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
-
-        // Verify not waiting for drawn windows.
-        makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
-        assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
-    }
-
     private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) {
         final WindowState[] windows = new WindowState[types.length];
         for (int i = 0; i < types.length; i++) {
@@ -538,9 +527,9 @@
         return windows;
     }
 
-    private static void makeWindowsDrawnState(WindowState[] windows, int state) {
+    private static void setDrawnState(int state, WindowState... windows) {
         for (WindowState window : windows) {
-            window.mHasSurface = true;
+            window.mHasSurface = state != WindowStateAnimator.NO_SURFACE;
             window.mWinAnimator.mDrawState = state;
         }
     }
@@ -1291,8 +1280,7 @@
     @Test
     public void testNoFixedRotationWithPip() {
         // Make resume-top really update the activity state.
-        doReturn(false).when(mWm.mAtmService).isBooting();
-        doReturn(true).when(mWm.mAtmService).isBooted();
+        setBooted(mWm.mAtmService);
         // Speed up the test by a few seconds.
         mWm.mAtmService.deferWindowLayout();
         doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
@@ -1512,6 +1500,24 @@
         mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
     }
 
+    @Test
+    public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
+        final DisplayContent dc = createNewDisplay();
+        final ActivityStack stack =
+                new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
+                .setDisplay(dc)
+                .build();
+        doAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            final Configuration config = ((Configuration) args[0]);
+            assertEquals(config.windowConfiguration.getWindowingMode(),
+                    config.windowConfiguration.getDisplayWindowingMode());
+            return null;
+        }).when(stack).onConfigurationChanged(any());
+        dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index add2054..13f04d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -46,7 +46,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.server.testutils.OffsettableClock;
@@ -244,7 +243,7 @@
     }
 
     @Test
-    public void testChange() throws Exception {
+    public void testChangeToSmallerSize() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mDisplayContent.mChangingContainers.add(win.mActivityRecord);
         try {
@@ -279,8 +278,61 @@
             assertEquals(false, app.isTranslucent);
             verify(mMockTransaction).setPosition(
                     mMockLeash, app.startBounds.left, app.startBounds.top);
-            verify(mMockTransaction).setWindowCrop(mMockLeash, 200, 200);
+            verify(mMockTransaction).setWindowCrop(
+                    mMockLeash, app.startBounds.width(), app.startBounds.height());
             verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+            verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
+
+            finishedCaptor.getValue().onAnimationFinished();
+            verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
+                    eq(record.mAdapter));
+            verify(mThumbnailFinishedCallback).onAnimationFinished(
+                    eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
+        } finally {
+            mDisplayContent.mChangingContainers.clear();
+        }
+    }
+
+    @Test
+    public void testChangeTolargerSize() throws Exception {
+        final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+        mDisplayContent.mChangingContainers.add(win.mActivityRecord);
+        try {
+            final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
+                    win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200),
+                    new Rect(50, 100, 150, 150));
+            assertNotNull(record.mThumbnailAdapter);
+            ((AnimationAdapter) record.mAdapter)
+                    .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
+                            mFinishedCallback);
+            ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
+                    mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
+            mController.goodToGo();
+            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+                    ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+                    finishedCaptor.capture());
+            assertEquals(1, appsCaptor.getValue().length);
+            final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+            assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
+            assertEquals(new Point(0, 0), app.position);
+            assertEquals(new Rect(0, 0, 200, 200), app.sourceContainerBounds);
+            assertEquals(new Rect(50, 100, 150, 150), app.startBounds);
+            assertEquals(mMockLeash, app.leash);
+            assertEquals(mMockThumbnailLeash, app.startLeash);
+            assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+            assertEquals(false, app.isTranslucent);
+            verify(mMockTransaction).setPosition(
+                    mMockLeash, app.startBounds.left, app.startBounds.top);
+            verify(mMockTransaction).setWindowCrop(
+                    mMockLeash, app.startBounds.width(), app.startBounds.height());
+            verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+            verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
 
             finishedCaptor.getValue().onAnimationFinished();
             verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 5dba0045..51db099 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -42,6 +42,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -57,8 +58,10 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
 import android.util.Pair;
 
 import androidx.test.filters.MediumTest;
@@ -221,6 +224,35 @@
                 null /* target */, null /* targetOptions */);
     }
 
+    @Test
+    public void testAwakeFromSleepingWithAppConfiguration() {
+        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true).build();
+        activity.moveFocusableActivityToTop("test");
+        assertTrue(activity.getStack().isFocusedStackOnDisplay());
+        ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
+
+        final Configuration rotatedConfig = new Configuration();
+        display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
+                .rotationForOrientation(activity.getOrientation(), display.getRotation()));
+        assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
+        // Assume the activity was shown in different orientation. For example, the top activity is
+        // landscape and the portrait lockscreen is shown.
+        activity.setLastReportedConfiguration(
+                new MergedConfiguration(mService.getGlobalConfiguration(), rotatedConfig));
+        activity.setState(ActivityState.STOPPED, "sleep");
+
+        display.setIsSleeping(true);
+        doReturn(false).when(display).shouldSleep();
+        // Allow to resume when awaking.
+        setBooted(mService);
+        mRootWindowContainer.applySleepTokens(true);
+
+        // The display orientation should be changed by the activity so there is no relaunch.
+        verify(activity, never()).relaunchActivityLocked(anyBoolean());
+        assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
+    }
+
     /**
      * Verifies that removal of activity with task and stack is done correctly.
      */
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index d7462f8..53c2a5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import android.os.Handler;
 import android.testing.DexmakerShareClassLoaderRule;
 
@@ -67,6 +69,15 @@
     }
 
     /**
+     * Make the system booted, so that {@link ActivityStack#resumeTopActivityInnerLocked} can really
+     * be executed to update activity state and configuration when resuming the current top.
+     */
+    static void setBooted(ActivityTaskManagerService atmService) {
+        doReturn(false).when(atmService).isBooting();
+        doReturn(true).when(atmService).isBooted();
+    }
+
+    /**
      * Utility class to compare the output of T#toString. It is convenient to have readable output
      * of assertion if the string content can represent the expected states.
      */
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7595e3f..6cf0eec 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -164,6 +164,8 @@
     private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
     private static final int MSG_GADGET_HAL_REGISTERED = 18;
     private static final int MSG_RESET_USB_GADGET = 19;
+    private static final int MSG_ACCESSORY_HANDSHAKE_TIMEOUT = 20;
+    private static final int MSG_INCREASE_SENDSTRING_COUNT = 21;
 
     private static final int AUDIO_MODE_SOURCE = 1;
 
@@ -176,6 +178,13 @@
     // Request is cancelled if host does not configure device within 10 seconds.
     private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000;
 
+    /**
+     * Timeout for receiving USB accessory request
+     * Reset when receive next control request
+     * Broadcast intent if not receive next control request or enter next step.
+     */
+    private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000;
+
     private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
 
     private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
@@ -222,8 +231,19 @@
             String accessory = event.get("ACCESSORY");
             if (state != null) {
                 mHandler.updateState(state);
+            } else if ("GETPROTOCOL".equals(accessory)) {
+                if (DEBUG) Slog.d(TAG, "got accessory get protocol");
+                long elapsedRealtime = SystemClock.elapsedRealtime();
+                mHandler.setAccessoryUEventTime(elapsedRealtime);
+                resetAccessoryHandshakeTimeoutHandler();
+            } else if ("SENDSTRING".equals(accessory)) {
+                if (DEBUG) Slog.d(TAG, "got accessory send string");
+                mHandler.sendEmptyMessage(MSG_INCREASE_SENDSTRING_COUNT);
+                resetAccessoryHandshakeTimeoutHandler();
             } else if ("START".equals(accessory)) {
                 if (DEBUG) Slog.d(TAG, "got accessory start");
+                mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+                mHandler.setStartAccessoryTrue();
                 startAccessoryMode();
             }
         }
@@ -395,6 +415,23 @@
         mHandler.sendEmptyMessage(MSG_UPDATE_USER_RESTRICTIONS);
     }
 
+    /*
+     * Start the timeout-timer upon receiving "get_protocol" uevent.
+     * Restart the timer every time if any succeeding uevnet received.
+     * (Only when USB is under accessory mode)
+     * <p>About the details of the related control request and sequence ordering, refer to
+     * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+     */
+    private void resetAccessoryHandshakeTimeoutHandler() {
+        long functions = getCurrentFunctions();
+        // some accesories send get_protocol after start accessory
+        if ((functions & UsbManager.FUNCTION_ACCESSORY) == 0) {
+            mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+                    ACCESSORY_HANDSHAKE_TIMEOUT);
+        }
+    }
+
     private void startAccessoryMode() {
         if (!mHasUsbAccessory) return;
 
@@ -416,6 +453,8 @@
         if (functions != UsbManager.FUNCTION_NONE) {
             mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT),
                     ACCESSORY_REQUEST_TIMEOUT);
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+                    ACCESSORY_HANDSHAKE_TIMEOUT);
             setCurrentFunctions(functions);
         }
     }
@@ -468,6 +507,15 @@
         private int mMidiCard;
         private int mMidiDevice;
 
+        /**
+         * mAccessoryConnectionStartTime record the timing of start_accessory
+         * mSendStringCount count the number of receiving uevent send_string
+         * mStartAccessory record whether start_accessory is received
+         */
+        private long mAccessoryConnectionStartTime = 0L;
+        private int mSendStringCount = 0;
+        private boolean mStartAccessory = false;
+
         private final Context mContext;
         private final UsbAlsaManager mUsbAlsaManager;
         private final UsbPermissionManager mPermissionManager;
@@ -645,6 +693,8 @@
                     // defer accessoryAttached if system is not ready
                     if (mBootCompleted) {
                         mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+                        removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+                        broadcastUsbAccessoryHandshake();
                     } // else handle in boot completed
                 } else {
                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -701,6 +751,30 @@
             return false;
         }
 
+        private void broadcastUsbAccessoryHandshake() {
+            long elapsedRealtime = SystemClock.elapsedRealtime();
+            long accessoryHandShakeEnd = elapsedRealtime;
+
+            // send a sticky broadcast containing USB accessory handshake information
+            Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
+                    .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
+                            mAccessoryConnectionStartTime)
+                    .putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT,
+                            mSendStringCount)
+                    .putExtra(UsbManager.EXTRA_ACCESSORY_START,
+                            mStartAccessory)
+                    .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
+                            accessoryHandShakeEnd);
+
+            if (DEBUG) {
+                Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
+            }
+
+            sendStickyBroadcast(intent);
+            resetUsbAccessoryHandshakeDebuggingInfo();
+            accessoryHandShakeEnd = 0L;
+        }
+
         protected void updateUsbStateBroadcastIfNeeded(long functions) {
             // send a sticky broadcast containing current USB state
             Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
@@ -998,6 +1072,16 @@
                     }
                     break;
                 }
+                case MSG_ACCESSORY_HANDSHAKE_TIMEOUT: {
+                    if (DEBUG) {
+                        Slog.v(TAG, "Accessory handshake timeout");
+                    }
+                    broadcastUsbAccessoryHandshake();
+                    break;
+                }
+                case MSG_INCREASE_SENDSTRING_COUNT: {
+                    mSendStringCount = mSendStringCount + 1;
+                }
             }
         }
 
@@ -1015,6 +1099,7 @@
                 }
                 if (mCurrentAccessory != null) {
                     mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+                    broadcastUsbAccessoryHandshake();
                 }
 
                 updateUsbNotification(false);
@@ -1282,6 +1367,9 @@
             try {
                 writeStringIfNotNull(dump, "kernel_state", UsbHandlerProto.KERNEL_STATE,
                         FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
+            } catch (FileNotFoundException exNotFound) {
+                Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+                        + "kernel state:" + STATE_PATH);
             } catch (Exception e) {
                 Slog.e(TAG, "Could not read kernel state", e);
             }
@@ -1290,6 +1378,9 @@
                 writeStringIfNotNull(dump, "kernel_function_list",
                         UsbHandlerProto.KERNEL_FUNCTION_LIST,
                         FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
+            } catch (FileNotFoundException exNotFound) {
+                Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+                        + "kernel function list:" + FUNCTIONS_PATH);
             } catch (Exception e) {
                 Slog.e(TAG, "Could not read kernel function list", e);
             }
@@ -1301,6 +1392,20 @@
          * Evaluates USB function policies and applies the change accordingly.
          */
         protected abstract void setEnabledFunctions(long functions, boolean forceRestart);
+
+        public void setAccessoryUEventTime(long accessoryConnectionStartTime) {
+            mAccessoryConnectionStartTime = accessoryConnectionStartTime;
+        }
+
+        public void setStartAccessoryTrue() {
+            mStartAccessory = true;
+        }
+
+        public void resetUsbAccessoryHandshakeDebuggingInfo() {
+            mAccessoryConnectionStartTime = 0L;
+            mSendStringCount = 0;
+            mStartAccessory = false;
+        }
     }
 
     private static final class UsbHandlerLegacy extends UsbHandler {
diff --git a/test-base/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java
index 6b79314..9f7a2fa 100644
--- a/test-base/src/android/test/InstrumentationTestCase.java
+++ b/test-base/src/android/test/InstrumentationTestCase.java
@@ -34,9 +34,9 @@
  * A test case that has access to {@link Instrumentation}.
  *
  * @deprecated Use
- * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * <a href="{@docRoot}reference/androidx/test/platform/app/InstrumentationRegistry.html">
  * InstrumentationRegistry</a> instead. New tests should be written using the
- * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ * <a href="{@docRoot}training/testing/index.html">AndroidX Test Library</a>.
  */
 @Deprecated
 public class InstrumentationTestCase extends TestCase {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
index 372765d..00da62f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -19,11 +19,9 @@
 import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 
 import org.junit.FixMethodOrder;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
@@ -36,8 +34,6 @@
 @LargeTest
 @RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 151632128)
-@Ignore("Waiting bug feedback")
 public class OpenAppToSplitScreenTest extends NonRotationTestBase {
 
     public OpenAppToSplitScreenTest(String beginRotationName, int beginRotation) {
@@ -67,15 +63,6 @@
     }
 
     @Test
-    public void checkVisibility_dividerWindowBecomesVisible() {
-        checkResults(result -> WmTraceSubject.assertThat(result)
-                .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
-                .then()
-                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
-                .forAllEntries());
-    }
-
-    @Test
     public void checkVisibility_dividerLayerBecomesVisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
                 .hidesLayer(DOCKED_STACK_DIVIDER)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
index bd9529d..9e9c829 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -24,14 +24,12 @@
 import android.view.Surface;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiDevice;
 
 import org.junit.AfterClass;
 import org.junit.FixMethodOrder;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
@@ -43,8 +41,6 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 140856143)
-@Ignore("Waiting bug feedback")
 public class SplitScreenToLauncherTest extends FlickerTestBase {
 
     public SplitScreenToLauncherTest() {
@@ -95,15 +91,6 @@
                 .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
     }
 
-    @Test
-    public void checkVisibility_dividerWindowBecomesInVisible() {
-        checkResults(result -> WmTraceSubject.assertThat(result)
-                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
-                .then()
-                .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
-                .forAllEntries());
-    }
-
     @AfterClass
     public static void teardown() {
         UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 9e3aeef..5a29c2c 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -114,6 +114,7 @@
     @Mock private INetd mNetdService;
     @Mock private PackageManagerInternal mMockPmi;
     @Mock private UserManager mUserManager;
+    @Mock private PermissionMonitor.Dependencies mDeps;
 
     private PermissionMonitor mPermissionMonitor;
 
@@ -128,7 +129,7 @@
                         new UserInfo(MOCK_USER2, "", 0),
                 }));
 
-        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService));
+        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mMockPmi);
@@ -283,14 +284,14 @@
 
     @Test
     public void testHasRestrictedNetworkPermissionSystemUid() {
-        doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
+        doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
         assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
 
-        doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
+        doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
         assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
         assertFalse(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));