Merge "Use a SLEEP transition to trigger failsafe animation cancelling" into udc-dev
diff --git a/OWNERS b/OWNERS
index 7bea58b..dad8bfe 100644
--- a/OWNERS
+++ b/OWNERS
@@ -30,6 +30,7 @@
 # Support bulk translation updates
 per-file */res*/values*/*.xml = byi@google.com, delphij@google.com
 
+per-file **.bp,**.mk = hansson@google.com
 per-file *.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
 per-file Android.mk = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
 per-file framework-jarjar-rules.txt = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 79a2659..5de1172 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -5277,6 +5277,8 @@
             pw.print("  mScreenLocked="); pw.println(mScreenLocked);
             pw.print("  mNetworkConnected="); pw.println(mNetworkConnected);
             pw.print("  mCharging="); pw.println(mCharging);
+            pw.print("  activeEmergencyCall=");
+            pw.println(mEmergencyCallListener.isEmergencyCallActive());
             if (mConstraints.size() != 0) {
                 pw.println("  mConstraints={");
                 for (int i = 0; i < mConstraints.size(); i++) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 53dcd75..ac63513 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10333,7 +10333,7 @@
     field public static final int BIND_AUTO_CREATE = 1; // 0x1
     field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
     field public static final int BIND_EXTERNAL_SERVICE = -2147483648; // 0x80000000
-    field public static final long BIND_EXTERNAL_SERVICE_LONG = -9223372036854775808L; // 0x8000000000000000L
+    field public static final long BIND_EXTERNAL_SERVICE_LONG = 4611686018427387904L; // 0x4000000000000000L
     field public static final int BIND_IMPORTANT = 64; // 0x40
     field public static final int BIND_INCLUDE_CAPABILITIES = 4096; // 0x1000
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
@@ -10441,7 +10441,6 @@
   }
 
   public static final class Context.BindServiceFlags {
-    method public long getValue();
     method @NonNull public static android.content.Context.BindServiceFlags of(long);
   }
 
@@ -39766,6 +39765,7 @@
     method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender);
     method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull android.view.autofill.AutofillId, @Nullable android.service.autofill.Field);
     method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull String, @NonNull android.service.autofill.Field);
+    method @NonNull public android.service.autofill.Dataset.Builder setFieldForAllHints(@NonNull android.service.autofill.Field);
     method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String);
     method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
     method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
@@ -40549,7 +40549,7 @@
     method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder addCreateEntry(@NonNull android.service.credentials.CreateEntry);
     method @NonNull public android.service.credentials.BeginCreateCredentialResponse build();
     method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setCreateEntries(@NonNull java.util.List<android.service.credentials.CreateEntry>);
-    method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setRemoteCreateEntry(@Nullable android.service.credentials.RemoteEntry);
+    method @NonNull @RequiresPermission("android.permission.PROVIDE_REMOTE_CREDENTIALS") public android.service.credentials.BeginCreateCredentialResponse.Builder setRemoteCreateEntry(@Nullable android.service.credentials.RemoteEntry);
   }
 
   public class BeginGetCredentialOption implements android.os.Parcelable {
@@ -40598,7 +40598,7 @@
     method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setActions(@NonNull java.util.List<android.service.credentials.Action>);
     method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setAuthenticationActions(@NonNull java.util.List<android.service.credentials.Action>);
     method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setCredentialEntries(@NonNull java.util.List<android.service.credentials.CredentialEntry>);
-    method @NonNull public android.service.credentials.BeginGetCredentialResponse.Builder setRemoteCredentialEntry(@Nullable android.service.credentials.RemoteEntry);
+    method @NonNull @RequiresPermission("android.permission.PROVIDE_REMOTE_CREDENTIALS") public android.service.credentials.BeginGetCredentialResponse.Builder setRemoteCredentialEntry(@Nullable android.service.credentials.RemoteEntry);
   }
 
   public final class CallingAppInfo implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 05f31a5..a8a48b7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -254,7 +254,7 @@
     field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
     field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
     field public static final String PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE = "android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE";
-    field public static final String PROVIDE_HYBRID_CREDENTIAL_SERVICE = "android.permission.PROVIDE_HYBRID_CREDENTIAL_SERVICE";
+    field public static final String PROVIDE_REMOTE_CREDENTIALS = "android.permission.PROVIDE_REMOTE_CREDENTIALS";
     field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
     field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
     field public static final String PROVISION_DEMO_DEVICE = "android.permission.PROVISION_DEMO_DEVICE";
@@ -3362,7 +3362,7 @@
   }
 
   public static final class VirtualSensorConfig.Builder {
-    ctor public VirtualSensorConfig.Builder(int, @NonNull String);
+    ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 777faa7..a9ae225 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2629,8 +2629,13 @@
     method @Nullable public android.content.IntentSender getAuthentication();
     method @Nullable public java.util.ArrayList<java.lang.String> getAutofillDatatypes();
     method @Nullable public android.content.ClipData getFieldContent();
+    method @Nullable public android.widget.RemoteViews getFieldDialogPresentation(int);
     method @Nullable public java.util.ArrayList<android.view.autofill.AutofillId> getFieldIds();
+    method @Nullable public android.service.autofill.InlinePresentation getFieldInlinePresentation(int);
+    method @Nullable public android.service.autofill.InlinePresentation getFieldInlineTooltipPresentation(int);
+    method @Nullable public android.widget.RemoteViews getFieldPresentation(int);
     method @Nullable public java.util.ArrayList<android.view.autofill.AutofillValue> getFieldValues();
+    method @Nullable public android.service.autofill.Dataset.DatasetFieldFilter getFilter(int);
     method @Nullable public String getId();
     method public boolean isEmpty();
   }
@@ -2639,6 +2644,13 @@
     method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData);
   }
 
+  public static final class Dataset.DatasetFieldFilter implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public java.util.regex.Pattern getPattern();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.Dataset.DatasetFieldFilter> CREATOR;
+  }
+
   public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
   }
@@ -3419,6 +3431,7 @@
   }
 
   public final class AutofillManager {
+    field public static final String ANY_HINT = "any";
     field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0
     field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
     field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index ffbdff8..401e754 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -17,6 +17,7 @@
 package android.companion.virtual.sensor;
 
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -167,7 +168,10 @@
          * @param name The name of the sensor. Must be unique among all sensors with the same type
          *             that belong to the same virtual device.
          */
-        public Builder(int type, @NonNull String name) {
+        public Builder(@IntRange(from = 1) int type, @NonNull String name) {
+            if (type <= 0) {
+                throw new IllegalArgumentException("Virtual sensor type must be positive");
+            }
             mType = type;
             mName = Objects.requireNonNull(name);
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a412560..36f7ff5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -316,10 +316,12 @@
             BIND_ALLOW_ACTIVITY_STARTS,
             BIND_INCLUDE_CAPABILITIES,
             BIND_SHARED_ISOLATED_PROCESS,
-            // Intentionally not included, because it'd cause sign-extension.
+            // Intentionally not include BIND_EXTERNAL_SERVICE, because it'd cause sign-extension.
             // This would allow Android Studio to show a warning, if someone tries to use
             // BIND_EXTERNAL_SERVICE BindServiceFlags.
-            BIND_EXTERNAL_SERVICE_LONG
+            BIND_EXTERNAL_SERVICE_LONG,
+            // Make sure no flag uses the sign bit (most significant bit) of the long integer,
+            // to avoid future confusion.
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BindServiceFlagsLongBits {}
@@ -338,6 +340,7 @@
 
         /**
          * @return Return flags in 64 bits long integer.
+         * @hide
          */
         public long getValue() {
             return mValue;
@@ -678,13 +681,11 @@
      */
     public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
 
-
     /**
      * Works in the same way as {@link #BIND_EXTERNAL_SERVICE}, but it's defined as a (@code long)
      * value that is compatible to {@link BindServiceFlags}.
      */
-    public static final long BIND_EXTERNAL_SERVICE_LONG = 0x8000_0000_0000_0000L;
-
+    public static final long BIND_EXTERNAL_SERVICE_LONG = 1L << 62;
 
     /**
      * These bind flags reduce the strength of the binding such that we shouldn't
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index e2af9b0..b749d69 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -420,6 +420,7 @@
     // Guarded by NfcAdapter.class
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
+    static boolean sHasCeFeature;
 
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -616,11 +617,13 @@
             PackageManager pm;
             pm = context.getPackageManager();
             sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
-            boolean hasHceFeature =
+            sHasCeFeature =
                     pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
-                    || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
+                    || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)
+                    || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)
+                    || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE);
             /* is this device meant to have NFC */
-            if (!sHasNfcFeature && !hasHceFeature) {
+            if (!sHasNfcFeature && !sHasCeFeature) {
                 Log.v(TAG, "this device does not have NFC support");
                 throw new UnsupportedOperationException();
             }
@@ -643,7 +646,7 @@
                     throw new UnsupportedOperationException();
                 }
             }
-            if (hasHceFeature) {
+            if (sHasCeFeature) {
                 try {
                     sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface();
                 } catch (RemoteException e) {
@@ -1669,7 +1672,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean enableSecureNfc(boolean enable) {
-        if (!sHasNfcFeature) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
         try {
@@ -1694,10 +1697,13 @@
      * Checks if the device supports Secure NFC functionality.
      *
      * @return True if device supports Secure NFC, false otherwise
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
      */
     public boolean isSecureNfcSupported() {
-        if (!sHasNfcFeature) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
         try {
@@ -1723,11 +1729,14 @@
      * such as their relative positioning on the device.
      *
      * @return Information on the nfc antenna(s) on the device.
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
      */
     @Nullable
     public NfcAntennaInfo getNfcAntennaInfo() {
-        if (!sHasNfcFeature) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
         try {
@@ -1752,12 +1761,15 @@
      * Checks Secure NFC feature is enabled.
      *
      * @return True if Secure NFC is enabled, false otherwise
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
      * @throws UnsupportedOperationException if device doesn't support
      *         Secure NFC functionality. {@link #isSecureNfcSupported}
      */
     public boolean isSecureNfcEnabled() {
-        if (!sHasNfcFeature) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
         try {
@@ -2071,14 +2083,17 @@
      * always on.
      * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
      * disabled), if false the NFCC will follow completely the Nfc adapter state.
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
      * @return void
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean setControllerAlwaysOn(boolean value) {
-        if (!sHasNfcFeature) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
         try {
@@ -2103,7 +2118,10 @@
      * Checks NFC controller always on feature is enabled.
      *
      * @return True if NFC controller always on is enabled, false otherwise
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
      * @hide
      */
     @SystemApi
@@ -2131,13 +2149,16 @@
      * Checks if the device supports NFC controller always on functionality.
      *
      * @return True if device supports NFC controller always on, false otherwise
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+     * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+     * are unavailable
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOnSupported() {
-        if (!sHasNfcFeature) {
+        if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
         try {
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index d943bf9..0ef8bb64 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -29,6 +29,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.widget.RemoteViews;
 
@@ -283,24 +284,28 @@
     }
 
     /** @hide */
-    public RemoteViews getFieldPresentation(int index) {
+    @TestApi
+    public @Nullable RemoteViews getFieldPresentation(int index) {
         final RemoteViews customPresentation = mFieldPresentations.get(index);
         return customPresentation != null ? customPresentation : mPresentation;
     }
 
     /** @hide */
-    public RemoteViews getFieldDialogPresentation(int index) {
+    @TestApi
+    public @Nullable RemoteViews getFieldDialogPresentation(int index) {
         final RemoteViews customPresentation = mFieldDialogPresentations.get(index);
         return customPresentation != null ? customPresentation : mDialogPresentation;
     }
 
     /** @hide */
+    @TestApi
     public @Nullable InlinePresentation getFieldInlinePresentation(int index) {
         final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index);
         return inlinePresentation != null ? inlinePresentation : mInlinePresentation;
     }
 
     /** @hide */
+    @TestApi
     public @Nullable InlinePresentation getFieldInlineTooltipPresentation(int index) {
         final InlinePresentation inlineTooltipPresentation =
                 mFieldInlineTooltipPresentations.get(index);
@@ -309,6 +314,7 @@
     }
 
     /** @hide */
+    @TestApi
     public @Nullable DatasetFieldFilter getFilter(int index) {
         return mFieldFilters.get(index);
     }
@@ -389,6 +395,9 @@
         if (mAuthentication != null) {
             builder.append(", hasAuthentication");
         }
+        if (mAutofillDatatypes != null) {
+            builder.append(", autofillDatatypes=").append(mAutofillDatatypes);
+        }
         return builder.append(']').toString();
     }
 
@@ -1090,8 +1099,7 @@
          *
          * @return this builder.
          */
-        public @NonNull Dataset.Builder setField(
-                @NonNull String hint, @NonNull Field field) {
+        public @NonNull Dataset.Builder setField(@NonNull String hint, @NonNull Field field) {
             throwIfDestroyed();
 
             final DatasetFieldFilter filter = field.getDatasetFieldFilter();
@@ -1111,6 +1119,23 @@
         }
 
         /**
+         * Adds a field to this Dataset that is relevant to all applicable hints. This is used to
+         * provide field information when autofill with platform detections is enabled.
+         * Platform detections are on when receiving a populated list from
+         * FillRequest#getHints().
+         *
+         * @param field the fill information about the field.
+         *
+         * @throws IllegalStateException if {@link #build()} was already called
+         * or this builder also contains AutofillId information
+         *
+         * @return this builder.
+         */
+        public @NonNull Dataset.Builder setFieldForAllHints(@NonNull Field field) {
+            return setField(AutofillManager.ANY_HINT, field);
+        }
+
+        /**
          * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an
          * {@link InlinePresentation} to visualize it as an inline suggestion.
          *
@@ -1304,7 +1329,7 @@
                     parcel.createTypedArrayList(InlinePresentation.CREATOR);
             final ArrayList<DatasetFieldFilter> filters =
                     parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
-            final ArrayList<String> datatypes =
+            final ArrayList<String> autofillDatatypes =
                     parcel.createStringArrayList();
             final ClipData fieldContent = parcel.readParcelable(null,
                     android.content.ClipData.class);
@@ -1341,9 +1366,9 @@
             }
             final int inlinePresentationsSize = inlinePresentations.size();
 
-            if (ids.size() == 0 && datatypes.size() > 0) {
-                for (int i = 0; i < ids.size(); i++) {
-                    final String datatype = datatypes.get(i);
+            if (ids.size() == 0 && autofillDatatypes.size() > 0) {
+                for (int i = 0; i < autofillDatatypes.size(); i++) {
+                    final String datatype = autofillDatatypes.get(i);
                     final AutofillValue value = values.get(i);
                     final RemoteViews fieldPresentation = presentations.get(i);
                     final RemoteViews fieldDialogPresentation = dialogPresentations.get(i);
@@ -1393,8 +1418,10 @@
      *
      * @hide
      */
+    @TestApi
     public static final class DatasetFieldFilter implements Parcelable {
 
+        /** @hide */
         @Nullable
         public final Pattern pattern;
 
@@ -1402,6 +1429,10 @@
             this.pattern = pattern;
         }
 
+        public @Nullable Pattern getPattern() {
+            return pattern;
+        }
+
         @Override
         public String toString() {
             if (!sDebug) return super.toString();
@@ -1416,7 +1447,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel parcel, int flags) {
+        public void writeToParcel(@NonNull Parcel parcel, int flags) {
             parcel.writeSerializable(pattern);
         }
 
diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
index eebd31c..cd53cb6 100644
--- a/core/java/android/service/credentials/BeginCreateCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
@@ -16,8 +16,10 @@
 
 package android.service.credentials;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -137,7 +139,17 @@
          * result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the
          * {@link CredentialProviderService#EXTRA_CREATE_CREDENTIAL_RESPONSE} key should be populated
          * with a {@link android.credentials.CreateCredentialResponse} object.
+         *
+         * <p> Note that as a provider service you will only be able to set a remote entry if :
+         * - Provider service possesses the
+         * {@link Manifest.permission.PROVIDE_REMOTE_CREDENTIALS} permission.
+         * - Provider service is configured as the provider that can provide remote entries.
+         *
+         * If the above conditions are not met, setting back {@link BeginCreateCredentialResponse}
+         * on the callback from {@link CredentialProviderService#onBeginCreateCredential}
+         * will throw a {@link SecurityException}.
          */
+        @RequiresPermission(Manifest.permission.PROVIDE_REMOTE_CREDENTIALS)
         public @NonNull Builder setRemoteCreateEntry(@Nullable RemoteEntry remoteCreateEntry) {
             mRemoteCreateEntry = remoteCreateEntry;
             return this;
diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java
index 97f5079..e25b686 100644
--- a/core/java/android/service/credentials/BeginGetCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java
@@ -16,8 +16,10 @@
 
 package android.service.credentials;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -154,7 +156,17 @@
          * result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the
          * {@link CredentialProviderService#EXTRA_GET_CREDENTIAL_RESPONSE} key should be populated
          * with a {@link android.credentials.Credential} object.
+         *
+         * <p> Note that as a provider service you will only be able to set a remote entry if :
+         * - Provider service possesses the
+         * {@link Manifest.permission.PROVIDE_REMOTE_CREDENTIALS} permission.
+         * - Provider service is configured as the provider that can provide remote entries.
+         *
+         * If the above conditions are not met, setting back {@link BeginGetCredentialResponse}
+         * on the callback from {@link CredentialProviderService#onBeginGetCredential} will
+         * throw a {@link SecurityException}.
          */
+        @RequiresPermission(Manifest.permission.PROVIDE_REMOTE_CREDENTIALS)
         public @NonNull Builder setRemoteCredentialEntry(@Nullable RemoteEntry
                 remoteCredentialEntry) {
             mRemoteCredentialEntry = remoteCredentialEntry;
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index d737f6b..e88474d 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
+import android.Manifest;
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
@@ -218,6 +219,11 @@
                             GetCredentialException>() {
                         @Override
                         public void onResult(BeginGetCredentialResponse result) {
+                            // If provider service does not possess the HYBRID permission, this
+                            // check will throw an exception in the provider process.
+                            if (result.getRemoteCredentialEntry() != null) {
+                                enforceRemoteEntryPermission();
+                            }
                             try {
                                 callback.onSuccess(result);
                             } catch (RemoteException e) {
@@ -236,6 +242,15 @@
             ));
             return transport;
         }
+        private void enforceRemoteEntryPermission() {
+            String permission =
+                    Manifest.permission.PROVIDE_REMOTE_CREDENTIALS;
+            getApplicationContext().enforceCallingOrSelfPermission(
+                    permission,
+                    String.format("Provider must have %s, in order to set a "
+                            + "remote entry", permission)
+            );
+        }
 
         @Override
         public ICancellationSignal onBeginCreateCredential(BeginCreateCredentialRequest request,
@@ -253,6 +268,11 @@
                             BeginCreateCredentialResponse, CreateCredentialException>() {
                         @Override
                         public void onResult(BeginCreateCredentialResponse result) {
+                            // If provider service does not possess the HYBRID permission, this
+                            // check will throw an exception in the provider process.
+                            if (result.getRemoteCreateEntry() != null) {
+                                enforceRemoteEntryPermission();
+                            }
                             try {
                                 callback.onSuccess(result);
                             } catch (RemoteException e) {
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 07ac597..b4675e0 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -81,7 +81,10 @@
     // GC'd while the native peer of the receiver is using them.
     private MessageQueue mMessageQueue;
 
+    private final VsyncEventData mVsyncEventData = new VsyncEventData();
+
     private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
+            WeakReference<VsyncEventData> vsyncEventData,
             MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle);
     private static native long nativeGetDisplayEventReceiverFinalizer();
     @FastNative
@@ -124,7 +127,9 @@
         }
 
         mMessageQueue = looper.getQueue();
-        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
+        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this),
+                new WeakReference<VsyncEventData>(mVsyncEventData),
+                mMessageQueue,
                 vsyncSource, eventRegistration, layerHandle);
         mFreeNativeResources = sNativeAllocationRegistry.registerNativeAllocation(this,
                 mReceiverPtr);
@@ -142,9 +147,6 @@
     }
 
     static final class VsyncEventData {
-
-        static final FrameTimeline[] INVALID_FRAME_TIMELINES =
-                {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)};
         // The amount of frame timeline choices.
         // Must be in sync with VsyncEventData::kFrameTimelinesLength in
         // frameworks/native/libs/gui/include/gui/VsyncEventData.h. If they do not match, a runtime
@@ -152,6 +154,10 @@
         static final int FRAME_TIMELINES_LENGTH = 7;
 
         public static class FrameTimeline {
+            FrameTimeline() {}
+
+            // Called from native code.
+            @SuppressWarnings("unused")
             FrameTimeline(long vsyncId, long expectedPresentationTime, long deadline) {
                 this.vsyncId = vsyncId;
                 this.expectedPresentationTime = expectedPresentationTime;
@@ -160,14 +166,14 @@
 
             // The frame timeline vsync id, used to correlate a frame
             // produced by HWUI with the timeline data stored in Surface Flinger.
-            public final long vsyncId;
+            public long vsyncId = FrameInfo.INVALID_VSYNC_ID;
 
             // The frame timestamp for when the frame is expected to be presented.
-            public final long expectedPresentationTime;
+            public long expectedPresentationTime = Long.MAX_VALUE;
 
             // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
             // allotted for the frame to be completed.
-            public final long deadline;
+            public long deadline = Long.MAX_VALUE;
         }
 
         /**
@@ -175,11 +181,18 @@
          * {@link FrameInfo#VSYNC} to the current vsync in case Choreographer callback was heavily
          * delayed by the app.
          */
-        public final long frameInterval;
+        public long frameInterval = -1;
 
         public final FrameTimeline[] frameTimelines;
 
-        public final int preferredFrameTimelineIndex;
+        public int preferredFrameTimelineIndex = 0;
+
+        VsyncEventData() {
+            frameTimelines = new FrameTimeline[FRAME_TIMELINES_LENGTH];
+            for (int i = 0; i < frameTimelines.length; i++) {
+                frameTimelines[i] = new FrameTimeline();
+            }
+        }
 
         // Called from native code.
         @SuppressWarnings("unused")
@@ -190,12 +203,6 @@
             this.frameInterval = frameInterval;
         }
 
-        VsyncEventData() {
-            this.frameInterval = -1;
-            this.frameTimelines = INVALID_FRAME_TIMELINES;
-            this.preferredFrameTimelineIndex = 0;
-        }
-
         public FrameTimeline preferredFrameTimeline() {
             return frameTimelines[preferredFrameTimelineIndex];
         }
@@ -299,9 +306,8 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
-    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
-            VsyncEventData vsyncEventData) {
-        onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
+    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
+        onVsync(timestampNanos, physicalDisplayId, frame, mVsyncEventData);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index a208cb3..21fe87f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -823,6 +823,11 @@
     /** @hide */
     public final void destroy() {
         mDestroyed = true;
+        onDestroy();
+    }
+
+    /** @hide */
+    protected void onDestroy() {
     }
 
     /** @hide */
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index aef0e65..ab9f492 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -301,6 +301,14 @@
     public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
             "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
 
+    /**
+     * Autofill Hint to indicate that it can match any field.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String ANY_HINT = "any";
+
     private static final String SESSION_ID_TAG = "android:sessionId";
     private static final String STATE_TAG = "android:state";
     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index c41b822..9b9e010 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -295,6 +295,7 @@
     private boolean mClosingActionMenu;
 
     private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
+    private int mAudioMode = AudioManager.MODE_NORMAL;
     private MediaController mMediaController;
 
     private AudioManager mAudioManager;
@@ -317,6 +318,8 @@
         }
     };
 
+    private AudioManager.OnModeChangedListener mOnModeChangedListener;
+
     private Transition mEnterTransition = null;
     private Transition mReturnTransition = USE_DEFAULT_TRANSITION;
     private Transition mExitTransition = null;
@@ -1944,7 +1947,7 @@
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 // If we have a session and no active phone call send it the volume command,
                 // otherwise use the suggested stream.
-                if (mMediaController != null && !isActivePhoneCallKnown()) {
+                if (mMediaController != null && !isActivePhoneCallOngoing()) {
                     getMediaSessionManager().dispatchVolumeKeyEventToSessionAsSystemService(event,
                             mMediaController.getSessionToken());
                 } else {
@@ -1995,16 +1998,9 @@
         return false;
     }
 
-    private boolean isActivePhoneCallKnown() {
-        boolean isActivePhoneCallKnown = false;
-        AudioManager audioManager =
-                (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
-        int audioManagerMode = audioManager.getMode();
-        if (audioManagerMode == AudioManager.MODE_IN_CALL
-                || audioManagerMode == AudioManager.MODE_IN_COMMUNICATION) {
-            isActivePhoneCallKnown = true;
-        }
-        return isActivePhoneCallKnown;
+    private boolean isActivePhoneCallOngoing() {
+        return mAudioMode == AudioManager.MODE_IN_CALL
+                || mAudioMode == AudioManager.MODE_IN_COMMUNICATION;
     }
 
     private KeyguardManager getKeyguardManager() {
@@ -2331,6 +2327,14 @@
         }
     }
 
+    @Override
+    protected void onDestroy() {
+        if (mOnModeChangedListener != null) {
+            getAudioManager().removeOnModeChangedListener(mOnModeChangedListener);
+            mOnModeChangedListener = null;
+        }
+    }
+
     private class PanelMenuPresenterCallback implements MenuPresenter.Callback {
         @Override
         public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
@@ -3229,6 +3233,15 @@
     @Override
     public void setMediaController(MediaController controller) {
         mMediaController = controller;
+        if (controller != null && mOnModeChangedListener == null) {
+            mAudioMode = getAudioManager().getMode();
+            mOnModeChangedListener = mode -> mAudioMode = mode;
+            getAudioManager().addOnModeChangedListener(getContext().getMainExecutor(),
+                    mOnModeChangedListener);
+        } else if (mOnModeChangedListener != null) {
+            getAudioManager().removeOnModeChangedListener(mOnModeChangedListener);
+            mOnModeChangedListener = null;
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
index f2b0544..abe6c7c 100644
--- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
@@ -72,11 +72,13 @@
     private static final String TAG = "ProtoLog";
     private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
     static final String PROTOLOG_VERSION = "1.0.0";
+    private static final int DEFAULT_PER_CHUNK_SIZE = 0;
 
     private final File mLogFile;
     private final String mViewerConfigFilename;
     private final TraceBuffer mBuffer;
     protected final ProtoLogViewerConfigReader mViewerConfig;
+    private final int mPerChunkSize;
 
     private boolean mProtoLogEnabled;
     private boolean mProtoLogEnabledLockFree;
@@ -160,7 +162,7 @@
             return;
         }
         try {
-            ProtoOutputStream os = new ProtoOutputStream();
+            ProtoOutputStream os = new ProtoOutputStream(mPerChunkSize);
             long token = os.start(LOG);
             os.write(MESSAGE_HASH, messageHash);
             os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
@@ -219,10 +221,16 @@
 
     public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
             ProtoLogViewerConfigReader viewerConfig) {
+        this(file, viewerConfigFilename, bufferCapacity, viewerConfig, DEFAULT_PER_CHUNK_SIZE);
+    }
+
+    public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
+            ProtoLogViewerConfigReader viewerConfig, int perChunkSize) {
         mLogFile = file;
         mBuffer = new TraceBuffer(bufferCapacity);
         mViewerConfigFilename = viewerConfigFilename;
         mViewerConfig = viewerConfig;
+        mPerChunkSize = perChunkSize;
     }
 
     /**
@@ -368,7 +376,7 @@
         try {
             long offset =
                     (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
-            ProtoOutputStream proto = new ProtoOutputStream();
+            ProtoOutputStream proto = new ProtoOutputStream(mPerChunkSize);
             proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
             proto.write(VERSION, PROTOLOG_VERSION);
             proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 353c6c0..527cfdd 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -30,6 +30,7 @@
     private static final int BUFFER_CAPACITY = 1024 * 1024;
     private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.winscope";
     private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
+    private static final int PER_CHUNK_SIZE = 1024;
 
     private static ProtoLogImpl sServiceInstance = null;
 
@@ -94,7 +95,10 @@
     public static synchronized ProtoLogImpl getSingleInstance() {
         if (sServiceInstance == null) {
             sServiceInstance = new ProtoLogImpl(
-                    new File(LOG_FILENAME), BUFFER_CAPACITY, new ProtoLogViewerConfigReader());
+                    new File(LOG_FILENAME)
+                    , BUFFER_CAPACITY
+                    , new ProtoLogViewerConfigReader()
+                    , PER_CHUNK_SIZE);
         }
         return sServiceInstance;
     }
@@ -105,8 +109,8 @@
     }
 
     public ProtoLogImpl(File logFile, int bufferCapacity,
-            ProtoLogViewerConfigReader viewConfigReader) {
-        super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader);
-    }
+            ProtoLogViewerConfigReader viewConfigReader, int perChunkSize) {
+        super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader, perChunkSize);
+  }
 }
 
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index b09a9c3..739055e 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -50,12 +50,22 @@
 
     struct {
         jclass clazz;
+
         jmethodID init;
+
+        jfieldID vsyncId;
+        jfieldID expectedPresentationTime;
+        jfieldID deadline;
     } frameTimelineClassInfo;
 
     struct {
         jclass clazz;
+
         jmethodID init;
+
+        jfieldID frameInterval;
+        jfieldID preferredFrameTimelineIndex;
+        jfieldID frameTimelines;
     } vsyncEventDataClassInfo;
 
 } gDisplayEventReceiverClassInfo;
@@ -63,7 +73,7 @@
 
 class NativeDisplayEventReceiver : public DisplayEventDispatcher {
 public:
-    NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+    NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, jobject vsyncEventDataWeak,
                                const sp<MessageQueue>& messageQueue, jint vsyncSource,
                                jint eventRegistration, jlong layerHandle);
 
@@ -74,6 +84,7 @@
 
 private:
     jobject mReceiverWeakGlobal;
+    jobject mVsyncEventDataWeakGlobal;
     sp<MessageQueue> mMessageQueue;
 
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
@@ -87,6 +98,7 @@
 };
 
 NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+                                                       jobject vsyncEventDataWeak,
                                                        const sp<MessageQueue>& messageQueue,
                                                        jint vsyncSource, jint eventRegistration,
                                                        jlong layerHandle)
@@ -98,6 +110,7 @@
                                                           reinterpret_cast<IBinder*>(layerHandle))
                                                 : nullptr),
         mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
+        mVsyncEventDataWeakGlobal(env->NewGlobalRef(vsyncEventDataWeak)),
         mMessageQueue(messageQueue) {
     ALOGV("receiver %p ~ Initializing display event receiver.", this);
 }
@@ -144,12 +157,39 @@
     JNIEnv* env = AndroidRuntime::getJNIEnv();
 
     ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
-    if (receiverObj.get()) {
+    ScopedLocalRef<jobject> vsyncEventDataObj(env, GetReferent(env, mVsyncEventDataWeakGlobal));
+    if (receiverObj.get() && vsyncEventDataObj.get()) {
         ALOGV("receiver %p ~ Invoking vsync handler.", this);
 
-        jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData);
+        env->SetIntField(vsyncEventDataObj.get(),
+                         gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo
+                                 .preferredFrameTimelineIndex,
+                         vsyncEventData.preferredFrameTimelineIndex);
+        env->SetLongField(vsyncEventDataObj.get(),
+                          gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval,
+                          vsyncEventData.frameInterval);
+
+        jobjectArray frameTimelinesObj = reinterpret_cast<jobjectArray>(
+                env->GetObjectField(vsyncEventDataObj.get(),
+                                    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo
+                                            .frameTimelines));
+        for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+            VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i];
+            jobject frameTimelineObj = env->GetObjectArrayElement(frameTimelinesObj, i);
+            env->SetLongField(frameTimelineObj,
+                              gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId,
+                              frameTimeline.vsyncId);
+            env->SetLongField(frameTimelineObj,
+                              gDisplayEventReceiverClassInfo.frameTimelineClassInfo
+                                      .expectedPresentationTime,
+                              frameTimeline.expectedPresentationTime);
+            env->SetLongField(frameTimelineObj,
+                              gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline,
+                              frameTimeline.deadlineTimestamp);
+        }
+
         env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
-                            timestamp, displayId.value, count, javaVsyncEventData);
+                            timestamp, displayId.value, count);
         ALOGV("receiver %p ~ Returned from vsync handler.", this);
     }
 
@@ -217,8 +257,9 @@
     mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
-static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
-                        jint vsyncSource, jint eventRegistration, jlong layerHandle) {
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak,
+                        jobject messageQueueObj, jint vsyncSource, jint eventRegistration,
+                        jlong layerHandle) {
     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
     if (messageQueue == NULL) {
         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
@@ -226,8 +267,8 @@
     }
 
     sp<NativeDisplayEventReceiver> receiver =
-            new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource,
-                                           eventRegistration, layerHandle);
+            new NativeDisplayEventReceiver(env, receiverWeak, vsyncEventDataWeak, messageQueue,
+                                           vsyncSource, eventRegistration, layerHandle);
     status_t status = receiver->initialize();
     if (status) {
         String8 message;
@@ -274,7 +315,9 @@
 
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
-        {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;IIJ)J",
+        {"nativeInit",
+         "(Ljava/lang/ref/WeakReference;Ljava/lang/ref/WeakReference;Landroid/os/"
+         "MessageQueue;IIJ)J",
          (void*)nativeInit},
         {"nativeGetDisplayEventReceiverFinalizer", "()J",
          (void*)nativeGetDisplayEventReceiverFinalizer},
@@ -291,8 +334,7 @@
     gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
 
     gDisplayEventReceiverClassInfo.dispatchVsync =
-            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
-                             "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V");
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
     gDisplayEventReceiverClassInfo.dispatchModeChanged =
@@ -318,6 +360,15 @@
     gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
                              "<init>", "(JJJ)V");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                            "vsyncId", "J");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.expectedPresentationTime =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                            "expectedPresentationTime", "J");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                            "deadline", "J");
 
     jclass vsyncEventDataClazz =
             FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData");
@@ -329,6 +380,17 @@
                              "([Landroid/view/"
                              "DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V");
 
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.preferredFrameTimelineIndex =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                            "preferredFrameTimelineIndex", "I");
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                            "frameInterval", "J");
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameTimelines =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                            "frameTimelines",
+                            "[Landroid/view/DisplayEventReceiver$VsyncEventData$FrameTimeline;");
+
     return res;
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b736230..5a0ac0f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4482,8 +4482,8 @@
     <!-- Allows an application to be able to store and retrieve credentials from a remote
          device.
          @hide @SystemApi -->
-    <permission android:name="android.permission.PROVIDE_HYBRID_CREDENTIAL_SERVICE"
-                android:protectionLevel="signature|privileged" />
+    <permission android:name="android.permission.PROVIDE_REMOTE_CREDENTIALS"
+                android:protectionLevel="signature|privileged|role" />
 
     <!-- ========================================= -->
     <!-- Permissions for special development tools -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 47d771f..07f3530 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4746,8 +4746,7 @@
     <string name="accessibility_shortcut_disabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned off.</string>
 
     <!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] -->
-    <string name="accessibility_shortcut_spoken_feedback">Press and hold both volume keys for three seconds to use
-        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string>
+    <string name="accessibility_shortcut_spoken_feedback">Release the volume keys. To turn on <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g>, press and hold both volume keys again for 3 seconds.</string>
 
     <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. [CHAR LIMIT=none]-->
     <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the accessibility button:</string>
diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
index 16ed3ef..17b064c 100644
--- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
+++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
@@ -26,6 +26,7 @@
 
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.Sensor;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
 
@@ -67,6 +68,24 @@
     }
 
     @Test
+    public void virtualSensorConfig_invalidName_throwsException() {
+        assertThrows(
+                NullPointerException.class,
+                () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, null));
+    }
+
+    @Test
+    public void virtualSensorConfig_invalidType_throwsException() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new VirtualSensorConfig.Builder(Sensor.TYPE_ALL, SENSOR_NAME));
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new VirtualSensorConfig.Builder(0, SENSOR_NAME));
+    }
+
+    @Test
     public void hardwareBufferDirectChannelTypeSupported_throwsException() {
         assertThrows(
                 IllegalArgumentException.class,
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index b6fc137..ccf8085 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -91,7 +91,7 @@
         mConfigsCacheGenerationStore = new MemoryIntArray(2);
         mConfigsCacheGenerationStore.set(0, 123);
         mConfigsCacheGenerationStore.set(1, 456);
-        mSettingsCacheGenerationStore = new MemoryIntArray(2);
+        mSettingsCacheGenerationStore = new MemoryIntArray(3);
         mSettingsCacheGenerationStore.set(0, 234);
         mSettingsCacheGenerationStore.set(1, 567);
         mConfigsStorage = new HashMap<>();
@@ -163,6 +163,10 @@
                     Bundle incomingBundle = invocationOnMock.getArgument(4);
                     String key = invocationOnMock.getArgument(3);
                     String value = incomingBundle.getString(Settings.NameValueTable.VALUE);
+                    boolean newSetting = false;
+                    if (!mSettingsStorage.containsKey(key)) {
+                        newSetting = true;
+                    }
                     mSettingsStorage.put(key, value);
                     int currentGeneration;
                     // Different settings have different generation codes
@@ -173,12 +177,18 @@
                         currentGeneration = mSettingsCacheGenerationStore.get(1);
                         mSettingsCacheGenerationStore.set(1, ++currentGeneration);
                     }
+                    if (newSetting) {
+                        // Tracking the generation of all unset settings.
+                        // Increment when a new setting is inserted
+                        currentGeneration = mSettingsCacheGenerationStore.get(2);
+                        mSettingsCacheGenerationStore.set(2, ++currentGeneration);
+                    }
                     return null;
                 });
 
         // Returns the value corresponding to a setting, or null if the setting
-        // doesn't have a value stored for it. Returns the generation key if the value isn't null
-        // and the caller asked for the generation key.
+        // doesn't have a value stored for it. Returns the generation key
+        // if the caller asked for the generation key.
         when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()),
                 eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer(
                 invocationOnMock -> {
@@ -189,9 +199,15 @@
                     Bundle bundle = new Bundle();
                     bundle.putSerializable(Settings.NameValueTable.VALUE, value);
 
-                    if (value != null && incomingBundle.containsKey(
+                    if (incomingBundle.containsKey(
                             Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
-                        int index = key.equals(SETTING) ? 0 : 1;
+                        int index;
+                        if (value != null) {
+                            index = key.equals(SETTING) ? 0 : 1;
+                        } else {
+                            // special index for unset settings
+                            index = 2;
+                        }
                         bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
                                 mSettingsCacheGenerationStore);
                         bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
@@ -361,16 +377,38 @@
     }
 
     @Test
-    public void testCaching_nullSetting() throws Exception {
+    public void testCaching_unsetSetting() throws Exception {
         String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
         verify(mMockIContentProvider, times(1)).call(any(), any(),
                 eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
         assertThat(returnedValue).isNull();
 
         String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        // Empty list won't be cached
+        // The first unset setting's generation number is cached
+        verifyNoMoreInteractions(mMockIContentProvider);
+        assertThat(cachedValue).isNull();
+
+        String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
         verify(mMockIContentProvider, times(2)).call(any(), any(),
                 eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(cachedValue).isNull();
+        assertThat(returnedValue2).isNull();
+
+        String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING);
+        // The second unset setting's generation number is cached
+        verifyNoMoreInteractions(mMockIContentProvider);
+        assertThat(cachedValue2).isNull();
+
+        Settings.Secure.putString(mMockContentResolver, SETTING, "a");
+        // The generation for unset settings should have changed
+        returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
+        verify(mMockIContentProvider, times(3)).call(any(), any(),
+                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
+        assertThat(returnedValue2).isNull();
+
+        // The generation tracker for the first setting should have change because it's set now
+        returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
+        verify(mMockIContentProvider, times(4)).call(any(), any(),
+                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
+        assertThat(returnedValue).isEqualTo("a");
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 36c0cb6..852fae6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -61,6 +61,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.notification.NotificationListenerService;
@@ -129,6 +130,15 @@
     private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
     private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
 
+    // TODO(b/256873975) Should use proper flag when available to shell/launcher
+    /**
+     * Whether bubbles are showing in the bubble bar from launcher. This is only available
+     * on large screens and {@link BubbleController#isShowingAsBubbleBar()} should be used
+     * to check all conditions that indicate if the bubble bar is in use.
+     */
+    private static final boolean BUBBLE_BAR_ENABLED =
+            SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
+
     private final Context mContext;
     private final BubblesImpl mImpl = new BubblesImpl();
     private Bubbles.BubbleExpandListener mExpandListener;
@@ -155,9 +165,6 @@
 
     private final ShellExecutor mBackgroundExecutor;
 
-    // Whether or not we should show bubbles pinned at the bottom of the screen.
-    private boolean mIsBubbleBarEnabled;
-
     private BubbleLogger mLogger;
     private BubbleData mBubbleData;
     @Nullable private BubbleStackView mStackView;
@@ -540,10 +547,10 @@
         mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
     }
 
-    // TODO(b/256873975): Should pass this into the constructor once flags are available to shell.
-    /** Sets whether the bubble bar is enabled (i.e. bubbles pinned to bottom on large screens). */
-    public void setBubbleBarEnabled(boolean enabled) {
-        mIsBubbleBarEnabled = enabled;
+    /** Whether bubbles are showing in the bubble bar. */
+    public boolean isShowingAsBubbleBar() {
+        // TODO(b/269670598): should also check that we're in gesture nav
+        return BUBBLE_BAR_ENABLED && mBubblePositioner.isLargeScreen();
     }
 
     /** Whether this userId belongs to the current user. */
@@ -612,12 +619,6 @@
             mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation);
         }
 
-        if (mIsBubbleBarEnabled && mBubblePositioner.isLargeScreen()) {
-            mBubblePositioner.setUsePinnedLocation(true);
-        } else {
-            mBubblePositioner.setUsePinnedLocation(false);
-        }
-
         addToWindowManagerMaybe();
     }
 
@@ -1918,13 +1919,6 @@
         }
 
         @Override
-        public void setBubbleBarEnabled(boolean enabled) {
-            mMainExecutor.execute(() -> {
-                BubbleController.this.setBubbleBarEnabled(enabled);
-            });
-        }
-
-        @Override
         public void onNotificationPanelExpandedChanged(boolean expanded) {
             mMainExecutor.execute(
                     () -> BubbleController.this.onNotificationPanelExpandedChanged(expanded));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 6230d22..3fd0967 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -283,7 +283,7 @@
     }
 
     boolean isShowingOverflow() {
-        return mShowingOverflow && (isExpanded() || mPositioner.showingInTaskbar());
+        return mShowingOverflow && isExpanded();
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 07c5852..5ea2450 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -18,9 +18,6 @@
 
 import static android.view.View.LAYOUT_DIRECTION_RTL;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -39,8 +36,6 @@
 import com.android.launcher3.icons.IconNormalizer;
 import com.android.wm.shell.R;
 
-import java.lang.annotation.Retention;
-
 /**
  * Keeps track of display size, configuration, and specific bubble sizes. One place for all
  * placement and positioning calculations to refer to.
@@ -50,15 +45,6 @@
             ? "BubblePositioner"
             : BubbleDebugConfig.TAG_BUBBLES;
 
-    @Retention(SOURCE)
-    @IntDef({TASKBAR_POSITION_NONE, TASKBAR_POSITION_RIGHT, TASKBAR_POSITION_LEFT,
-            TASKBAR_POSITION_BOTTOM})
-    @interface TaskbarPosition {}
-    public static final int TASKBAR_POSITION_NONE = -1;
-    public static final int TASKBAR_POSITION_RIGHT = 0;
-    public static final int TASKBAR_POSITION_LEFT = 1;
-    public static final int TASKBAR_POSITION_BOTTOM = 2;
-
     /** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/
     public static final int NUM_VISIBLE_WHEN_RESTING = 2;
     /** Indicates a bubble's height should be the maximum available space. **/
@@ -108,15 +94,9 @@
     private int mOverflowHeight;
     private int mMinimumFlyoutWidthLargeScreen;
 
-    private PointF mPinLocation;
     private PointF mRestingStackPosition;
     private int[] mPaddings = new int[4];
 
-    private boolean mShowingInTaskbar;
-    private @TaskbarPosition int mTaskbarPosition = TASKBAR_POSITION_NONE;
-    private int mTaskbarIconSize;
-    private int mTaskbarSize;
-
     public BubblePositioner(Context context, WindowManager windowManager) {
         mContext = context;
         mWindowManager = windowManager;
@@ -153,27 +133,11 @@
                     + " insets: " + insets
                     + " isLargeScreen: " + mIsLargeScreen
                     + " isSmallTablet: " + mIsSmallTablet
-                    + " bounds: " + bounds
-                    + " showingInTaskbar: " + mShowingInTaskbar);
+                    + " bounds: " + bounds);
         }
         updateInternal(mRotation, insets, bounds);
     }
 
-    /**
-     * Updates position information to account for taskbar state.
-     *
-     * @param taskbarPosition which position the taskbar is displayed in.
-     * @param showingInTaskbar whether the taskbar is being shown.
-     */
-    public void updateForTaskbar(int iconSize,
-            @TaskbarPosition int taskbarPosition, boolean showingInTaskbar, int taskbarSize) {
-        mShowingInTaskbar = showingInTaskbar;
-        mTaskbarIconSize =  iconSize;
-        mTaskbarPosition = taskbarPosition;
-        mTaskbarSize = taskbarSize;
-        update();
-    }
-
     @VisibleForTesting
     public void updateInternal(int rotation, Insets insets, Rect bounds) {
         mRotation = rotation;
@@ -232,10 +196,6 @@
                 R.dimen.bubbles_flyout_min_width_large_screen);
 
         mMaxBubbles = calculateMaxBubbles();
-
-        if (mShowingInTaskbar) {
-            adjustForTaskbar();
-        }
     }
 
     /**
@@ -260,30 +220,6 @@
         return mDefaultMaxBubbles;
     }
 
-    /**
-     * Taskbar insets appear as navigationBar insets, however, unlike navigationBar this should
-     * not inset bubbles UI as bubbles floats above the taskbar. This adjust the available space
-     * and insets to account for the taskbar.
-     */
-    // TODO(b/171559950): When the insets are reported correctly we can remove this logic
-    private void adjustForTaskbar() {
-        // When bar is showing on edges... subtract that inset because we appear on top
-        if (mShowingInTaskbar && mTaskbarPosition != TASKBAR_POSITION_BOTTOM) {
-            WindowInsets metricInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
-            Insets navBarInsets = metricInsets.getInsetsIgnoringVisibility(
-                    WindowInsets.Type.navigationBars());
-            int newInsetLeft = mInsets.left;
-            int newInsetRight = mInsets.right;
-            if (mTaskbarPosition == TASKBAR_POSITION_LEFT) {
-                mPositionRect.left -= navBarInsets.left;
-                newInsetLeft -= navBarInsets.left;
-            } else if (mTaskbarPosition == TASKBAR_POSITION_RIGHT) {
-                mPositionRect.right += navBarInsets.right;
-                newInsetRight -= navBarInsets.right;
-            }
-            mInsets = Insets.of(newInsetLeft, mInsets.top, newInsetRight, mInsets.bottom);
-        }
-    }
 
     /**
      * @return a rect of available screen space accounting for orientation, system bars and cutouts.
@@ -327,14 +263,12 @@
      * to the left or right side.
      */
     public boolean showBubblesVertically() {
-        return isLandscape() || mShowingInTaskbar || mIsLargeScreen;
+        return isLandscape() || mIsLargeScreen;
     }
 
     /** Size of the bubble. */
     public int getBubbleSize() {
-        return (mShowingInTaskbar && mTaskbarIconSize > 0)
-                ? mTaskbarIconSize
-                : mBubbleSize;
+        return mBubbleSize;
     }
 
     /** The amount of padding at the top of the screen that the bubbles avoid when being placed. */
@@ -699,9 +633,6 @@
 
     /** The position the bubble stack should rest at when collapsed. */
     public PointF getRestingPosition() {
-        if (mPinLocation != null) {
-            return mPinLocation;
-        }
         if (mRestingStackPosition == null) {
             return getDefaultStartPosition();
         }
@@ -713,9 +644,6 @@
      * is being shown.
      */
     public PointF getDefaultStartPosition() {
-        if (mPinLocation != null) {
-            return mPinLocation;
-        }
         // Start on the left if we're in LTR, right otherwise.
         final boolean startOnLeft =
                 mContext.getResources().getConfiguration().getLayoutDirection()
@@ -730,7 +658,6 @@
                         1 /* default starts with 1 bubble */));
     }
 
-
     /**
      * Returns the region that the stack position must stay within. This goes slightly off the left
      * and right sides of the screen, below the status bar/cutout and above the navigation bar.
@@ -751,39 +678,6 @@
     }
 
     /**
-     * @return whether the bubble stack is pinned to the taskbar.
-     */
-    public boolean showingInTaskbar() {
-        return mShowingInTaskbar;
-    }
-
-    /**
-     * @return the taskbar position if set.
-     */
-    public int getTaskbarPosition() {
-        return mTaskbarPosition;
-    }
-
-    public int getTaskbarSize() {
-        return mTaskbarSize;
-    }
-
-    /**
-     * In some situations bubbles will be pinned to a specific onscreen location. This sets whether
-     * bubbles should be pinned or not.
-     */
-    public void setUsePinnedLocation(boolean usePinnedLocation) {
-        if (usePinnedLocation) {
-            mShowingInTaskbar = true;
-            mPinLocation = new PointF(mPositionRect.right - mBubbleSize,
-                    mPositionRect.bottom - mBubbleSize);
-        } else {
-            mPinLocation = null;
-            mShowingInTaskbar = false;
-        }
-    }
-
-    /**
      * Navigation bar has an area where system gestures can be started from.
      *
      * @return {@link Rect} for system navigation bar gesture zone
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index abe42ee..7d71089e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -680,8 +680,6 @@
 
                     // Re-show the expanded view if we hid it.
                     showExpandedViewIfNeeded();
-                } else if (mPositioner.showingInTaskbar()) {
-                    mStackAnimationController.snapStackBack();
                 } else {
                     // Fling the stack to the edge, and save whether or not it's going to end up on
                     // the left side of the screen.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 1753cda..4c0a93f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -273,11 +273,6 @@
      */
     void onUserRemoved(int removedUserId);
 
-    /**
-     * Sets whether bubble bar should be enabled or not.
-     */
-    void setBubbleBarEnabled(boolean enabled);
-
     /** Listener to find out about stack expansion / collapse events. */
     interface BubbleExpandListener {
         /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 0ee0ea6..5533842 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -417,23 +417,9 @@
     }
 
     /**
-     * Snaps the stack back to the previous resting position.
-     */
-    public void snapStackBack() {
-        if (mLayout == null) {
-            return;
-        }
-        PointF p = getStackPositionAlongNearestHorizontalEdge();
-        springStackAfterFling(p.x, p.y);
-    }
-
-    /**
      * Where the stack would be if it were snapped to the nearest horizontal edge (left or right).
      */
     public PointF getStackPositionAlongNearestHorizontalEdge() {
-        if (mPositioner.showingInTaskbar()) {
-            return mPositioner.getRestingPosition();
-        }
         final PointF stackPos = getStackPosition();
         final boolean onLeft = mLayout.isFirstChildXLeftOfCenter(stackPos.x);
         final RectF bounds = mPositioner.getAllowableStackPositionRegion(getBubbleCount());
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 7e9443b..c39a6db 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -32,6 +32,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.NullPointerException;
 import java.util.concurrent.Executor;
 
 /**
@@ -271,7 +272,12 @@
                 mExecutor.execute(() -> {
                     synchronized (mCallbackLock) {
                         if (mCallback != null) {
-                            mCallback.onFilterStatusChanged(this, status);
+                            try {
+                                mCallback.onFilterStatusChanged(this, status);
+                            }
+                            catch (NullPointerException e) {
+                                Log.d(TAG, "catch exception:" + e);
+                            }
                         }
                     }
                 });
@@ -285,7 +291,12 @@
                 mExecutor.execute(() -> {
                     synchronized (mCallbackLock) {
                         if (mCallback != null) {
-                            mCallback.onFilterEvent(this, events);
+                            try {
+                                mCallback.onFilterEvent(this, events);
+                            }
+                            catch (NullPointerException e) {
+                                Log.d(TAG, "catch exception:" + e);
+                            }
                         } else {
                             for (FilterEvent event : events) {
                                 if (event instanceof MediaEvent) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
index 1a3c0ab..47bf85d 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import android.text.format.Formatter
+import android.util.Log
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.produceState
@@ -30,18 +31,26 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 
+private const val TAG = "AppStorageSize"
+
 @Composable
 fun ApplicationInfo.getStorageSize(): State<String> {
     val context = LocalContext.current
     return produceState(initialValue = stringResource(R.string.summary_placeholder)) {
         withContext(Dispatchers.IO) {
-            value = Formatter.formatFileSize(context, calculateSizeBytes(context))
+            val sizeBytes = calculateSizeBytes(context)
+            value = if (sizeBytes != null) Formatter.formatFileSize(context, sizeBytes) else ""
         }
     }
 }
 
-private fun ApplicationInfo.calculateSizeBytes(context: Context): Long {
+private fun ApplicationInfo.calculateSizeBytes(context: Context): Long? {
     val storageStatsManager = context.storageStatsManager
-    val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle)
-    return stats.codeBytes + stats.dataBytes
+    return try {
+        val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle)
+        stats.codeBytes + stats.dataBytes
+    } catch (e: Exception) {
+        Log.w(TAG, "Failed to query stats: $e")
+        null
+    }
 }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
index fcacc34..e3af587 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
@@ -20,6 +20,7 @@
 import android.app.usage.StorageStatsManager
 import android.content.Context
 import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.NameNotFoundException
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -60,9 +61,11 @@
     @Before
     fun setUp() {
         whenever(context.storageStatsManager).thenReturn(storageStatsManager)
-        whenever(storageStatsManager.queryStatsForPackage(
-            app.storageUuid, app.packageName, app.userHandle
-        )).thenReturn(STATS)
+        whenever(
+            storageStatsManager.queryStatsForPackage(
+                app.storageUuid, app.packageName, app.userHandle
+            )
+        ).thenReturn(STATS)
     }
 
     @Test
@@ -78,6 +81,24 @@
         composeTestRule.waitUntil { storageSize.value == "120 B" }
     }
 
+    @Test
+    fun getStorageSize_throwException() {
+        var storageSize = stateOf("Computing")
+        whenever(
+            storageStatsManager.queryStatsForPackage(
+                app.storageUuid, app.packageName, app.userHandle
+            )
+        ).thenThrow(NameNotFoundException())
+
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                storageSize = app.getStorageSize()
+            }
+        }
+
+        composeTestRule.waitUntil { storageSize.value == "" }
+    }
+
     companion object {
         private val STATS = StorageStats().apply {
             codeBytes = 100
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index 7f3b0ff..db6cc1a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.settings;
 
+import android.annotation.NonNull;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.ArrayMap;
@@ -59,6 +60,10 @@
     // Maximum size of an individual backing store
     static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize();
 
+    // Use an empty string to track the generation number of all non-predefined, unset settings
+    // The generation number is only increased when a new non-predefined setting is inserted
+    private static final String DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS = "";
+
     public GenerationRegistry(Object lock) {
         mLock = lock;
     }
@@ -72,6 +77,10 @@
                 (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
         // Only store the prefix if the mutated setting is a config
         final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name;
+        incrementGenerationInternal(key, indexMapKey);
+    }
+
+    private void incrementGenerationInternal(int key, @NonNull String indexMapKey) {
         synchronized (mLock) {
             final MemoryIntArray backingStore = getBackingStoreLocked(key,
                     /* createIfNotExist= */ false);
@@ -87,7 +96,8 @@
                 final int generation = backingStore.get(index) + 1;
                 backingStore.set(index, generation);
                 if (DEBUG) {
-                    Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey
+                    Slog.i(LOG_TAG, "Incremented generation for "
+                            + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
                             + " key:" + SettingsState.keyToString(key)
                             + " at index:" + index);
                 }
@@ -98,6 +108,18 @@
         }
     }
 
+    // A new, non-predefined setting has been inserted, increment the tracking number for all unset
+    // settings
+    public void incrementGenerationForUnsetSettings(int key) {
+        final boolean isConfig =
+                (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
+        if (isConfig) {
+            // No need to track new settings for configs
+            return;
+        }
+        incrementGenerationInternal(key, DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
+    }
+
     /**
      *  Return the backing store's reference, the index and the current generation number
      *  of a cached setting. If it was not in the backing store, first create the entry in it before
@@ -124,8 +146,8 @@
                 bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
                         backingStore.get(index));
                 if (DEBUG) {
-                    Slog.i(LOG_TAG, "Exported index:" + index
-                            + " for setting:" + indexMapKey
+                    Slog.i(LOG_TAG, "Exported index:" + index + " for "
+                            + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
                             + " key:" + SettingsState.keyToString(key));
                 }
             } catch (IOException e) {
@@ -135,6 +157,10 @@
         }
     }
 
+    public void addGenerationDataForUnsetSettings(Bundle bundle, int key) {
+        addGenerationData(bundle, key, /* indexMapKey= */ DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
+    }
+
     public void onUserRemoved(int userId) {
         final int secureKey = SettingsState.makeKey(
                 SettingsState.SETTINGS_TYPE_SECURE, userId);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ba275eb..418011a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2327,11 +2327,15 @@
         result.putString(Settings.NameValueTable.VALUE,
                 (setting != null && !setting.isNull()) ? setting.getValue() : null);
 
-        if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
-            // Don't track generation for non-existent settings unless the name is predefined
-            synchronized (mLock) {
+        synchronized (mLock) {
+            if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
+                // Individual generation tracking for predefined settings even if they are unset
                 mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
                         SettingsState.makeKey(type, userId), name);
+            } else {
+                // All non-predefined, unset settings are tracked using the same generation number
+                mSettingsRegistry.mGenerationRegistry.addGenerationDataForUnsetSettings(result,
+                        SettingsState.makeKey(type, userId));
             }
         }
         return result;
@@ -2345,7 +2349,8 @@
         } else if (type == SETTINGS_TYPE_SYSTEM) {
             return sAllSystemSettings.contains(name);
         } else {
-            return false;
+            // Consider all config settings predefined because they are used by system apps only
+            return type == SETTINGS_TYPE_CONFIG;
         }
     }
 
@@ -2354,14 +2359,13 @@
         Bundle result = new Bundle();
         result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
         if (trackingGeneration) {
-            // Track generation even if the namespace is empty because this is for system apps
             synchronized (mLock) {
+                // Track generation even if namespace is empty because this is for system apps only
                 mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
-                        mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG,
-                                UserHandle.USER_SYSTEM).mKey, prefix);
+                        SettingsState.makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM),
+                        prefix);
             }
         }
-
         return result;
     }
 
@@ -3052,10 +3056,15 @@
             final int key = makeKey(type, userId);
 
             boolean success = false;
+            boolean isNewSetting = false;
             SettingsState settingsState = peekSettingsStateLocked(key);
             if (settingsState != null) {
+                if (!settingsState.hasSetting(name)) {
+                    isNewSetting = true;
+                }
                 success = settingsState.insertSettingLocked(name, value,
-                        tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore);
+                        tag, makeDefault, forceNonSystemPackage, packageName,
+                        overrideableByRestore);
             }
 
             if (success && criticalSettings != null && criticalSettings.contains(name)) {
@@ -3064,6 +3073,11 @@
 
             if (forceNotify || success) {
                 notifyForSettingsChange(key, name);
+                if (isNewSetting && !isSettingPreDefined(name, type)) {
+                    // Increment the generation number for all null settings because a new
+                    // non-predefined setting has been inserted
+                    mGenerationRegistry.incrementGenerationForUnsetSettings(key);
+                }
             }
             if (success) {
                 logSettingChanged(userId, name, type, CHANGE_TYPE_INSERT);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c388826..4d8705f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -759,6 +759,12 @@
         mPackageToMemoryUsage.put(packageName, newSize);
     }
 
+    public boolean hasSetting(String name) {
+        synchronized (mLock) {
+            return hasSettingLocked(name);
+        }
+    }
+
     @GuardedBy("mLock")
     private boolean hasSettingLocked(String name) {
         return mSettings.indexOfKey(name) >= 0;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index d34fe694..6ec8146 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -151,6 +151,26 @@
         checkBundle(b, 0, 1, false);
     }
 
+    @Test
+    public void testUnsetSettings() throws IOException {
+        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+        final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
+        final String testSecureSetting = "test_secure_setting";
+        Bundle b = new Bundle();
+        generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
+        checkBundle(b, 0, 1, false);
+        generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
+        checkBundle(b, 1, 1, false);
+        generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
+        // Test that unset settings always have the same index
+        checkBundle(b, 1, 1, false);
+        generationRegistry.incrementGenerationForUnsetSettings(secureKey);
+        // Test that the generation number of the unset settings have increased
+        generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
+        checkBundle(b, 1, 2, false);
+    }
+
+
     private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull)
             throws IOException {
         final MemoryIntArray array = getArray(b);
diff --git a/packages/SystemUI/animation/.gitignore b/packages/SystemUI/animation/.gitignore
new file mode 100644
index 0000000..f9a33db
--- /dev/null
+++ b/packages/SystemUI/animation/.gitignore
@@ -0,0 +1,9 @@
+.idea/
+.gradle/
+gradle/
+build/
+gradlew*
+local.properties
+*.iml
+android.properties
+buildSrc
\ No newline at end of file
diff --git a/packages/SystemUI/animation/build.gradle b/packages/SystemUI/animation/build.gradle
new file mode 100644
index 0000000..939455f
--- /dev/null
+++ b/packages/SystemUI/animation/build.gradle
@@ -0,0 +1,37 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+// TODO: Pull out surfaceeffects outside of src and have separate build files there.
+android {
+    sourceSets {
+        main {
+            java.srcDirs = ["${SYS_UI_DIR}/animation/src/com/android/systemui/surfaceeffects/"]
+            manifest.srcFile "${SYS_UI_DIR}/animation/AndroidManifest.xml"
+        }
+    }
+
+    compileSdk 33
+
+    defaultConfig {
+        minSdk 33
+        targetSdk 33
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+    tasks.lint.enabled = false
+    tasks.withType(JavaCompile) {
+        options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+        freeCompilerArgs = ["-Xjvm-default=all"]
+    }
+}
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0"
+    implementation 'androidx.core:core-animation:1.0.0-alpha02'
+    implementation 'androidx.core:core-ktx:1.9.0'
+}
diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml
index 58fe368..97bd18e 100644
--- a/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml
+++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_background.xml
@@ -27,7 +27,7 @@
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/magnifier_outer_corner_radius" />
             <stroke
-                android:color="@android:color/black"
+                android:color="@color/magnification_drag_handle_stroke"
                 android:width="@dimen/magnifier_stroke_width"/>
         </shape>
     </item>
diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml
index a52e805..66617e1 100644
--- a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml
+++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background.xml
@@ -16,7 +16,7 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
     <stroke
-        android:color="@android:color/black"
+        android:color="@color/magnification_drag_handle_stroke"
         android:width="@dimen/magnifier_stroke_width"/>
     <corners android:radius="@dimen/magnifier_corner_radius" />
     <solid android:color="@color/magnification_border_color" />
diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change.xml
new file mode 100644
index 0000000..e367f50
--- /dev/null
+++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?><!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <stroke
+        android:color="@color/magnification_border_color"
+        android:width="@dimen/magnifier_stroke_width"/>
+    <corners android:radius="@dimen/magnifier_corner_radius" />
+    <solid android:color="@color/magnification_drag_handle_background_change" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_move_magnification.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml
index 641bb43..0796007 100644
--- a/packages/SystemUI/res/drawable/ic_move_magnification.xml
+++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml
@@ -20,6 +20,6 @@
     android:viewportHeight="24"
     android:tint="?attr/colorControlNormal">
   <path
-      android:fillColor="@color/magnification_drag_handle_tint"
+      android:fillColor="@color/magnification_drag_handle_stroke"
       android:pathData="M12,15Q10.75,15 9.875,14.125Q9,13.25 9,12Q9,10.75 9.875,9.875Q10.75,9 12,9Q13.25,9 14.125,9.875Q15,10.75 15,12Q15,13.25 14.125,14.125Q13.25,15 12,15ZM12,22 L7.75,17.75 9.15,16.35 12,19.15 14.85,16.35 16.25,17.75ZM6.25,16.25 L2,12 6.25,7.75 7.65,9.15 4.85,12 7.65,14.85ZM9.15,7.65 L7.75,6.25 12,2 16.25,6.25 14.85,7.65 12,4.85ZM17.75,16.25 L16.35,14.85 19.15,12 16.35,9.15 17.75,7.75 22,12Z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
new file mode 100644
index 0000000..de0a620
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <!-- gradient from 25% in the center to 100% at edges -->
+    <gradient
+        android:type="radial"
+        android:gradientRadius="40%p"
+        android:startColor="#AE000000"
+        android:endColor="#00000000" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_recommendation_view.xml b/packages/SystemUI/res/layout/media_recommendation_view.xml
index c54c4e4..a4aeba1 100644
--- a/packages/SystemUI/res/layout/media_recommendation_view.xml
+++ b/packages/SystemUI/res/layout/media_recommendation_view.xml
@@ -22,9 +22,10 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:translationZ="0dp"
-        android:scaleType="centerCrop"
+        android:scaleType="matrix"
         android:adjustViewBounds="true"
         android:clipToOutline="true"
+        android:layerType="hardware"
         android:background="@drawable/bg_smartspace_media_item"/>
 
     <!-- App icon -->
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c6cc0bc..d4ebd10 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -163,8 +163,8 @@
     <color name="magnification_border_color">#F29900</color>
     <color name="magnification_switch_button_color">#7F000000</color>
     <color name="magnification_drag_corner_background">#E5FFFFFF</color>
-    <color name="magnification_drag_handle_color">#B3000000</color>
-    <color name="magnification_drag_handle_tint">#111111</color>
+    <color name="magnification_drag_handle_stroke">#000000</color>
+    <color name="magnification_drag_handle_background_change">#111111</color>
     <color name="accessibility_magnifier_bg">#FCFCFC</color>
     <color name="accessibility_magnifier_bg_stroke">#E0E0E0</color>
     <color name="accessibility_magnifier_icon_color">#252525</color>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 6dd359c..45a5ce3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.app.ActivityTaskManager.getService;
 
 import android.annotation.NonNull;
@@ -45,6 +44,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.Display;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationTarget;
@@ -112,6 +112,13 @@
     }
 
     /**
+     * @see #getRunningTasks(boolean , int)
+     */
+    public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+        return getRunningTasks(filterOnlyVisibleRecents, Display.INVALID_DISPLAY);
+    }
+
+    /**
      * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen,
      * we'll get back 2 activities for each split app and one for launcher. Launcher might be more
      * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss
@@ -120,10 +127,12 @@
      * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks
      *         filtering only for tasks that can be visible in the recent tasks list.
      */
-    public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+    public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents,
+            int displayId) {
         // Note: The set of running tasks from the system is ordered by recency
         List<ActivityManager.RunningTaskInfo> tasks =
-                mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents);
+                mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST,
+                        filterOnlyVisibleRecents, /* keepInExtras= */ false, displayId);
         return tasks.toArray(new RunningTaskInfo[tasks.size()]);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 21d35c9..30e2a0b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -877,7 +877,10 @@
 
     private void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
         mBackgroundExecutor.execute(
-                () -> mLockPatternUtils.reportSuccessfulBiometricUnlock(isStrongBiometric, userId));
+                () -> {
+                    mLogger.logReportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+                    mLockPatternUtils.reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+                });
     }
 
     private void handleFingerprintAuthFailed() {
@@ -2530,11 +2533,13 @@
         // If this message exists, we should not authenticate again until this message is
         // consumed by the handler
         if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
+            mLogger.logHandlerHasAuthContinueMsgs(action);
             return;
         }
 
         // don't start running fingerprint until they're registered
         if (!mAuthController.areAllFingerprintAuthenticatorsRegistered()) {
+            mLogger.d("All FP authenticators not registered, skipping FP listening state update");
             return;
         }
         final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
index bc0bd8c..20f9007 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard.logging
 
+import android.hardware.biometrics.BiometricSourceType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.dagger.BiometricLog
 import com.android.systemui.plugins.log.LogBuffer
@@ -157,6 +158,36 @@
             }
         )
     }
+
+    fun deferringAuthenticationDueToSleep(
+        userId: Int,
+        biometricSourceType: BiometricSourceType,
+        alreadyPendingAuth: Boolean
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                str1 = biometricSourceType.name
+                bool2 = alreadyPendingAuth
+            },
+            {
+                "onBiometricAuthenticated, deferring auth: userId: $int1, " +
+                    "biometricSourceType: $str1, " +
+                    "goingToSleep: true, " +
+                    "mPendingAuthentication != null: $bool2"
+            }
+        )
+    }
+
+    fun finishedGoingToSleepWithPendingAuth() {
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            "onFinishedGoingToSleep with pendingAuthenticated != null"
+        )
+    }
 }
 
 private fun wakeAndUnlockModeToString(mode: Int): String {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 379c78a..51aca07 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard.logging
 
+import com.android.systemui.biometrics.AuthRippleController
 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
 import com.android.systemui.log.dagger.KeyguardLog
 import com.android.systemui.plugins.log.LogBuffer
@@ -120,4 +121,29 @@
             "type=${KeyguardIndicationRotateTextViewController.indicationTypeToString(type)}"
         }
     }
+
+    fun notShowingUnlockRipple(keyguardNotShowing: Boolean, unlockNotAllowed: Boolean) {
+        buffer.log(
+            AuthRippleController.TAG,
+            LogLevel.DEBUG,
+            {
+                bool1 = keyguardNotShowing
+                bool2 = unlockNotAllowed
+            },
+            { "Not showing unlock ripple: keyguardNotShowing: $bool1, unlockNotAllowed: $bool2" }
+        )
+    }
+
+    fun showingUnlockRippleAt(x: Int, y: Int, context: String) {
+        buffer.log(
+            AuthRippleController.TAG,
+            LogLevel.DEBUG,
+            {
+                int1 = x
+                int2 = y
+                str1 = context
+            },
+            { "Showing unlock ripple with center (x, y): ($int1, $int2), context: $str1" }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index fb2c02a..2403d11 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -584,6 +584,30 @@
         )
     }
 
+    fun logReportSuccessfulBiometricUnlock(isStrongBiometric: Boolean, userId: Int) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = isStrongBiometric
+                int1 = userId
+            },
+            { "reporting successful biometric unlock: isStrongBiometric: $bool1, userId: $int1" }
+        )
+    }
+
+    fun logHandlerHasAuthContinueMsgs(action: Int) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = action },
+            {
+                "MSG_BIOMETRIC_AUTHENTICATION_CONTINUE already queued up, " +
+                    "ignoring updating FP listening state to $int1"
+            }
+        )
+    }
+
     fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) {
         logBuffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index d35c77c..d811d30 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -37,6 +37,8 @@
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -1398,6 +1400,11 @@
             mWindowMagnifierCallback.onPerformScaleAction(mDisplayId,
                     A11Y_ACTION_SCALE_RANGE.clamp(scale));
         }
+
+        @Override
+        public void onSettingsPanelVisibilityChanged(boolean shown) {
+            updateDragHandleResourcesIfNeeded(/* settingsPanelIsShown= */ shown);
+        }
     };
 
     @Override
@@ -1436,6 +1443,20 @@
         }
     }
 
+    private void updateDragHandleResourcesIfNeeded(boolean settingsPanelIsShown) {
+        mDragView.setBackground(mContext.getResources().getDrawable(settingsPanelIsShown
+                ? R.drawable.accessibility_window_magnification_drag_handle_background_change
+                : R.drawable.accessibility_window_magnification_drag_handle_background));
+
+        PorterDuffColorFilter filter = new PorterDuffColorFilter(
+                mContext.getColor(settingsPanelIsShown
+                        ? R.color.magnification_border_color
+                        : R.color.magnification_drag_handle_stroke),
+                PorterDuff.Mode.SRC_ATOP);
+
+        mDragView.setColorFilter(filter);
+    }
+
     private void animateBounceEffect() {
         final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mMirrorView,
                 PropertyValuesHolder.ofFloat(View.SCALE_X, 1, mBounceEffectAnimationScale, 1),
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index e1f3c6c..9ad64e29 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -323,6 +323,7 @@
         }
 
         mContext.unregisterReceiver(mScreenOffReceiver);
+        mCallback.onSettingsPanelVisibilityChanged(/* shown= */ false);
     }
 
     public void showSettingPanel() {
@@ -361,6 +362,7 @@
             // Exclude magnification switch button from system gesture area.
             setSystemGestureExclusion();
             mIsVisible = true;
+            mCallback.onSettingsPanelVisibilityChanged(/* shown= */ true);
         }
         mContext.registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java
index 22ec650..1d83340 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettingsCallback.java
@@ -61,4 +61,11 @@
      * 1 : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, 2 : ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
      */
     void onModeSwitch(int newMode);
+
+    /**
+     * Called when the visibility of the magnification settings panel changed.
+     *
+     * @param shown The visibility of the magnification settings panel.
+     */
+    void onSettingsPanelVisibilityChanged(boolean shown);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index d561cd7..93b57dc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -26,6 +26,7 @@
 import androidx.annotation.VisibleForTesting
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.keyguard.logging.KeyguardLogger
 import com.android.settingslib.Utils
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
@@ -74,6 +75,7 @@
     private val udfpsControllerProvider: Provider<UdfpsController>,
     private val statusBarStateController: StatusBarStateController,
     private val featureFlags: FeatureFlags,
+    private val logger: KeyguardLogger,
         rippleView: AuthRippleView?
 ) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
     WakefulnessLifecycle.Observer {
@@ -120,8 +122,11 @@
     }
 
     fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
-        if (!keyguardStateController.isShowing ||
-                !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(biometricSourceType)) {
+        val keyguardNotShowing = !keyguardStateController.isShowing
+        val unlockNotAllowed = !keyguardUpdateMonitor
+                .isUnlockingWithBiometricAllowed(biometricSourceType)
+        if (keyguardNotShowing || unlockNotAllowed) {
+            logger.notShowingUnlockRipple(keyguardNotShowing, unlockNotAllowed)
             return
         }
 
@@ -138,6 +143,7 @@
                                 Math.max(it.y, centralSurfaces.displayHeight.toInt() - it.y)
                         )
                 )
+                logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius")
                 showUnlockedRipple()
             }
         } else if (biometricSourceType == BiometricSourceType.FACE) {
@@ -155,6 +161,7 @@
                                 Math.max(it.y, centralSurfaces.displayHeight.toInt() - it.y)
                         )
                 )
+                logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple")
                 showUnlockedRipple()
             }
         }
@@ -391,5 +398,6 @@
 
     companion object {
         const val RIPPLE_ANIMATION_DURATION: Long = 1533
+        const val TAG = "AuthRippleController"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index bc0f995..f83885b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -38,6 +38,7 @@
 public class FalsingDataProvider {
 
     private static final long MOTION_EVENT_AGE_MS = 1000;
+    private static final long DROP_EVENT_THRESHOLD_MS = 50;
     private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
 
     private final int mWidthPixels;
@@ -60,6 +61,7 @@
     private float mAngle = 0;
     private MotionEvent mFirstRecentMotionEvent;
     private MotionEvent mLastMotionEvent;
+    private boolean mDropLastEvent;
     private boolean mJustUnlockedWithFace;
     private boolean mA11YAction;
 
@@ -95,6 +97,12 @@
             // Ensure prior gesture was completed. May be a no-op.
             completePriorGesture();
         }
+
+        // Drop the gesture closing event if it is close in time to a previous ACTION_MOVE event.
+        // The reason is that the closing ACTION_UP event of  a swipe can be a bit offseted from the
+        // previous ACTION_MOVE event and when it happens, it makes some classifiers fail.
+        mDropLastEvent = shouldDropEvent(motionEvent);
+
         mRecentMotionEvents.addAll(motionEvents);
 
         FalsingClassifier.logVerbose("Size: " + mRecentMotionEvents.size());
@@ -129,6 +137,7 @@
             mPriorMotionEvents = mRecentMotionEvents;
             mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
         }
+        mDropLastEvent = false;
         mA11YAction = false;
     }
 
@@ -150,8 +159,18 @@
         return mYdpi;
     }
 
+    /**
+     * Get the {@link MotionEvent}s of the most recent gesture.
+     *
+     * Note that this list may not include the last recorded event.
+     * @see #mDropLastEvent
+     */
     public List<MotionEvent> getRecentMotionEvents() {
-        return mRecentMotionEvents;
+        if (!mDropLastEvent || mRecentMotionEvents.isEmpty()) {
+            return mRecentMotionEvents;
+        } else {
+            return mRecentMotionEvents.subList(0, mRecentMotionEvents.size() - 1);
+        }
     }
 
     public List<MotionEvent> getPriorMotionEvents() {
@@ -169,7 +188,12 @@
         return mFirstRecentMotionEvent;
     }
 
-    /** Get the last recorded {@link MotionEvent}. */
+    /**
+     * Get the last {@link MotionEvent} of the most recent gesture.
+     *
+     * Note that this may be the event prior to the last recorded event.
+     * @see #mDropLastEvent
+     */
     public MotionEvent getLastMotionEvent() {
         recalculateData();
         return mLastMotionEvent;
@@ -236,12 +260,13 @@
             return;
         }
 
-        if (mRecentMotionEvents.isEmpty()) {
+        List<MotionEvent> recentMotionEvents = getRecentMotionEvents();
+        if (recentMotionEvents.isEmpty()) {
             mFirstRecentMotionEvent = null;
             mLastMotionEvent = null;
         } else {
-            mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
-            mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+            mFirstRecentMotionEvent = recentMotionEvents.get(0);
+            mLastMotionEvent = recentMotionEvents.get(recentMotionEvents.size() - 1);
         }
 
         calculateAngleInternal();
@@ -249,6 +274,17 @@
         mDirty = false;
     }
 
+    private boolean shouldDropEvent(MotionEvent event) {
+        if (mRecentMotionEvents.size() < 3) return false;
+
+        MotionEvent lastEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+        boolean isCompletingGesture = event.getActionMasked() == MotionEvent.ACTION_UP
+                && lastEvent.getActionMasked() == MotionEvent.ACTION_MOVE;
+        boolean isRecentEvent =
+                event.getEventTime() - lastEvent.getEventTime() < DROP_EVENT_THRESHOLD_MS;
+        return isCompletingGesture && isRecentEvent;
+    }
+
     private void calculateAngleInternal() {
         if (mRecentMotionEvents.size() < 2) {
             mAngle = Float.MAX_VALUE;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
index 4773f2a..51aede7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
@@ -183,7 +183,7 @@
 
     @Override
     public List<MotionEvent> subList(int fromIndex, int toIndex) {
-        throw new UnsupportedOperationException();
+        return mMotionEvents.subList(fromIndex, toIndex);
     }
 
     class Iter implements ListIterator<MotionEvent> {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index c214f53..2501be9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -263,10 +263,11 @@
     @Override // ClipboardListener.ClipboardOverlay
     public void setClipData(ClipData data, String source) {
         ClipboardModel model = ClipboardModel.fromClipData(mContext, mClipboardUtils, data, source);
-        if (mExitAnimator != null && mExitAnimator.isRunning()) {
+        boolean wasExiting = (mExitAnimator != null && mExitAnimator.isRunning());
+        if (wasExiting) {
             mExitAnimator.cancel();
         }
-        boolean shouldAnimate = !model.dataMatches(mClipboardModel);
+        boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting;
         mClipboardModel = model;
         mClipboardLogger.setClipSource(mClipboardModel.getSource());
         if (shouldAnimate) {
@@ -313,15 +314,19 @@
                 mOnPreviewTapped = this::editText;
                 break;
             case IMAGE:
-                if (model.isSensitive() || model.loadThumbnail(mContext) != null) {
-                    mView.showImagePreview(
-                            model.isSensitive() ? null : model.loadThumbnail(mContext));
-                    mView.setEditAccessibilityAction(true);
-                    mOnPreviewTapped = () -> editImage(model.getUri());
-                } else {
-                    // image loading failed
-                    mView.showDefaultTextPreview();
-                }
+                mBgExecutor.execute(() -> {
+                    if (model.isSensitive() || model.loadThumbnail(mContext) != null) {
+                        mView.post(() -> {
+                            mView.showImagePreview(
+                                    model.isSensitive() ? null : model.loadThumbnail(mContext));
+                            mView.setEditAccessibilityAction(true);
+                        });
+                        mOnPreviewTapped = () -> editImage(model.getUri());
+                    } else {
+                        // image loading failed
+                        mView.post(mView::showDefaultTextPreview);
+                    }
+                });
                 break;
             case URI:
             case OTHER:
@@ -363,15 +368,15 @@
 
     private void classifyText(ClipboardModel model) {
         mBgExecutor.execute(() -> {
-            Optional<RemoteAction> remoteAction = mClipboardUtils.getAction(
-                            model.getText(), model.getTextLinks(), model.getSource());
+            Optional<RemoteAction> remoteAction =
+                    mClipboardUtils.getAction(model.getTextLinks(), model.getSource());
             if (model.equals(mClipboardModel)) {
                 remoteAction.ifPresent(action -> {
                     mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN);
-                    mView.setActionChip(action, () -> {
+                    mView.post(() -> mView.setActionChip(action, () -> {
                         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
                         animateOut();
-                    });
+                    }));
                 });
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
index a85f8b9..25caaea 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
@@ -39,6 +39,9 @@
 
 class ClipboardOverlayUtils {
 
+    // minimum proportion of entire text an entity must take up, to be considered for smart actions
+    private static final float MINIMUM_ENTITY_PROPORTION = .8f;
+
     private final TextClassifier mTextClassifier;
 
     @Inject
@@ -65,19 +68,23 @@
         return false;
     }
 
-    public Optional<RemoteAction> getAction(CharSequence text, TextLinks textLinks, String source) {
-        return getActions(text, textLinks).stream().filter(remoteAction -> {
+    public Optional<RemoteAction> getAction(TextLinks textLinks, String source) {
+        return getActions(textLinks).stream().filter(remoteAction -> {
             ComponentName component = remoteAction.getActionIntent().getIntent().getComponent();
             return component != null && !TextUtils.equals(source, component.getPackageName());
         }).findFirst();
     }
 
-    private ArrayList<RemoteAction> getActions(CharSequence text, TextLinks textLinks) {
+    private ArrayList<RemoteAction> getActions(TextLinks textLinks) {
         ArrayList<RemoteAction> actions = new ArrayList<>();
         for (TextLinks.TextLink link : textLinks.getLinks()) {
-            TextClassification classification = mTextClassifier.classifyText(
-                    text, link.getStart(), link.getEnd(), null);
-            actions.addAll(classification.getActions());
+            // skip classification for incidental entities
+            if (link.getEnd() - link.getStart()
+                    >= textLinks.getText().length() * MINIMUM_ENTITY_PROPORTION) {
+                TextClassification classification = mTextClassifier.classifyText(
+                        textLinks.getText(), link.getStart(), link.getEnd(), null);
+                actions.addAll(classification.getActions());
+            }
         }
         return actions;
     }
@@ -92,9 +99,13 @@
     private ArrayList<RemoteAction> getActions(ClipData.Item item) {
         ArrayList<RemoteAction> actions = new ArrayList<>();
         for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
-            TextClassification classification = mTextClassifier.classifyText(
-                    item.getText(), link.getStart(), link.getEnd(), null);
-            actions.addAll(classification.getActions());
+            // skip classification for incidental entities
+            if (link.getEnd() - link.getStart()
+                    >= item.getText().length() * MINIMUM_ENTITY_PROPORTION) {
+                TextClassification classification = mTextClassifier.classifyText(
+                        item.getText(), link.getStart(), link.getEnd(), null);
+                actions.addAll(classification.getActions());
+            }
         }
         return actions;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5817415..95b37ac 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -438,7 +438,9 @@
         )
 
     // TODO(b/256873975): Tracking Bug
-    @JvmField @Keep val WM_BUBBLE_BAR = unreleasedFlag(1111, "wm_bubble_bar")
+    @JvmField
+    @Keep
+    val WM_BUBBLE_BAR = sysPropBooleanFlag(1111, "persist.wm.debug.bubble_bar", default = false)
 
     // TODO(b/260271148): Tracking bug
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
index b0f9c4e..d078688 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyboard.backlight.ui.KeyboardBacklightDialogCoordinator
 import javax.inject.Inject
 
 /** A [CoreStartable] that launches components interested in physical keyboard interaction. */
@@ -28,11 +29,12 @@
 class PhysicalKeyboardCoreStartable
 @Inject
 constructor(
+    private val keyboardBacklightDialogCoordinator: KeyboardBacklightDialogCoordinator,
     private val featureFlags: FeatureFlags,
 ) : CoreStartable {
     override fun start() {
         if (featureFlags.isEnabled(Flags.KEYBOARD_BACKLIGHT_INDICATOR)) {
-            // TODO(b/268645743) start listening for keyboard backlight brightness
+            keyboardBacklightDialogCoordinator.startListening()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
new file mode 100644
index 0000000..65e70b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+/** Allows listening to changes to keyboard backlight level */
+@SysUISingleton
+class KeyboardBacklightInteractor
+@Inject
+constructor(
+    private val keyboardRepository: KeyboardRepository,
+) {
+
+    /** Emits current backlight level as [BacklightModel] or null if keyboard is not connected */
+    val backlight: Flow<BacklightModel?> =
+        keyboardRepository.keyboardConnected.flatMapLatest { connected ->
+            if (connected) keyboardRepository.backlight else flowOf(null)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
new file mode 100644
index 0000000..85d0379
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.ui
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Based on the state produced from [BacklightDialogViewModel] shows or hides keyboard backlight
+ * indicator
+ */
+@SysUISingleton
+class KeyboardBacklightDialogCoordinator
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val context: Context,
+    private val viewModel: BacklightDialogViewModel,
+) {
+
+    var dialog: KeyboardBacklightDialog? = null
+
+    fun startListening() {
+        applicationScope.launch {
+            viewModel.dialogContent.collect { dialogViewModel ->
+                if (dialogViewModel != null) {
+                    if (dialog == null) {
+                        dialog = KeyboardBacklightDialog(context, dialogViewModel)
+                        // pass viewModel and show
+                    }
+                } else {
+                    dialog?.dismiss()
+                    dialog = null
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
new file mode 100644
index 0000000..b68a2a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.ui.view
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogContentViewModel
+
+class KeyboardBacklightDialog(context: Context, val viewModel: BacklightDialogContentViewModel) :
+    Dialog(context) {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        // TODO(b/268650355) Implement the dialog
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
similarity index 68%
copy from packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
copy to packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
index ea15a9f..3ef0ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
@@ -15,10 +15,6 @@
  *
  */
 
-package com.android.systemui.keyboard.data.model
+package com.android.systemui.keyboard.backlight.ui.viewmodel
 
-/**
- * Model for current state of keyboard backlight brightness. [level] indicates current level of
- * backlight brightness and [maxLevel] its max possible value.
- */
-data class BacklightModel(val level: Int, val maxLevel: Int)
+data class BacklightDialogContentViewModel(val currentValue: Int, val maxValue: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt
new file mode 100644
index 0000000..86abca5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.ui.viewmodel
+
+import android.view.accessibility.AccessibilityManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import javax.inject.Inject
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Responsible for dialog visibility and content - emits [BacklightDialogContentViewModel] if dialog
+ * should be shown and hidden otherwise
+ */
+@SysUISingleton
+class BacklightDialogViewModel
+@Inject
+constructor(
+    interactor: KeyboardBacklightInteractor,
+    private val accessibilityManagerWrapper: AccessibilityManagerWrapper,
+) {
+
+    private val timeoutMillis: Long
+        get() =
+            accessibilityManagerWrapper
+                .getRecommendedTimeoutMillis(
+                    DEFAULT_DIALOG_TIMEOUT_MILLIS,
+                    AccessibilityManager.FLAG_CONTENT_ICONS
+                )
+                .toLong()
+
+    val dialogContent: Flow<BacklightDialogContentViewModel?> =
+        interactor.backlight
+            .filterNotNull()
+            .map { BacklightDialogContentViewModel(it.level, it.maxLevel) }
+            .timeout(timeoutMillis, emitAfterTimeout = null)
+
+    private fun <T> Flow<T>.timeout(timeoutMillis: Long, emitAfterTimeout: T): Flow<T> {
+        return flatMapLatest {
+            flow {
+                emit(it)
+                delay(timeoutMillis)
+                emit(emitAfterTimeout)
+            }
+        }
+    }
+
+    private companion object {
+        const val DEFAULT_DIALOG_TIMEOUT_MILLIS = 3000
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
index dd5c5d3..b86083a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.data.model.BacklightModel
+import com.android.systemui.keyboard.shared.model.BacklightModel
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
rename to packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
index ea15a9f..4a32f79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.keyboard.data.model
+package com.android.systemui.keyboard.shared.model
 
 /**
  * Model for current state of keyboard backlight brightness. [level] indicates current level of
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 288f0cc..377a136 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1929,20 +1929,24 @@
 
         // If the keyguard is already showing, see if we don't need to bother re-showing it. Check
         // flags in both files to account for the hiding animation which results in a delay and
-        // discrepancy between flags.
+        // discrepancy between flags. If we're in the middle of hiding, do not short circuit so that
+        // we explicitly re-set state.
         if (mShowing && mKeyguardStateController.isShowing()) {
-            if (mPM.isInteractive()) {
+            if (mPM.isInteractive() && !mHiding) {
                 // It's already showing, and we're not trying to show it while the screen is off.
                 // We can simply reset all of the views.
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+                if (DEBUG) Log.d(TAG, "doKeyguard: not showing (instead, resetting) because it is "
+                        + "already showing, we're interactive, and we were not previously hiding. "
+                        + "It should be safe to short-circuit here.");
                 resetStateLocked();
                 return;
             } else {
-                // We are trying to show the keyguard while the screen is off - this results from
-                // race conditions involving locking while unlocking. Don't short-circuit here and
-                // ensure the keyguard is fully re-shown.
+                // We are trying to show the keyguard while the screen is off or while we were in
+                // the middle of hiding - this results from race conditions involving locking while
+                // unlocking. Don't short-circuit here and ensure the keyguard is fully re-shown.
                 Log.e(TAG,
-                        "doKeyguard: already showing, but re-showing since we're not interactive");
+                        "doKeyguard: already showing, but re-showing because we're interactive or "
+                                + "were in the middle of hiding.");
             }
         }
 
@@ -2436,11 +2440,19 @@
                 if (DEBUG) Log.d(TAG, "handleShow");
             }
 
-            mHiding = false;
             mKeyguardExitAnimationRunner = null;
             mWakeAndUnlocking = false;
             setPendingLock(false);
-            setShowingLocked(true);
+
+            // Force if we we're showing in the middle of hiding, to ensure we end up in the correct
+            // state.
+            setShowingLocked(true, mHiding /* force */);
+            if (mHiding) {
+                Log.d(TAG, "Forcing setShowingLocked because mHiding=true, which means we're "
+                        + "showing in the middle of hiding.");
+            }
+            mHiding = false;
+
             mKeyguardViewControllerLazy.get().show(options);
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 1fafd7f..2dc8fee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -45,7 +45,6 @@
 
     override fun start() {
         listenForOccludedToLockscreen()
-        listenForOccludedToGone()
         listenForOccludedToDreaming()
         listenForOccludedToAodOrDozing()
     }
@@ -73,22 +72,11 @@
     private fun listenForOccludedToLockscreen() {
         scope.launch {
             keyguardInteractor.isKeyguardOccluded
-                .sample(
-                    combine(
-                        keyguardInteractor.isKeyguardShowing,
-                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
-                        ::Pair
-                    ),
-                    ::toTriple
-                )
-                .collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isOccluded, lastStartedKeyguardState) ->
                     // Occlusion signals come from the framework, and should interrupt any
                     // existing transition
-                    if (
-                        !isOccluded &&
-                            isShowing &&
-                            lastStartedKeyguardState.to == KeyguardState.OCCLUDED
-                    ) {
+                    if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 name,
@@ -102,38 +90,6 @@
         }
     }
 
-    private fun listenForOccludedToGone() {
-        scope.launch {
-            keyguardInteractor.isKeyguardOccluded
-                .sample(
-                    combine(
-                        keyguardInteractor.isKeyguardShowing,
-                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
-                        ::Pair
-                    ),
-                    ::toTriple
-                )
-                .collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
-                    // Occlusion signals come from the framework, and should interrupt any
-                    // existing transition
-                    if (
-                        !isOccluded &&
-                            !isShowing &&
-                            lastStartedKeyguardState.to == KeyguardState.OCCLUDED
-                    ) {
-                        keyguardTransitionRepository.startTransition(
-                            TransitionInfo(
-                                name,
-                                KeyguardState.OCCLUDED,
-                                KeyguardState.GONE,
-                                getAnimator(),
-                            )
-                        )
-                    }
-                }
-        }
-    }
-
     private fun listenForOccludedToAodOrDozing() {
         scope.launch {
             keyguardInteractor.wakefulnessModel
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 097cc3e..a31c1e5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -32,12 +32,15 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
 import android.graphics.BlendMode;
 import android.graphics.Color;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
@@ -63,7 +66,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
-import androidx.appcompat.content.res.AppCompatResources;
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -146,6 +148,12 @@
     private static final int SMARTSPACE_CARD_CLICK_EVENT = 760;
     protected static final int SMARTSPACE_CARD_DISMISS_EVENT = 761;
 
+    private static final float REC_MEDIA_COVER_SCALE_FACTOR = 1.25f;
+    private static final float MEDIA_SCRIM_START_ALPHA = 0.25f;
+    private static final float MEDIA_REC_SCRIM_START_ALPHA = 0.15f;
+    private static final float MEDIA_PLAYER_SCRIM_END_ALPHA = 0.9f;
+    private static final float MEDIA_REC_SCRIM_END_ALPHA = 1.0f;
+
     private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
 
     // Buttons to show in small player when using semantic actions
@@ -779,7 +787,7 @@
             WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
             if (wallpaperColors != null) {
                 mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
-                artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
+                artwork = addGradientToPlayerAlbum(artworkIcon, mutableColorScheme, width, height);
                 isArtworkBound = true;
             } else {
                 // If there's no artwork, use colors from the app icon
@@ -869,8 +877,9 @@
         Trace.beginAsyncSection(traceName, traceCookie);
 
         // Capture width & height from views in foreground for artwork scaling in background
-        int width = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredWidth();
-        int height = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredHeight();
+        int width = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_rec_album_width);
+        int height = mContext.getResources().getDimensionPixelSize(
+                R.dimen.qs_media_rec_album_height_expanded);
 
         mBackgroundExecutor.execute(() -> {
             // Album art
@@ -880,7 +889,8 @@
             WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
             if (wallpaperColors != null) {
                 mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
-                artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
+                artwork = addGradientToRecommendationAlbum(artworkIcon, mutableColorScheme, width,
+                        height);
             } else {
                 artwork = new ColorDrawable(Color.TRANSPARENT);
             }
@@ -889,6 +899,11 @@
                 // Bind the artwork drawable to media cover.
                 ImageView mediaCover =
                         mRecommendationViewHolder.getMediaCoverItems().get(itemIndex);
+                // Rescale media cover
+                Matrix coverMatrix = new Matrix(mediaCover.getImageMatrix());
+                coverMatrix.postScale(REC_MEDIA_COVER_SCALE_FACTOR, REC_MEDIA_COVER_SCALE_FACTOR,
+                        0.5f * width, 0.5f * height);
+                mediaCover.setImageMatrix(coverMatrix);
                 mediaCover.setImageDrawable(artwork);
 
                 // Set up the app icon.
@@ -910,40 +925,62 @@
     // This method should be called from a background thread. WallpaperColors.fromBitmap takes a
     // good amount of time. We do that work on the background executor to avoid stalling animations
     // on the UI Thread.
-    private WallpaperColors getWallpaperColor(Icon artworkIcon) {
+    @VisibleForTesting
+    protected WallpaperColors getWallpaperColor(Icon artworkIcon) {
         if (artworkIcon != null) {
             if (artworkIcon.getType() == Icon.TYPE_BITMAP
                     || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
                 // Avoids extra processing if this is already a valid bitmap
-                return WallpaperColors
-                        .fromBitmap(artworkIcon.getBitmap());
+                Bitmap artworkBitmap = artworkIcon.getBitmap();
+                if (artworkBitmap.isRecycled()) {
+                    Log.d(TAG, "Cannot load wallpaper color from a recycled bitmap");
+                    return null;
+                }
+                return WallpaperColors.fromBitmap(artworkBitmap);
             } else {
                 Drawable artworkDrawable = artworkIcon.loadDrawable(mContext);
                 if (artworkDrawable != null) {
-                    return WallpaperColors
-                            .fromDrawable(artworkIcon.loadDrawable(mContext));
+                    return WallpaperColors.fromDrawable(artworkDrawable);
                 }
             }
         }
         return null;
     }
 
-    private LayerDrawable addGradientToIcon(
-            Icon artworkIcon,
-            ColorScheme mutableColorScheme,
-            int width,
-            int height
-    ) {
+    @VisibleForTesting
+    protected LayerDrawable addGradientToPlayerAlbum(Icon artworkIcon,
+            ColorScheme mutableColorScheme, int width, int height) {
         Drawable albumArt = getScaledBackground(artworkIcon, width, height);
-        GradientDrawable gradient = (GradientDrawable) AppCompatResources
-                .getDrawable(mContext, R.drawable.qs_media_scrim);
+        GradientDrawable gradient = (GradientDrawable) mContext.getDrawable(
+                R.drawable.qs_media_scrim).mutate();
+        return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
+                MEDIA_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA);
+    }
+
+    @VisibleForTesting
+    protected LayerDrawable addGradientToRecommendationAlbum(Icon artworkIcon,
+            ColorScheme mutableColorScheme, int width, int height) {
+        // First try scaling rec card using bitmap drawable.
+        // If returns null, set drawable bounds.
+        Drawable albumArt = getScaledRecommendationCover(artworkIcon, width, height);
+        if (albumArt == null) {
+            albumArt = getScaledBackground(artworkIcon, width, height);
+        }
+        GradientDrawable gradient = (GradientDrawable) mContext.getDrawable(
+                R.drawable.qs_media_rec_scrim).mutate();
+        return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
+                MEDIA_REC_SCRIM_START_ALPHA, MEDIA_REC_SCRIM_END_ALPHA);
+    }
+
+    private LayerDrawable setupGradientColorOnDrawable(Drawable albumArt, GradientDrawable gradient,
+            ColorScheme mutableColorScheme, float startAlpha, float endAlpha) {
         gradient.setColors(new int[] {
                 ColorUtilKt.getColorWithAlpha(
                         MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
-                        0.25f),
+                        startAlpha),
                 ColorUtilKt.getColorWithAlpha(
                         MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
-                        0.9f),
+                        endAlpha),
         });
         return new LayerDrawable(new Drawable[] { albumArt, gradient });
     }
@@ -1589,6 +1626,29 @@
     }
 
     /**
+     * Scale artwork to fill the background of media covers in recommendation card.
+     */
+    @UiThread
+    private Drawable getScaledRecommendationCover(Icon artworkIcon, int width, int height) {
+        if (width == 0 || height == 0) {
+            return null;
+        }
+        if (artworkIcon != null) {
+            Bitmap bitmap;
+            if (artworkIcon.getType() == Icon.TYPE_BITMAP
+                    || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
+                Bitmap artworkBitmap = artworkIcon.getBitmap();
+                if (artworkBitmap != null) {
+                    bitmap = Bitmap.createScaledBitmap(artworkIcon.getBitmap(), width,
+                            height, false);
+                    return new BitmapDrawable(mContext.getResources(), bitmap);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Get the current media controller
      *
      * @return the controller
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 7855cdf..cf5ecdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -389,6 +389,9 @@
             boolean isStrongBiometric) {
         Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
         if (mUpdateMonitor.isGoingToSleep()) {
+            mLogger.deferringAuthenticationDueToSleep(userId,
+                    biometricSourceType,
+                    mPendingAuthenticated != null);
             mPendingAuthenticated = new PendingAuthenticated(userId, biometricSourceType,
                     isStrongBiometric);
             Trace.endSection();
@@ -795,6 +798,7 @@
                 public void onFinishedGoingToSleep() {
                     Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
                     if (mPendingAuthenticated != null) {
+                        mLogger.finishedGoingToSleepWithPendingAuth();
                         PendingAuthenticated pendingAuthenticated = mPendingAuthenticated;
                         // Post this to make sure it's executed after the device is fully locked.
                         mHandler.post(() -> onBiometricAuthenticated(pendingAuthenticated.userId,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index b88531e..ae715b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -429,7 +429,6 @@
         }
 
         dispatchAlwaysOnEvent();
-        mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
     }
 
     @Override
@@ -469,6 +468,7 @@
         for (Callback callback : mCallbacks) {
             callback.onAlwaysOnChange();
         }
+        mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
     }
 
     private boolean getPostureSpecificBool(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 726b234..edfc95f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -16,11 +16,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
 
 import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions;
 
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -579,8 +581,14 @@
             EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
                     entry.getKey());
             mCentralSurfaces.wakeUpForFullScreenIntent();
-            fullScreenIntent.send();
+
+            ActivityOptions options = ActivityOptions.makeBasic();
+            options.setPendingIntentBackgroundActivityStartMode(
+                    MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+            fullScreenIntent.sendAndReturnResult(null, 0, null, null, null, null,
+                    options.toBundle());
             entry.notifyFullScreenIntentLaunched();
+
             mMetricsLogger.count("note_fullscreen", 1);
 
             String activityName;
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 81ae6e8..c72853e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -115,6 +115,17 @@
     }
 
     /**
+     * Provide a Long running Executor.
+     */
+    @Provides
+    @SysUISingleton
+    @LongRunning
+    public static DelayableExecutor provideLongRunningDelayableExecutor(
+            @LongRunning Looper looper) {
+        return new ExecutorImpl(looper);
+    }
+
+    /**
      * Provide a Background-Thread Executor.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 76a01b9..54b3030 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -41,7 +41,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.LongRunning;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -71,17 +71,16 @@
     private HandlerThread mWorker;
 
     // used for most tasks (call canvas.drawBitmap, load/unload the bitmap)
-    @Background
-    private final DelayableExecutor mBackgroundExecutor;
+    @LongRunning
+    private final DelayableExecutor mLongExecutor;
 
     // wait at least this duration before unloading the bitmap
     private static final int DELAY_UNLOAD_BITMAP = 2000;
 
     @Inject
-    public ImageWallpaper(@Background DelayableExecutor backgroundExecutor,
-            UserTracker userTracker) {
+    public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker) {
         super();
-        mBackgroundExecutor = backgroundExecutor;
+        mLongExecutor = longExecutor;
         mUserTracker = userTracker;
     }
 
@@ -131,7 +130,7 @@
             setFixedSizeAllowed(true);
             setShowForAllUsers(true);
             mWallpaperLocalColorExtractor = new WallpaperLocalColorExtractor(
-                    mBackgroundExecutor,
+                    mLongExecutor,
                     new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
                         @Override
                         public void onColorsProcessed(List<RectF> regions,
@@ -230,7 +229,7 @@
         }
 
         private void drawFrame() {
-            mBackgroundExecutor.execute(this::drawFrameSynchronized);
+            mLongExecutor.execute(this::drawFrameSynchronized);
         }
 
         private void drawFrameSynchronized() {
@@ -285,7 +284,7 @@
         }
 
         private void unloadBitmapIfNotUsed() {
-            mBackgroundExecutor.execute(this::unloadBitmapIfNotUsedSynchronized);
+            mLongExecutor.execute(this::unloadBitmapIfNotUsedSynchronized);
         }
 
         private void unloadBitmapIfNotUsedSynchronized() {
@@ -381,7 +380,7 @@
                  *   - the mini bitmap from color extractor is recomputed
                  *   - the DELAY_UNLOAD_BITMAP has passed
                  */
-                mBackgroundExecutor.executeDelayed(
+                mLongExecutor.executeDelayed(
                         this::unloadBitmapIfNotUsedSynchronized, DELAY_UNLOAD_BITMAP);
             }
             // even if the bitmap cannot be loaded, call reportEngineShown
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
index 988fd71..1e8446f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
@@ -29,7 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.LongRunning;
 import com.android.systemui.util.Assert;
 
 import java.io.FileDescriptor;
@@ -66,8 +66,8 @@
     private final List<RectF> mPendingRegions = new ArrayList<>();
     private final Set<RectF> mProcessedRegions = new ArraySet<>();
 
-    @Background
-    private final Executor mBackgroundExecutor;
+    @LongRunning
+    private final Executor mLongExecutor;
 
     private final WallpaperLocalColorExtractorCallback mWallpaperLocalColorExtractorCallback;
 
@@ -101,13 +101,13 @@
 
     /**
      * Creates a new color extractor.
-     * @param backgroundExecutor the executor on which the color extraction will be performed
+     * @param longExecutor the executor on which the color extraction will be performed
      * @param wallpaperLocalColorExtractorCallback an interface to handle the callbacks from
      *                                        the color extractor.
      */
-    public WallpaperLocalColorExtractor(@Background Executor backgroundExecutor,
+    public WallpaperLocalColorExtractor(@LongRunning Executor longExecutor,
             WallpaperLocalColorExtractorCallback wallpaperLocalColorExtractorCallback) {
-        mBackgroundExecutor = backgroundExecutor;
+        mLongExecutor = longExecutor;
         mWallpaperLocalColorExtractorCallback = wallpaperLocalColorExtractorCallback;
     }
 
@@ -117,7 +117,7 @@
      * not recomputed.
      */
     public void setDisplayDimensions(int displayWidth, int displayHeight) {
-        mBackgroundExecutor.execute(() ->
+        mLongExecutor.execute(() ->
                 setDisplayDimensionsSynchronized(displayWidth, displayHeight));
     }
 
@@ -144,7 +144,7 @@
      * @param bitmap the new wallpaper
      */
     public void onBitmapChanged(@NonNull Bitmap bitmap) {
-        mBackgroundExecutor.execute(() -> onBitmapChangedSynchronized(bitmap));
+        mLongExecutor.execute(() -> onBitmapChangedSynchronized(bitmap));
     }
 
     private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) {
@@ -167,7 +167,7 @@
      * @param pages the total number of pages of the launcher
      */
     public void onPageChanged(int pages) {
-        mBackgroundExecutor.execute(() -> onPageChangedSynchronized(pages));
+        mLongExecutor.execute(() -> onPageChangedSynchronized(pages));
     }
 
     private void onPageChangedSynchronized(int pages) {
@@ -194,7 +194,7 @@
      */
     public void addLocalColorsAreas(@NonNull List<RectF> regions) {
         if (regions.size() > 0) {
-            mBackgroundExecutor.execute(() -> addLocalColorsAreasSynchronized(regions));
+            mLongExecutor.execute(() -> addLocalColorsAreasSynchronized(regions));
         } else {
             Log.w(TAG, "Attempt to add colors with an empty list");
         }
@@ -218,7 +218,7 @@
      * @param regions The areas of interest in our wallpaper (in screen pixel coordinates)
      */
     public void removeLocalColorAreas(@NonNull List<RectF> regions) {
-        mBackgroundExecutor.execute(() -> removeLocalColorAreasSynchronized(regions));
+        mLongExecutor.execute(() -> removeLocalColorAreasSynchronized(regions));
     }
 
     private void removeLocalColorAreasSynchronized(@NonNull List<RectF> regions) {
@@ -236,7 +236,7 @@
      * Clean up the memory (in particular, the mini bitmap) used by this class.
      */
     public void cleanUp() {
-        mBackgroundExecutor.execute(this::cleanUpSynchronized);
+        mLongExecutor.execute(this::cleanUpSynchronized);
     }
 
     private void cleanUpSynchronized() {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 9600fd8..08f7eae 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -25,7 +25,6 @@
 import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
-import static com.android.systemui.flags.Flags.WM_BUBBLE_BAR;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -364,7 +363,6 @@
                 });
             }
         };
-        mBubbles.setBubbleBarEnabled(featureFlags.isEnabled(WM_BUBBLE_BAR));
         mBubbles.setSysuiProxy(mSysuiProxy);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index b765ab3..a245c01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -25,7 +25,9 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.keyguard.logging.KeyguardLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -109,6 +111,7 @@
             udfpsControllerProvider,
             statusBarStateController,
             featureFlags,
+            KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
             rippleView
         )
         controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index faa5db4..ab6d5b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -94,7 +94,9 @@
         mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3));
         mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300));
         mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301));
-        mClassifier.onTouchEvent(appendUpEvent(1, 19, 501));
+        mClassifier.onTouchEvent(appendMoveEvent(1, 19, 501));
+        mClassifier.onTouchEvent(appendUpEvent(1, 19, 501)); //event will be dropped
+
         assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index 2edc3d3..8eadadf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -75,16 +75,17 @@
     }
 
     @Test
-    public void test_trackMotionEvents() {
+    public void test_trackMotionEvents_dropUpEvent() {
         mDataProvider.onMotionEvent(appendDownEvent(2, 9));
         mDataProvider.onMotionEvent(appendMoveEvent(4, 7));
-        mDataProvider.onMotionEvent(appendUpEvent(6, 5));
+        mDataProvider.onMotionEvent(appendMoveEvent(6, 5));
+        mDataProvider.onMotionEvent(appendUpEvent(0, 0)); // event will be dropped
         List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
 
         assertThat(motionEventList.size()).isEqualTo(3);
         assertThat(motionEventList.get(0).getActionMasked()).isEqualTo(MotionEvent.ACTION_DOWN);
         assertThat(motionEventList.get(1).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE);
-        assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_UP);
+        assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE);
         assertThat(motionEventList.get(0).getEventTime()).isEqualTo(1L);
         assertThat(motionEventList.get(1).getEventTime()).isEqualTo(2L);
         assertThat(motionEventList.get(2).getEventTime()).isEqualTo(3L);
@@ -97,6 +98,28 @@
     }
 
     @Test
+    public void test_trackMotionEvents_keepUpEvent() {
+        mDataProvider.onMotionEvent(appendDownEvent(2, 9));
+        mDataProvider.onMotionEvent(appendMoveEvent(4, 7));
+        mDataProvider.onMotionEvent(appendUpEvent(0, 0, 100));
+        List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
+
+        assertThat(motionEventList.size()).isEqualTo(3);
+        assertThat(motionEventList.get(0).getActionMasked()).isEqualTo(MotionEvent.ACTION_DOWN);
+        assertThat(motionEventList.get(1).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE);
+        assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_UP);
+        assertThat(motionEventList.get(0).getEventTime()).isEqualTo(1L);
+        assertThat(motionEventList.get(1).getEventTime()).isEqualTo(2L);
+        assertThat(motionEventList.get(2).getEventTime()).isEqualTo(100);
+        assertThat(motionEventList.get(0).getX()).isEqualTo(2f);
+        assertThat(motionEventList.get(1).getX()).isEqualTo(4f);
+        assertThat(motionEventList.get(2).getX()).isEqualTo(0f);
+        assertThat(motionEventList.get(0).getY()).isEqualTo(9f);
+        assertThat(motionEventList.get(1).getY()).isEqualTo(7f);
+        assertThat(motionEventList.get(2).getY()).isEqualTo(0f);
+    }
+
+    @Test
     public void test_trackRecentMotionEvents() {
         mDataProvider.onMotionEvent(appendDownEvent(2, 9, 1));
         mDataProvider.onMotionEvent(appendMoveEvent(4, 7, 800));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
index c343c20..ae2b8bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
@@ -68,6 +68,15 @@
     }
 
     @Test
+    public void testPass_dropClosingUpEvent() {
+        appendMoveEvent(0, 0);
+        appendMoveEvent(0, 100);
+        appendMoveEvent(0, 200);
+        appendUpEvent(0, 180); // this event would push us over the maxDevianceY
+        assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
+    }
+
+    @Test
     public void testPass_fewTouchesHorizontal() {
         assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
         appendMoveEvent(0, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 2099281..c2fb904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -446,7 +446,7 @@
         mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
         when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString()))
                 .thenReturn(true);
-        when(mClipboardUtils.getAction(any(CharSequence.class), any(TextLinks.class), anyString()))
+        when(mClipboardUtils.getAction(any(TextLinks.class), anyString()))
                 .thenReturn(Optional.of(Mockito.mock(RemoteAction.class)));
         when(mClipboardOverlayView.post(any(Runnable.class))).thenAnswer(new Answer<Object>() {
             @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
index aea6be3..3d8f04e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.when;
 
@@ -77,6 +78,74 @@
 
     @Test
     public void test_getAction_noLinks_returnsEmptyOptional() {
+        Optional<RemoteAction> action =
+                mClipboardUtils.getAction(Mockito.mock(TextLinks.class), "abc");
+
+        assertTrue(action.isEmpty());
+    }
+
+    @Test
+    public void test_getAction_returnsFirstLink() {
+        TextLinks links = getFakeTextLinksBuilder().build();
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn(
+                classificationA, classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(links, "test").orElse(null);
+
+        assertEquals(actionA, result);
+    }
+
+    @Test
+    public void test_getAction_skipsMatchingComponent() {
+        TextLinks links = getFakeTextLinksBuilder().build();
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn(
+                classificationA, classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(links, "abc").orElse(null);
+
+        assertEquals(actionB, result);
+    }
+
+    @Test
+    public void test_getAction_skipsShortEntity() {
+        TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22");
+        final Map<String, Float> scores = new ArrayMap<>();
+        scores.put(TextClassifier.TYPE_EMAIL, 1f);
+        textLinks.addLink(20, 22, scores);
+        textLinks.addLink(0, 22, scores);
+
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), eq(20), eq(22), isNull())).thenReturn(
+                classificationA);
+        when(mTextClassifier.classifyText(anyString(), eq(0), eq(22), isNull())).thenReturn(
+                classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(textLinks.build(), "test").orElse(null);
+
+        assertEquals(actionB, result);
+    }
+
+    // TODO(b/267162944): Next four tests (marked "legacy") are obsolete once
+    //  CLIPBOARD_MINIMIZED_LAYOUT flag is released and removed
+    @Test
+    public void test_getAction_noLinks_returnsEmptyOptional_legacy() {
         ClipData.Item item = new ClipData.Item("no text links");
         item.setTextLinks(Mockito.mock(TextLinks.class));
 
@@ -86,8 +155,8 @@
     }
 
     @Test
-    public void test_getAction_returnsFirstLink() {
-        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks());
+    public void test_getAction_returnsFirstLink_legacy() {
+        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinksBuilder().build());
         when(mClipDataItem.getText()).thenReturn("");
         RemoteAction actionA = constructRemoteAction("abc");
         RemoteAction actionB = constructRemoteAction("def");
@@ -98,14 +167,14 @@
         when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn(
                 classificationA, classificationB);
 
-        RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "def").orElse(null);
+        RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "test").orElse(null);
 
         assertEquals(actionA, result);
     }
 
     @Test
-    public void test_getAction_skipsMatchingComponent() {
-        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks());
+    public void test_getAction_skipsMatchingComponent_legacy() {
+        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinksBuilder().build());
         when(mClipDataItem.getText()).thenReturn("");
         RemoteAction actionA = constructRemoteAction("abc");
         RemoteAction actionB = constructRemoteAction("def");
@@ -122,6 +191,33 @@
     }
 
     @Test
+    public void test_getAction_skipsShortEntity_legacy() {
+        TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22");
+        final Map<String, Float> scores = new ArrayMap<>();
+        scores.put(TextClassifier.TYPE_EMAIL, 1f);
+        textLinks.addLink(20, 22, scores);
+        textLinks.addLink(0, 22, scores);
+
+        when(mClipDataItem.getTextLinks()).thenReturn(textLinks.build());
+        when(mClipDataItem.getText()).thenReturn(textLinks.build().getText());
+
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), eq(20), eq(22), isNull())).thenReturn(
+                classificationA);
+        when(mTextClassifier.classifyText(anyString(), eq(0), eq(22), isNull())).thenReturn(
+                classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "test").orElse(null);
+
+        assertEquals(actionB, result);
+    }
+
+    @Test
     public void test_extra_withPackage_returnsTrue() {
         PersistableBundle b = new PersistableBundle();
         b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true);
@@ -184,12 +280,12 @@
         return action;
     }
 
-    private static TextLinks getFakeTextLinks() {
-        TextLinks.Builder textLinks = new TextLinks.Builder("test");
+    private static TextLinks.Builder getFakeTextLinksBuilder() {
+        TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22");
         final Map<String, Float> scores = new ArrayMap<>();
         scores.put(TextClassifier.TYPE_EMAIL, 1f);
-        textLinks.addLink(0, 0, scores);
-        textLinks.addLink(0, 0, scores);
-        return textLinks.build();
+        textLinks.addLink(0, 22, scores);
+        textLinks.addLink(0, 22, scores);
+        return textLinks;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
new file mode 100644
index 0000000..ec94cde
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyboardBacklightInteractorTest : SysuiTestCase() {
+
+    private val keyboardRepository = FakeKeyboardRepository()
+    private lateinit var underTest: KeyboardBacklightInteractor
+
+    @Before
+    fun setUp() {
+        underTest = KeyboardBacklightInteractor(keyboardRepository)
+    }
+
+    @Test
+    fun emitsNull_whenKeyboardJustConnected() = runTest {
+        val latest by collectLastValue(underTest.backlight)
+        keyboardRepository.setKeyboardConnected(true)
+
+        assertThat(latest).isNull()
+    }
+
+    @Test
+    fun emitsBacklight_whenKeyboardConnectedAndBacklightChanged() = runTest {
+        keyboardRepository.setKeyboardConnected(true)
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        assertThat(underTest.backlight.first()).isEqualTo(BacklightModel(1, 5))
+    }
+
+    @Test
+    fun emitsNull_afterKeyboardDisconnecting() = runTest {
+        val latest by collectLastValue(underTest.backlight)
+        keyboardRepository.setKeyboardConnected(true)
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        keyboardRepository.setKeyboardConnected(false)
+
+        assertThat(latest).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
new file mode 100644
index 0000000..ec05d10
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class BacklightDialogViewModelTest : SysuiTestCase() {
+
+    private val keyboardRepository = FakeKeyboardRepository()
+    private lateinit var underTest: BacklightDialogViewModel
+    @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
+    private val timeoutMillis = 3000L
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
+            .thenReturn(timeoutMillis.toInt())
+        underTest =
+            BacklightDialogViewModel(
+                KeyboardBacklightInteractor(keyboardRepository),
+                accessibilityManagerWrapper
+            )
+        keyboardRepository.setKeyboardConnected(true)
+    }
+
+    @Test
+    fun emitsViewModel_whenBacklightChanged() = runTest {
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        assertThat(underTest.dialogContent.first()).isEqualTo(BacklightDialogContentViewModel(1, 5))
+    }
+
+    @Test
+    fun emitsNull_afterTimeout() = runTest {
+        val latest by collectLastValue(underTest.dialogContent)
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
+        advanceTimeBy(timeoutMillis + 1)
+        assertThat(latest).isNull()
+    }
+
+    @Test
+    fun emitsNull_after5secDelay_fromLastBacklightChange() = runTest {
+        val latest by collectLastValue(underTest.dialogContent)
+        keyboardRepository.setKeyboardConnected(true)
+
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
+
+        advanceTimeBy(timeoutMillis * 2 / 3)
+        // timeout yet to pass, no new emission
+        keyboardRepository.setBacklight(BacklightModel(2, 5))
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
+
+        advanceTimeBy(timeoutMillis * 2 / 3)
+        // timeout refreshed because of last `setBacklight`, still content present
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
+
+        advanceTimeBy(timeoutMillis * 2 / 3)
+        // finally timeout reached and null emitted
+        assertThat(latest).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 2209852..fe9098f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -358,92 +358,6 @@
         }
 
     @Test
-    fun `OCCLUDED to GONE`() =
-        testScope.runTest {
-            // GIVEN a device on lockscreen
-            keyguardRepository.setKeyguardShowing(true)
-            runCurrent()
-
-            // GIVEN a prior transition has run to OCCLUDED
-            runner.startTransition(
-                testScope,
-                TransitionInfo(
-                    ownerName = "",
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.OCCLUDED,
-                    animator =
-                        ValueAnimator().apply {
-                            duration = 10
-                            interpolator = Interpolators.LINEAR
-                        },
-                )
-            )
-            keyguardRepository.setKeyguardOccluded(true)
-            runCurrent()
-            reset(mockTransitionRepository)
-
-            // WHEN keyguard goes away
-            keyguardRepository.setKeyguardShowing(false)
-            // AND occlusion ends
-            keyguardRepository.setKeyguardOccluded(false)
-            runCurrent()
-
-            val info =
-                withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
-                }
-            // THEN a transition to GONE should occur
-            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
-            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
-            assertThat(info.to).isEqualTo(KeyguardState.GONE)
-            assertThat(info.animator).isNotNull()
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    fun `OCCLUDED to LOCKSCREEN`() =
-        testScope.runTest {
-            // GIVEN a device on lockscreen
-            keyguardRepository.setKeyguardShowing(true)
-            runCurrent()
-
-            // GIVEN a prior transition has run to OCCLUDED
-            runner.startTransition(
-                testScope,
-                TransitionInfo(
-                    ownerName = "",
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.OCCLUDED,
-                    animator =
-                        ValueAnimator().apply {
-                            duration = 10
-                            interpolator = Interpolators.LINEAR
-                        },
-                )
-            )
-            keyguardRepository.setKeyguardOccluded(true)
-            runCurrent()
-            reset(mockTransitionRepository)
-
-            // WHEN occlusion ends
-            keyguardRepository.setKeyguardOccluded(false)
-            runCurrent()
-
-            val info =
-                withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
-                }
-            // THEN a transition to LOCKSCREEN should occur
-            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
-            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
-            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
-            assertThat(info.animator).isNotNull()
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
     fun `LOCKSCREEN to DOZING`() =
         testScope.runTest {
             // GIVEN a device with AOD not available
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 55a33b6..fd353af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -27,6 +27,7 @@
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.Color
+import android.graphics.Matrix
 import android.graphics.drawable.Animatable2
 import android.graphics.drawable.AnimatedVectorDrawable
 import android.graphics.drawable.Drawable
@@ -78,6 +79,8 @@
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.media.dialog.MediaOutputDialogFactory
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -214,6 +217,7 @@
     @Mock private lateinit var recSubtitleMock2: TextView
     @Mock private lateinit var recSubtitleMock3: TextView
     @Mock private lateinit var coverItem: ImageView
+    @Mock private lateinit var matrix: Matrix
     private lateinit var coverItem1: ImageView
     private lateinit var coverItem2: ImageView
     private lateinit var coverItem3: ImageView
@@ -700,6 +704,46 @@
     }
 
     @Test
+    fun addTwoPlayerGradients_differentStates() {
+        // Setup redArtwork and its color scheme.
+        val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+        val redCanvas = Canvas(redBmp)
+        redCanvas.drawColor(Color.RED)
+        val redArt = Icon.createWithBitmap(redBmp)
+        val redWallpaperColor = player.getWallpaperColor(redArt)
+        val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT)
+
+        // Setup greenArt and its color scheme.
+        val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+        val greenCanvas = Canvas(greenBmp)
+        greenCanvas.drawColor(Color.GREEN)
+        val greenArt = Icon.createWithBitmap(greenBmp)
+        val greenWallpaperColor = player.getWallpaperColor(greenArt)
+        val greenColorScheme = ColorScheme(greenWallpaperColor, true, Style.CONTENT)
+
+        // Add gradient to both icons.
+        val redArtwork = player.addGradientToPlayerAlbum(redArt, redColorScheme, 10, 10)
+        val greenArtwork = player.addGradientToPlayerAlbum(greenArt, greenColorScheme, 10, 10)
+
+        // They should have different constant states as they have different gradient color.
+        assertThat(redArtwork.getDrawable(1).constantState)
+            .isNotEqualTo(greenArtwork.getDrawable(1).constantState)
+    }
+
+    @Test
+    fun getWallpaperColor_recycledBitmap_notCrashing() {
+        // Setup redArt icon.
+        val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+        val redArt = Icon.createWithBitmap(redBmp)
+
+        // Recycle bitmap of redArt icon.
+        redArt.bitmap.recycle()
+
+        // get wallpaperColor without illegal exception.
+        player.getWallpaperColor(redArt)
+    }
+
+    @Test
     fun bind_seekBarDisabled_hasActions_seekBarVisibilityIsSetToInvisible() {
         useRealConstraintSets()
 
@@ -2092,6 +2136,7 @@
             .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3))
         whenever(recommendationViewHolder.mediaSubtitles)
             .thenReturn(listOf(recSubtitleMock1, recSubtitleMock2, recSubtitleMock3))
+        whenever(coverItem.imageMatrix).thenReturn(matrix)
 
         val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
         val canvas = Canvas(bmp)
@@ -2127,6 +2172,7 @@
         verify(recCardTitle).setTextColor(any<Int>())
         verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
         verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
+        verify(coverItem, times(3)).imageMatrix = any()
     }
 
     @Test
@@ -2189,6 +2235,34 @@
     }
 
     @Test
+    fun addTwoRecommendationGradients_differentStates() {
+        // Setup redArtwork and its color scheme.
+        val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+        val redCanvas = Canvas(redBmp)
+        redCanvas.drawColor(Color.RED)
+        val redArt = Icon.createWithBitmap(redBmp)
+        val redWallpaperColor = player.getWallpaperColor(redArt)
+        val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT)
+
+        // Setup greenArt and its color scheme.
+        val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+        val greenCanvas = Canvas(greenBmp)
+        greenCanvas.drawColor(Color.GREEN)
+        val greenArt = Icon.createWithBitmap(greenBmp)
+        val greenWallpaperColor = player.getWallpaperColor(greenArt)
+        val greenColorScheme = ColorScheme(greenWallpaperColor, true, Style.CONTENT)
+
+        // Add gradient to both icons.
+        val redArtwork = player.addGradientToRecommendationAlbum(redArt, redColorScheme, 10, 10)
+        val greenArtwork =
+            player.addGradientToRecommendationAlbum(greenArt, greenColorScheme, 10, 10)
+
+        // They should have different constant states as they have different gradient color.
+        assertThat(redArtwork.getDrawable(1).constantState)
+            .isNotEqualTo(greenArtwork.getDrawable(1).constantState)
+    }
+
+    @Test
     fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() {
         fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
         val semanticActions =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index eb5edbc..f5b7ca8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -180,6 +180,7 @@
         when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
         mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
 
+        verify(mScreenOffAnimationController).onAlwaysOnChanged(false);
         assertThat(mDozeParameters.getAlwaysOn()).isFalse();
     }
 
@@ -196,13 +197,16 @@
         mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
 
         verify(callback, times(2)).onAlwaysOnChange();
+        verify(mScreenOffAnimationController, times(2)).onAlwaysOnChanged(false);
         assertThat(mDozeParameters.getAlwaysOn()).isFalse();
 
+        reset(mScreenOffAnimationController);
         reset(callback);
         when(mBatteryController.isAodPowerSave()).thenReturn(false);
         mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
 
         verify(callback).onAlwaysOnChange();
+        verify(mScreenOffAnimationController).onAlwaysOnChanged(true);
         assertThat(mDozeParameters.getAlwaysOn()).isTrue();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
index 85cfef7..fd368eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
@@ -16,22 +16,24 @@
 
 package com.android.systemui.unfold.updates
 
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.os.Looper
 import android.testing.AndroidTestingRunner
-import android.view.IRotationWatcher
-import android.view.IWindowManager
+import android.view.Display
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.os.FakeHandler
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
@@ -42,19 +44,23 @@
 
     private lateinit var rotationChangeProvider: RotationChangeProvider
 
-    @Mock lateinit var windowManagerInterface: IWindowManager
+    @Mock lateinit var displayManager: DisplayManager
     @Mock lateinit var listener: RotationListener
-    @Captor lateinit var rotationWatcher: ArgumentCaptor<IRotationWatcher>
-    private val fakeExecutor = FakeExecutor(FakeSystemClock())
+    @Mock lateinit var display: Display
+    @Captor lateinit var displayListener: ArgumentCaptor<DisplayManager.DisplayListener>
+    private val fakeHandler = FakeHandler(Looper.getMainLooper())
+
+    private lateinit var spyContext: Context
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        rotationChangeProvider =
-            RotationChangeProvider(windowManagerInterface, context, fakeExecutor)
+        spyContext = spy(context)
+        whenever(spyContext.display).thenReturn(display)
+        rotationChangeProvider = RotationChangeProvider(displayManager, spyContext, fakeHandler)
         rotationChangeProvider.addCallback(listener)
-        fakeExecutor.runAllReady()
-        verify(windowManagerInterface).watchRotation(rotationWatcher.capture(), anyInt())
+        fakeHandler.dispatchQueuedMessages()
+        verify(displayManager).registerDisplayListener(displayListener.capture(), any())
     }
 
     @Test
@@ -70,15 +76,16 @@
         verify(listener).onRotationChanged(42)
 
         rotationChangeProvider.removeCallback(listener)
-        fakeExecutor.runAllReady()
+        fakeHandler.dispatchQueuedMessages()
         sendRotationUpdate(43)
 
-        verify(windowManagerInterface).removeRotationWatcher(any())
+        verify(displayManager).unregisterDisplayListener(any())
         verifyNoMoreInteractions(listener)
     }
 
     private fun sendRotationUpdate(newRotation: Int) {
-        rotationWatcher.value.onRotationChanged(newRotation)
-        fakeExecutor.runAllReady()
+        whenever(display.rotation).thenReturn(newRotation)
+        displayListener.allValues.forEach { it.onDisplayChanged(display.displayId) }
+        fakeHandler.dispatchQueuedMessages()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 31cce4f..468c5a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -88,7 +88,7 @@
     @Mock
     private Bitmap mWallpaperBitmap;
     FakeSystemClock mFakeSystemClock = new FakeSystemClock();
-    FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
+    FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
 
     @Before
     public void setUp() throws Exception {
@@ -125,7 +125,7 @@
 
     @Test
     public void testBitmapWallpaper_normal() {
-        // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
+        // Will use an image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
         // Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
         int bitmapSide = DISPLAY_WIDTH;
         testSurfaceHelper(
@@ -137,7 +137,7 @@
 
     @Test
     public void testBitmapWallpaper_low_resolution() {
-        // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
+        // Will use an image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
         // Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
         testSurfaceHelper(LOW_BMP_WIDTH /* bitmapWidth */,
                 LOW_BMP_HEIGHT /* bitmapHeight */,
@@ -161,13 +161,13 @@
         ImageWallpaper.CanvasEngine spyEngine = getSpyEngine();
         spyEngine.onCreate(mSurfaceHolder);
         spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
-        assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1);
+        assertThat(mFakeExecutor.numPending()).isAtLeast(1);
 
         int n = 0;
-        while (mFakeBackgroundExecutor.numPending() >= 1) {
+        while (mFakeExecutor.numPending() >= 1) {
             n++;
             assertThat(n).isAtMost(10);
-            mFakeBackgroundExecutor.runNextReady();
+            mFakeExecutor.runNextReady();
             mFakeSystemClock.advanceTime(1000);
         }
 
@@ -176,7 +176,7 @@
     }
 
     private ImageWallpaper createImageWallpaper() {
-        return new ImageWallpaper(mFakeBackgroundExecutor, mUserTracker) {
+        return new ImageWallpaper(mFakeExecutor, mUserTracker) {
             @Override
             public Engine onCreateEngine() {
                 return new CanvasEngine() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt
new file mode 100644
index 0000000..4e43546
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.data.repository
+
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+
+class FakeKeyboardRepository : KeyboardRepository {
+
+    private val _keyboardConnected = MutableStateFlow(false)
+    override val keyboardConnected: Flow<Boolean> = _keyboardConnected
+
+    private val _backlightState: MutableStateFlow<BacklightModel?> = MutableStateFlow(null)
+    // filtering to make sure backlight doesn't have default initial value
+    override val backlight: Flow<BacklightModel> = _backlightState.filterNotNull()
+
+    fun setBacklight(state: BacklightModel) {
+        _backlightState.value = state
+    }
+
+    fun setKeyboardConnected(connected: Boolean) {
+        _keyboardConnected.value = connected
+    }
+}
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 180b611..2e0a946 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -35,6 +35,7 @@
     ],
     kotlincflags: ["-Xjvm-default=enable"],
     java_version: "1.8",
+    sdk_version: "current",
     min_sdk_version: "current",
     plugins: ["dagger2-compiler"],
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index 068347c..a079668 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -19,8 +19,8 @@
 import android.content.ContentResolver
 import android.content.Context
 import android.hardware.SensorManager
+import android.hardware.display.DisplayManager
 import android.os.Handler
-import android.view.IWindowManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
@@ -61,7 +61,7 @@
             @BindsInstance @UnfoldMain executor: Executor,
             @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
             @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
-            @BindsInstance windowManager: IWindowManager,
+            @BindsInstance displayManager: DisplayManager,
             @BindsInstance contentResolver: ContentResolver = context.contentResolver
         ): UnfoldSharedComponent
     }
@@ -84,8 +84,9 @@
             @BindsInstance context: Context,
             @BindsInstance config: UnfoldTransitionConfig,
             @BindsInstance @UnfoldMain executor: Executor,
+            @BindsInstance @UnfoldMain handler: Handler,
             @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
-            @BindsInstance windowManager: IWindowManager,
+            @BindsInstance displayManager: DisplayManager,
             @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
         ): RemoteUnfoldSharedComponent
     }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 8eb79df..1839919 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -19,8 +19,8 @@
 
 import android.content.Context
 import android.hardware.SensorManager
+import android.hardware.display.DisplayManager
 import android.os.Handler
-import android.view.IWindowManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
@@ -47,7 +47,7 @@
         mainExecutor: Executor,
         singleThreadBgExecutor: Executor,
         tracingTagPrefix: String,
-        windowManager: IWindowManager,
+        displayManager: DisplayManager,
 ): UnfoldSharedComponent =
         DaggerUnfoldSharedComponent.factory()
                 .create(
@@ -61,7 +61,7 @@
                         mainExecutor,
                         singleThreadBgExecutor,
                         tracingTagPrefix,
-                        windowManager,
+                        displayManager,
                 )
 
 /**
@@ -73,16 +73,18 @@
         context: Context,
         config: UnfoldTransitionConfig,
         mainExecutor: Executor,
+        mainHandler: Handler,
         singleThreadBgExecutor: Executor,
         tracingTagPrefix: String,
-        windowManager: IWindowManager,
+        displayManager: DisplayManager,
         ): RemoteUnfoldSharedComponent =
         DaggerRemoteUnfoldSharedComponent.factory()
                 .create(
                         context,
                         config,
                         mainExecutor,
+                        mainHandler,
                         singleThreadBgExecutor,
-                        windowManager,
+                        displayManager,
                         tracingTagPrefix,
                 )
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index d19b414..28e4936 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.unfold.progress
 
 import android.os.Trace
-import android.os.Trace.TRACE_TAG_APP
 import android.util.Log
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -110,7 +109,7 @@
 
         if (DEBUG) {
             Log.d(TAG, "onFoldUpdate = ${update.name()}")
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "fold_update", update)
+            Trace.setCounter("fold_update", update.toLong())
         }
     }
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 82fd225..d653fc7 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -119,7 +119,7 @@
                     "lastHingeAngle: $lastHingeAngle, " +
                     "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition"
             )
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "hinge_angle", angle.toInt())
+            Trace.setCounter( "hinge_angle", angle.toLong())
         }
 
         val currentDirection =
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index 0cf8224..ce8f1a1 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -17,36 +17,32 @@
 package com.android.systemui.unfold.updates
 
 import android.content.Context
+import android.hardware.display.DisplayManager
+import android.os.Handler
 import android.os.RemoteException
-import android.view.IRotationWatcher
-import android.view.IWindowManager
-import android.view.Surface.Rotation
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.util.CallbackController
-import java.util.concurrent.Executor
 import javax.inject.Inject
 
 /**
- * Allows to subscribe to rotation changes.
- *
- * This is needed as rotation updates from [IWindowManager] are received in a binder thread, while
- * most of the times we want them in the main one. Updates are provided for the display associated
+ * Allows to subscribe to rotation changes. Updates are provided for the display associated
  * to [context].
  */
 class RotationChangeProvider
 @Inject
 constructor(
-    private val windowManagerInterface: IWindowManager,
+    private val displayManager: DisplayManager,
     private val context: Context,
-    @UnfoldMain private val mainExecutor: Executor,
+    @UnfoldMain private val mainHandler: Handler,
 ) : CallbackController<RotationChangeProvider.RotationListener> {
 
     private val listeners = mutableListOf<RotationListener>()
 
-    private val rotationWatcher = RotationWatcher()
+    private val displayListener = RotationDisplayListener()
+    private var lastRotation: Int? = null
 
     override fun addCallback(listener: RotationListener) {
-        mainExecutor.execute {
+        mainHandler.post {
             if (listeners.isEmpty()) {
                 subscribeToRotation()
             }
@@ -55,17 +51,18 @@
     }
 
     override fun removeCallback(listener: RotationListener) {
-        mainExecutor.execute {
+        mainHandler.post {
             listeners -= listener
             if (listeners.isEmpty()) {
                 unsubscribeToRotation()
+                lastRotation = null
             }
         }
     }
 
     private fun subscribeToRotation() {
         try {
-            windowManagerInterface.watchRotation(rotationWatcher, context.displayId)
+            displayManager.registerDisplayListener(displayListener, mainHandler)
         } catch (e: RemoteException) {
             throw e.rethrowFromSystemServer()
         }
@@ -73,7 +70,7 @@
 
     private fun unsubscribeToRotation() {
         try {
-            windowManagerInterface.removeRotationWatcher(rotationWatcher)
+            displayManager.unregisterDisplayListener(displayListener)
         } catch (e: RemoteException) {
             throw e.rethrowFromSystemServer()
         }
@@ -82,12 +79,25 @@
     /** Gets notified of rotation changes. */
     fun interface RotationListener {
         /** Called once rotation changes. */
-        fun onRotationChanged(@Rotation newRotation: Int)
+        fun onRotationChanged(newRotation: Int)
     }
 
-    private inner class RotationWatcher : IRotationWatcher.Stub() {
-        override fun onRotationChanged(rotation: Int) {
-            mainExecutor.execute { listeners.forEach { it.onRotationChanged(rotation) } }
+    private inner class RotationDisplayListener : DisplayManager.DisplayListener {
+
+        override fun onDisplayChanged(displayId: Int) {
+            val display = context.display ?: return
+
+            if (displayId == display.displayId) {
+                val currentRotation = display.rotation
+                if (lastRotation == null || lastRotation != currentRotation) {
+                    listeners.forEach { it.onRotationChanged(currentRotation) }
+                    lastRotation = currentRotation
+                }
+            }
         }
+
+        override fun onDisplayAdded(displayId: Int) {}
+
+        override fun onDisplayRemoved(displayId: Int) {}
     }
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index 06ca153..ce5c5f9 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -79,10 +79,9 @@
     companion object {
         fun ContentResolver.areAnimationsEnabled(): Boolean {
             val animationScale =
-                Settings.Global.getStringForUser(
+                Settings.Global.getString(
                         this,
                         Settings.Global.ANIMATOR_DURATION_SCALE,
-                        this.userId
                     )
                     ?.toFloatOrNull()
                     ?: 1f
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 864fe0f..7df0d86 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -99,6 +99,9 @@
 
     private int createSensorInternal(IBinder sensorToken, VirtualSensorConfig config)
             throws SensorCreationException {
+        if (config.getType() <= 0) {
+            throw new SensorCreationException("Received an invalid virtual sensor type.");
+        }
         final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId,
                 config.getType(), config.getName(),
                 config.getVendor() == null ? "" : config.getVendor(), config.getFlags(),
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index e3f00ded..9e95e5f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -995,7 +995,7 @@
     private static final String KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION =
             "enable_wait_for_finish_attach_application";
 
-    private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = true;
+    private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = false;
 
     /** @see #KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION */
     public volatile boolean mEnableWaitForFinishAttachApplication =
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1960c33..4ba6854 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -101,7 +101,7 @@
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
-import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH;
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
@@ -5588,8 +5588,11 @@
                         boolean isChangeEnabled = CompatChanges.isChangeEnabled(
                                         PendingIntent.BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT,
                                         owningUid);
-                        logUnsafeMutableImplicitPi(packageName, resolvedTypes, owningUid, i, intent,
-                                isChangeEnabled);
+                        String resolvedType = resolvedTypes == null
+                                || i >= resolvedTypes.length ? null : resolvedTypes[i];
+                        ActivityManagerUtils.logUnsafeIntentEvent(
+                                UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED,
+                                owningUid, intent, resolvedType, isChangeEnabled);
                         if (isChangeEnabled) {
                             String msg = packageName + ": Targeting U+ (version "
                                     + Build.VERSION_CODES.UPSIDE_DOWN_CAKE + " and above) disallows"
@@ -5655,24 +5658,6 @@
         }
     }
 
-    private void logUnsafeMutableImplicitPi(String packageName, String[] resolvedTypes,
-            int owningUid, int i, Intent intent, boolean isChangeEnabled) {
-        String[] categories = intent.getCategories() == null ? new String[0]
-                : intent.getCategories().toArray(String[]::new);
-        String resolvedType = resolvedTypes == null || i >= resolvedTypes.length ? null
-                : resolvedTypes[i];
-        FrameworkStatsLog.write(UNSAFE_INTENT_EVENT_REPORTED,
-                UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED,
-                owningUid,
-                null,
-                packageName,
-                intent.getAction(),
-                categories,
-                resolvedType,
-                intent.getScheme(),
-                isChangeEnabled);
-    }
-
     @Override
     public int sendIntentSender(IApplicationThread caller, IIntentSender target,
             IBinder allowlistToken, int code, Intent intent, String resolvedType,
@@ -12912,18 +12897,9 @@
             boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid(
                     ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS,
                     callingUid);
-            String[] categories = intent.getCategories() == null ? new String[0]
-                    : intent.getCategories().toArray(String[]::new);
-            FrameworkStatsLog.write(UNSAFE_INTENT_EVENT_REPORTED,
-                    FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH,
-                    callingUid,
-                    componentInfo,
-                    callerPackage,
-                    intent.getAction(),
-                    categories,
-                    resolvedType,
-                    intent.getScheme(),
-                    hasToBeExportedToMatch);
+            ActivityManagerUtils.logUnsafeIntentEvent(
+                    UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH,
+                    callingUid, intent, resolvedType, hasToBeExportedToMatch);
             if (!hasToBeExportedToMatch) {
                 return;
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java
index 9be553c..01466b8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerUtils.java
+++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java
@@ -17,11 +17,13 @@
 
 import android.app.ActivityThread;
 import android.content.ContentResolver;
+import android.content.Intent;
 import android.provider.Settings;
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -133,4 +135,25 @@
     public static int hashComponentNameForAtom(String shortInstanceName) {
         return getUnsignedHashUnCached(shortInstanceName) ^ getAndroidIdHash();
     }
+
+    /**
+     * Helper method to log an unsafe intent event.
+     */
+    public static void logUnsafeIntentEvent(int event, int callingUid,
+            Intent intent, String resolvedType, boolean blocked) {
+        String[] categories = intent.getCategories() == null ? new String[0]
+                : intent.getCategories().toArray(String[]::new);
+        String component = intent.getComponent() == null ? null
+                : intent.getComponent().flattenToString();
+        FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED,
+                event,
+                callingUid,
+                component,
+                intent.getPackage(),
+                intent.getAction(),
+                categories,
+                resolvedType,
+                intent.getScheme(),
+                blocked);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index 42be95b..ecb7e7c 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -20,8 +20,13 @@
 import android.annotation.Nullable;
 import android.content.Intent;
 import android.hardware.biometrics.IBiometricContextListener;
+import android.hardware.biometrics.common.AuthenticateReason;
 import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.common.OperationReason;
+import android.hardware.biometrics.common.WakeReason;
+import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.os.PowerManager;
 import android.view.Surface;
 
 /**
@@ -50,12 +55,127 @@
         mAidlContext = context;
     }
 
-    /** Gets the subset of the context that can be shared with the HAL. */
+    /**
+     * Gets the subset of the context that can be shared with the HAL.
+     *
+     * When starting a new operation use methods like to update & fetch the context:
+     * <ul>
+     *     <li>{@link #toAidlContext(FaceAuthenticateOptions)}
+     *     <li>{@link #toAidlContext(FingerprintAuthenticateOptions)}
+     * </ul>
+     *
+     * Use this method for any subsequent calls to the HAL or for operations that do
+     * not accept any options.
+     *
+     * @return the underlying AIDL context
+     */
     @NonNull
     public OperationContext toAidlContext() {
         return mAidlContext;
     }
 
+    /**
+     * Gets the subset of the context that can be shared with the HAL and updates
+     * it with the given options.
+     *
+     * @param options authenticate options
+     * @return the underlying AIDL context
+     */
+    @NonNull
+    public OperationContext toAidlContext(@NonNull FaceAuthenticateOptions options) {
+        mAidlContext.authenticateReason = AuthenticateReason
+                .faceAuthenticateReason(getAuthReason(options));
+        mAidlContext.wakeReason = getWakeReason(options);
+
+        return mAidlContext;
+    }
+
+    /**
+     * Gets the subset of the context that can be shared with the HAL and updates
+     * it with the given options.
+     *
+     * @param options authenticate options
+     * @return the underlying AIDL context
+     */
+    @NonNull
+    public OperationContext toAidlContext(@NonNull FingerprintAuthenticateOptions options) {
+        mAidlContext.authenticateReason = AuthenticateReason
+                .fingerprintAuthenticateReason(getAuthReason(options));
+        mAidlContext.wakeReason = getWakeReason(options);
+
+        return mAidlContext;
+    }
+
+    @AuthenticateReason.Face
+    private int getAuthReason(@NonNull FaceAuthenticateOptions options) {
+        switch (options.getAuthenticateReason()) {
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP:
+                return AuthenticateReason.Face.STARTED_WAKING_UP;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN:
+                return AuthenticateReason.Face.PRIMARY_BOUNCER_SHOWN;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_ASSISTANT_VISIBLE:
+                return AuthenticateReason.Face.ASSISTANT_VISIBLE;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN:
+                return AuthenticateReason.Face.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED:
+                return AuthenticateReason.Face.NOTIFICATION_PANEL_CLICKED;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED:
+                return AuthenticateReason.Face.OCCLUDING_APP_REQUESTED;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED:
+                return AuthenticateReason.Face.PICK_UP_GESTURE_TRIGGERED;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_QS_EXPANDED:
+                return AuthenticateReason.Face.QS_EXPANDED;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER:
+                return AuthenticateReason.Face.SWIPE_UP_ON_BOUNCER;
+            case FaceAuthenticateOptions.AUTHENTICATE_REASON_UDFPS_POINTER_DOWN:
+                return AuthenticateReason.Face.UDFPS_POINTER_DOWN;
+            default:
+                return AuthenticateReason.Face.UNKNOWN;
+        }
+    }
+
+    @WakeReason
+    private int getWakeReason(@NonNull FaceAuthenticateOptions options) {
+        switch (options.getWakeReason()) {
+            case PowerManager.WAKE_REASON_POWER_BUTTON:
+                return WakeReason.POWER_BUTTON;
+            case PowerManager.WAKE_REASON_GESTURE:
+                return WakeReason.GESTURE;
+            case PowerManager.WAKE_REASON_WAKE_KEY:
+                return WakeReason.WAKE_KEY;
+            case PowerManager.WAKE_REASON_WAKE_MOTION:
+                return WakeReason.WAKE_MOTION;
+            case PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED:
+                return WakeReason.DISPLAY_GROUP_ADDED;
+            case PowerManager.WAKE_REASON_TAP:
+                return WakeReason.TAP;
+            case PowerManager.WAKE_REASON_LIFT:
+                return WakeReason.LIFT;
+            case PowerManager.WAKE_REASON_BIOMETRIC:
+                return WakeReason.BIOMETRIC;
+            case PowerManager.WAKE_REASON_CAMERA_LAUNCH:
+            case PowerManager.WAKE_REASON_HDMI:
+            case PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON:
+            case PowerManager.WAKE_REASON_UNFOLD_DEVICE:
+            case PowerManager.WAKE_REASON_DREAM_FINISHED:
+            case PowerManager.WAKE_REASON_TILT:
+            case PowerManager.WAKE_REASON_APPLICATION:
+            case PowerManager.WAKE_REASON_PLUGGED_IN:
+            default:
+                return WakeReason.UNKNOWN;
+        }
+    }
+
+    @AuthenticateReason.Fingerprint
+    private int getAuthReason(@NonNull FingerprintAuthenticateOptions options) {
+        return AuthenticateReason.Fingerprint.UNKNOWN;
+    }
+
+    @WakeReason
+    private int getWakeReason(@NonNull FingerprintAuthenticateOptions options) {
+        return WakeReason.UNKNOWN;
+    }
+
     /** {@link OperationContext#id}. */
     public int getId() {
         return mAidlContext.id;
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 005ad20..7b9fc36 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -74,6 +74,7 @@
     @Nullable
     private final TaskStackListener mTaskStackListener;
     private final LockoutTracker mLockoutTracker;
+    private final O mOptions;
     private final boolean mIsRestricted;
     private final boolean mAllowBackgroundAuthentication;
     // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update
@@ -110,6 +111,7 @@
         mAllowBackgroundAuthentication = allowBackgroundAuthentication;
         mShouldUseLockoutTracker = lockoutTracker != null;
         mSensorStrength = sensorStrength;
+        mOptions = options;
     }
 
     @LockoutTracker.LockoutMode
@@ -151,6 +153,11 @@
         return Utils.isSettings(getContext(), getOwnerString());
     }
 
+    /** The options requested at the start of the operation. */
+    protected O getOptions() {
+        return mOptions;
+    }
+
     @Override
     protected boolean isCryptoOperation() {
         return mOperationId != 0;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 976f1cb..84e2fb4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -166,7 +166,7 @@
 
         if (session.hasContextMethods()) {
             return session.getSession().authenticateWithContext(
-                    mOperationId, getOperationContext().toAidlContext());
+                    mOperationId, getOperationContext().toAidlContext(getOptions()));
         } else {
             return session.getSession().authenticate(mOperationId);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index e65202d..fa23ccd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -47,6 +47,7 @@
     private static final String TAG = "FaceDetectClient";
 
     private final boolean mIsStrongBiometric;
+    private final FaceAuthenticateOptions mOptions;
     @Nullable private ICancellationSignal mCancellationSignal;
     @Nullable private SensorPrivacyManager mSensorPrivacyManager;
 
@@ -74,6 +75,7 @@
         setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
         mSensorPrivacyManager = sensorPrivacyManager;
+        mOptions = options;
     }
 
     @Override
@@ -118,7 +120,7 @@
 
         if (session.hasContextMethods()) {
             return session.getSession().detectInteractionWithContext(
-                    getOperationContext().toAidlContext());
+                    getOperationContext().toAidlContext(mOptions));
         } else {
             return session.getSession().detectInteraction();
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 0f81f9f..435e81d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -285,7 +285,7 @@
 
         if (session.hasContextMethods()) {
             return session.getSession().authenticateWithContext(
-                    mOperationId, opContext.toAidlContext());
+                    mOperationId, opContext.toAidlContext(getOptions()));
         } else {
             return session.getSession().authenticate(mOperationId);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 376d231..16d16fc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -48,6 +48,7 @@
     private static final String TAG = "FingerprintDetectClient";
 
     private final boolean mIsStrongBiometric;
+    private final FingerprintAuthenticateOptions mOptions;
     @NonNull private final SensorOverlays mSensorOverlays;
     @Nullable private ICancellationSignal mCancellationSignal;
 
@@ -66,6 +67,7 @@
         mIsStrongBiometric = isStrongBiometric;
         mSensorOverlays = new SensorOverlays(udfpsOverlayController,
                 null /* sideFpsController*/, udfpsOverlay);
+        mOptions = options;
     }
 
     @Override
@@ -105,7 +107,7 @@
 
         if (session.hasContextMethods()) {
             return session.getSession().detectInteractionWithContext(
-                    getOperationContext().toAidlContext());
+                    getOperationContext().toAidlContext(mOptions));
         } else {
             return session.getSession().detectInteraction();
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 4f28432..cc41207 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -3101,6 +3101,16 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, printWriter)) return;
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            dumpInternal(printWriter);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void dumpInternal(PrintWriter printWriter) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
 
         pw.println("Current lock settings service state:");
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9329f06..0d417e4 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -136,9 +136,7 @@
     }
 
     /**
-     * @param isolated indicates if this object should <em>not</em> connect to
-     *            the real {@code installd}. All remote calls will be ignored
-     *            unless you extend this class and intercept them.
+     * @param isolated Make the installer isolated. See {@link isIsolated}.
      */
     public Installer(Context context, boolean isolated) {
         super(context);
@@ -153,6 +151,15 @@
         mWarnIfHeld = warnIfHeld;
     }
 
+    /**
+     * Returns true if the installer is isolated, i.e. if this object should <em>not</em> connect to
+     * the real {@code installd}. All remote calls will be ignored unless you extend this class and
+     * intercept them.
+     */
+    public boolean isIsolated() {
+        return mIsolated;
+    }
+
     @Override
     public void onStart() {
         if (mIsolated) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 767c0a7..6a2ddc8 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import static com.android.server.pm.DexOptHelper.useArtService;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -301,6 +302,15 @@
                     throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
+                if (useArtService()) {
+                    if ((dexFlags & DEXOPT_SECONDARY_DEX) != 0) {
+                        // installd may change the reference profile in place for secondary dex
+                        // files, which isn't safe with the lock free approach in ART Service.
+                        throw new IllegalArgumentException(
+                                "Invalid OTA dexopt call for secondary dex");
+                    }
+                }
+
                 // The current version. For v10, see b/115993344.
                 builder.append("10 ");
 
@@ -353,7 +363,6 @@
         PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
                 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
 
-        // TODO(b/251903639): Allow this use of legacy dexopt code even when ART Service is enabled.
         try {
             optimizer.performDexOpt(pkg, pkgSetting, null /* ISAs */,
                     null /* CompilerStats.PackageStats */,
@@ -362,9 +371,19 @@
                     new DexoptOptions(pkg.getPackageName(), compilationReason,
                             DexoptOptions.DEXOPT_BOOT_COMPLETE));
         } catch (LegacyDexoptDisabledException e) {
-            throw new RuntimeException(e);
+            // OTA is still allowed to use the legacy dexopt code even when ART Service is enabled.
+            // The installer is isolated and won't call into installd, and the dexopt() method is
+            // overridden to only collect the command above. Hence we shouldn't go into any code
+            // path where this exception is thrown.
+            Slog.wtf(TAG, e);
         }
 
+        // ART Service compat note: These commands are consumed by the otapreopt binary, which uses
+        // the same legacy dexopt code as installd to invoke dex2oat. It provides output path
+        // implementations (see calculate_odex_file_path and create_cache_path in
+        // frameworks/native/cmds/installd/otapreopt.cpp) to write to different odex files than
+        // those used by ART Service in its ordinary operations, so it doesn't interfere with ART
+        // Service even when dalvik.vm.useartservice is true.
         return commands;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0a90e7a3..8a4080f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
 
+import static com.android.server.pm.DexOptHelper.useArtService;
 import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
 import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
@@ -329,8 +330,22 @@
 
             String profileName = ArtManager.getProfileName(
                     i == 0 ? null : pkg.getSplitNames()[i - 1]);
-            final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
-                    || packageUseInfo.isUsedByOtherApps(path);
+
+            final boolean isUsedByOtherApps;
+            if (options.isDexoptAsSharedLibrary()) {
+                isUsedByOtherApps = true;
+            } else if (useArtService()) {
+                // We get here when collecting dexopt commands in OTA preopt, even when ART Service
+                // is in use. packageUseInfo isn't useful in that case since the legacy dex use
+                // database hasn't been updated. So we'd have to query ART Service instead, but it
+                // doesn't provide that API. Just cop-out and bypass the cloud profile handling.
+                // That means such apps will get preopted wrong, and we'll leave it to a later
+                // background dexopt after reboot instead.
+                isUsedByOtherApps = false;
+            } else {
+                isUsedByOtherApps = packageUseInfo.isUsedByOtherApps(path);
+            }
+
             String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter());
             // If the app is used by other apps, we must not use the existing profile because it
             // may contain user data, unless the profile is newly created on install.
@@ -446,6 +461,14 @@
     private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
             @Nullable String dexMetadataPath) throws LegacyDexoptDisabledException {
         if (dexMetadataPath != null) {
+            if (mInstaller.isIsolated()) {
+                // If the installer is isolated, the two calls to it below will return immediately,
+                // so this only short-circuits that a bit. We need to do it to avoid the
+                // LegacyDexoptDisabledException getting thrown first, when we get here during OTA
+                // preopt and ART Service is enabled.
+                return true;
+            }
+
             try {
                 // Make sure we don't keep any existing contents.
                 mInstaller.deleteReferenceProfile(pkg.getPackageName(), profileName);
@@ -879,7 +902,12 @@
     private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
             String classLoaderContext, int profileAnalysisResult, boolean downgrade,
             int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException {
-        Installer.checkLegacyDexoptDisabled();
+        // Allow calls from OtaDexoptService even when ART Service is in use. The installer is
+        // isolated in that case so later calls to it won't call into installd anyway.
+        if (!mInstaller.isIsolated()) {
+            Installer.checkLegacyDexoptDisabled();
+        }
+
         final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
         final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0;
         boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
@@ -948,6 +976,8 @@
      */
     private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
             String compilerFilter) throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
+
         // Check if we are allowed to merge and if the compiler filter is profile guided.
         if (!isProfileGuidedCompilerFilter(compilerFilter)) {
             return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 928ffa7..3f9a0bc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -24,6 +24,7 @@
 import static android.system.OsConstants.O_RDWR;
 
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH;
 import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
 import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
@@ -94,6 +95,7 @@
 import com.android.server.IntentResolver;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerUtils;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -1186,12 +1188,6 @@
                 continue;
             }
 
-            // Only enforce filter matching if target app's target SDK >= T
-            if (!compat.isChangeEnabledInternal(
-                    ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) {
-                continue;
-            }
-
             final ParsedMainComponent comp;
             if (info instanceof ActivityInfo) {
                 if (isReceiver) {
@@ -1210,6 +1206,10 @@
                 continue;
             }
 
+            // Only enforce filter matching if target app's target SDK >= T
+            final boolean enforce = compat.isChangeEnabledInternal(
+                    ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo);
+
             boolean match = false;
             for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
                 IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
@@ -1219,14 +1219,19 @@
                 }
             }
             if (!match) {
-                Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
-                Slog.w(TAG, "Access blocked: " + comp.getComponentName());
-                if (DEBUG_INTENT_MATCHING) {
-                    Slog.v(TAG, "Component intent filters:");
-                    comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, "  "));
-                    Slog.v(TAG, "-----------------------------");
+                ActivityManagerUtils.logUnsafeIntentEvent(
+                        UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH,
+                        filterCallingUid, intent, resolvedType, enforce);
+                if (enforce) {
+                    Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
+                    Slog.w(TAG, "Access blocked: " + comp.getComponentName());
+                    if (DEBUG_INTENT_MATCHING) {
+                        Slog.v(TAG, "Component intent filters:");
+                        comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, "  "));
+                        Slog.v(TAG, "-----------------------------");
+                    }
+                    resolveInfos.remove(i);
                 }
-                resolveInfos.remove(i);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index a13c568..7ed10a4 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
+import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
 import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
 import static com.android.server.pm.PackageManagerService.TAG;
@@ -55,9 +56,9 @@
 
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.am.ActivityManagerUtils;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -130,18 +131,9 @@
                 boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid(
                         ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS,
                         filterCallingUid);
-                String[] categories = intent.getCategories() == null ? new String[0]
-                        : intent.getCategories().toArray(String[]::new);
-                FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED,
-                        FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH,
-                        filterCallingUid,
-                        query.get(i).getComponentInfo().getComponentName().flattenToShortString(),
-                        callerPackage,
-                        intent.getAction(),
-                        categories,
-                        resolvedType,
-                        intent.getScheme(),
-                        hasToBeExportedToMatch);
+                ActivityManagerUtils.logUnsafeIntentEvent(
+                        UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH,
+                        filterCallingUid, intent, resolvedType, hasToBeExportedToMatch);
                 if (callback != null) {
                     handler.post(() -> {
                         try {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index f11c864..bc23020 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -12688,8 +12688,8 @@
             energy = info.getControllerEnergyUsed();
             if (!info.getUidTraffic().isEmpty()) {
                 for (UidTraffic traffic : info.getUidTraffic()) {
-                    uidRxBytes.incrementValue(traffic.getUid(), traffic.getRxBytes());
-                    uidTxBytes.incrementValue(traffic.getUid(), traffic.getTxBytes());
+                    uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
+                    uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
                 }
             }
         }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 97b7811..370d21f 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -137,7 +137,8 @@
                 remoteCredentialService);
         mCompleteRequest = completeCreateRequest;
         setStatus(Status.PENDING);
-        mProviderResponseDataHandler = new ProviderResponseDataHandler(hybridService);
+        mProviderResponseDataHandler = new ProviderResponseDataHandler(
+                ComponentName.unflattenFromString(hybridService));
     }
 
     @Override
@@ -297,21 +298,23 @@
     }
 
     private class ProviderResponseDataHandler {
-        private final ComponentName mExpectedRemoteEntryProviderService;
+        @Nullable private final ComponentName mExpectedRemoteEntryProviderService;
 
         @NonNull
         private final Map<String, Pair<CreateEntry, Entry>> mUiCreateEntries = new HashMap<>();
 
         @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null;
 
-        ProviderResponseDataHandler(String hybridService) {
-            mExpectedRemoteEntryProviderService = ComponentName.unflattenFromString(hybridService);
+        ProviderResponseDataHandler(@Nullable ComponentName expectedRemoteEntryProviderService) {
+            mExpectedRemoteEntryProviderService = expectedRemoteEntryProviderService;
         }
 
         public void addResponseContent(List<CreateEntry> createEntries,
                 RemoteEntry remoteEntry) {
             createEntries.forEach(this::addCreateEntry);
-            setRemoteEntry(remoteEntry);
+            if (remoteEntry != null) {
+                setRemoteEntry(remoteEntry);
+            }
         }
         public void addCreateEntry(CreateEntry createEntry) {
             String id = generateUniqueId();
@@ -321,13 +324,13 @@
         }
 
         public void setRemoteEntry(@Nullable RemoteEntry remoteEntry) {
-            if (remoteEntry == null) {
-                mUiRemoteEntry = null;
+            if (!enforceRemoteEntryRestrictions(mExpectedRemoteEntryProviderService)) {
+                Log.i(TAG, "Remote entry being dropped as it does not meet the restriction"
+                        + "checks.");
                 return;
             }
-            if (!mComponentName.equals(mExpectedRemoteEntryProviderService)) {
-                Log.i(TAG, "Remote entry being dropped as it is not from the service "
-                        + "configured by the OEM.");
+            if (remoteEntry == null) {
+                mUiRemoteEntry = null;
                 return;
             }
             String id = generateUniqueId();
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index ee813e9..80a61b9 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -181,7 +181,6 @@
     /** Called when the provider response has been updated by an external source. */
     @Override // Callback from the remote provider
     public void onProviderResponseSuccess(@Nullable BeginGetCredentialResponse response) {
-        Log.i(TAG, "in onProviderResponseSuccess");
         onSetInitialRemoteResponse(response);
     }
 
@@ -393,7 +392,7 @@
                 .extractResponseContent(providerPendingIntentResponse
                         .getResultData());
         if (response != null && !mProviderResponseDataHandler.isEmptyResponse(response)) {
-            addToInitialRemoteResponse(response);
+            addToInitialRemoteResponse(response, /*isInitialResponse=*/ false);
             // Additional content received is in the form of new response content.
             return true;
         }
@@ -401,7 +400,8 @@
         return false;
     }
 
-    private void addToInitialRemoteResponse(BeginGetCredentialResponse content) {
+    private void addToInitialRemoteResponse(BeginGetCredentialResponse content,
+            boolean isInitialResponse) {
         if (content == null) {
             return;
         }
@@ -409,7 +409,8 @@
                 content.getCredentialEntries(),
                 content.getActions(),
                 content.getAuthenticationActions(),
-                content.getRemoteCredentialEntry()
+                content.getRemoteCredentialEntry(),
+                isInitialResponse
         );
     }
 
@@ -424,7 +425,7 @@
     /** Updates the response being maintained in state by this provider session. */
     private void onSetInitialRemoteResponse(BeginGetCredentialResponse response) {
         mProviderResponse = response;
-        addToInitialRemoteResponse(response);
+        addToInitialRemoteResponse(response, /*isInitialResponse=*/true);
         if (mProviderResponseDataHandler.isEmptyResponse(response)) {
             updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE);
             return;
@@ -463,7 +464,7 @@
     }
 
     private class ProviderResponseDataHandler {
-        private final ComponentName mExpectedRemoteEntryProviderService;
+        @Nullable private final ComponentName mExpectedRemoteEntryProviderService;
         @NonNull
         private final Map<String, Pair<CredentialEntry, Entry>> mUiCredentialEntries =
                 new HashMap<>();
@@ -475,19 +476,27 @@
 
         @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null;
 
-        ProviderResponseDataHandler(ComponentName expectedRemoteEntryProviderService) {
+        ProviderResponseDataHandler(@Nullable ComponentName expectedRemoteEntryProviderService) {
             mExpectedRemoteEntryProviderService = expectedRemoteEntryProviderService;
         }
 
         public void addResponseContent(List<CredentialEntry> credentialEntries,
                 List<Action> actions, List<Action> authenticationActions,
-                RemoteEntry remoteEntry) {
+                RemoteEntry remoteEntry, boolean isInitialResponse) {
             credentialEntries.forEach(this::addCredentialEntry);
             actions.forEach(this::addAction);
             authenticationActions.forEach(
                     authenticationAction -> addAuthenticationAction(authenticationAction,
                             AuthenticationEntry.STATUS_LOCKED));
-            setRemoteEntry(remoteEntry);
+            // In the query phase, it is likely most providers will return a null remote entry
+            // so no need to invoke the setter since it adds the overhead of checking for the
+            // hybrid permission, and then sets an already null value to null.
+            // If this is not the query phase, e.g. response after a locked entry is unlocked
+            // then it is valid for the provider to remove the remote entry, and so we allow
+            // them to set it to null.
+            if (remoteEntry != null || !isInitialResponse) {
+                setRemoteEntry(remoteEntry);
+            }
         }
         public void addCredentialEntry(CredentialEntry credentialEntry) {
             String id = generateUniqueId();
@@ -524,12 +533,13 @@
         }
 
         public void setRemoteEntry(@Nullable RemoteEntry remoteEntry) {
-            if (remoteEntry == null) {
+            if (!enforceRemoteEntryRestrictions(mExpectedRemoteEntryProviderService)) {
+                Log.i(TAG, "Remote entry being dropped as it does not meet the restriction"
+                        + " checks.");
                 return;
             }
-            if (!mComponentName.equals(mExpectedRemoteEntryProviderService)) {
-                Log.i(TAG, "Remote entry being dropped as it is not from the service "
-                        + "configured by the OEM.");
+            if (remoteEntry == null) {
+                mUiRemoteEntry = null;
                 return;
             }
             String id = generateUniqueId();
@@ -538,6 +548,8 @@
             mUiRemoteEntry = new Pair<>(generateUniqueId(), new Pair<>(remoteEntry, entry));
         }
 
+
+
         public GetCredentialProviderData toGetCredentialProviderData() {
             return new GetCredentialProviderData.Builder(
                     mComponentName.flattenToString()).setActionChips(prepareActionEntries())
@@ -571,7 +583,6 @@
             return credEntries;
         }
 
-
         private Entry prepareRemoteEntry() {
             if (mUiRemoteEntry == null || mUiRemoteEntry.first == null
                     || mUiRemoteEntry.second == null) {
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index ecddcf3..53ed070 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -19,10 +19,13 @@
 import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_FAILURE;
 import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_SUCCESS;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.credentials.Credential;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.ProviderPendingIntentResponse;
@@ -228,6 +231,39 @@
         return mProviderResponse;
     }
 
+    protected boolean enforceRemoteEntryRestrictions(
+            @Nullable ComponentName expectedRemoteEntryProviderService) {
+        // Check if the service is the one set by the OEM. If not silently reject this entry
+        if (!mComponentName.equals(expectedRemoteEntryProviderService)) {
+            Log.i(TAG, "Remote entry being dropped as it is not from the service "
+                    + "configured by the OEM.");
+            return false;
+        }
+        // Check if the service has the hybrid permission .If not, silently reject this entry.
+        // This check is in addition to the permission check happening in the provider's process.
+        try {
+            ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
+                    mComponentName.getPackageName(),
+                    PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
+            if (appInfo != null
+                    && mContext.checkPermission(
+                    Manifest.permission.PROVIDE_REMOTE_CREDENTIALS,
+                    /*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+        } catch (SecurityException e) {
+            Log.i(TAG, "Error getting info for "
+                    + mComponentName.flattenToString() + ": " + e.getMessage());
+            return false;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.i(TAG, "Error getting info for "
+                    + mComponentName.flattenToString() + ": " + e.getMessage());
+            return false;
+        }
+        Log.i(TAG, "In enforceRemoteEntryRestrictions - remote entry checks fail");
+        return false;
+    }
+
     /** Should be overridden to prepare, and stores state for {@link ProviderData} to be
      * shown on the UI. */
     @Nullable protected abstract ProviderData prepareUiData();
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index 702261e..ff4e3b6 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -121,8 +121,6 @@
             ProviderCallbacks<BeginGetCredentialResponse> callback) {
         Log.i(TAG, "In onGetCredentials in RemoteCredentialService");
         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
-        AtomicReference<CompletableFuture<BeginGetCredentialResponse>> futureRef =
-                new AtomicReference<>();
 
         CompletableFuture<BeginGetCredentialResponse> connectThenExecute = postAsync(service -> {
             CompletableFuture<BeginGetCredentialResponse> getCredentials =
@@ -134,7 +132,6 @@
                                 new IBeginGetCredentialCallback.Stub() {
                                     @Override
                                     public void onSuccess(BeginGetCredentialResponse response) {
-                                        Log.i(TAG, "In onSuccess in RemoteCredentialService");
                                         getCredentials.complete(response);
                                     }
 
@@ -147,22 +144,15 @@
                                                 new GetCredentialException(errorType, errorMsg));
                                     }
                                 });
-                CompletableFuture<BeginGetCredentialResponse> future = futureRef.get();
-                if (future != null && future.isCancelled()) {
-                    dispatchCancellationSignal(cancellationSignal);
-                } else {
-                    cancellationSink.set(cancellationSignal);
-                }
+                cancellationSink.set(cancellationSignal);
                 return getCredentials;
             } finally {
                 Binder.restoreCallingIdentity(originalCallingUidToken);
             }
         }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
 
-        futureRef.set(connectThenExecute);
         connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
                 handleExecutionResponse(result, error, cancellationSink, callback)));
-
         return cancellationSink.get();
     }
 
@@ -178,8 +168,6 @@
             ProviderCallbacks<BeginCreateCredentialResponse> callback) {
         Log.i(TAG, "In onCreateCredential in RemoteCredentialService");
         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
-        AtomicReference<CompletableFuture<BeginCreateCredentialResponse>> futureRef =
-                new AtomicReference<>();
 
         CompletableFuture<BeginCreateCredentialResponse> connectThenExecute =
                 postAsync(service -> {
@@ -205,19 +193,13 @@
                                                 new CreateCredentialException(errorType, errorMsg));
                                     }
                                 });
-                        CompletableFuture<BeginCreateCredentialResponse> future = futureRef.get();
-                        if (future != null && future.isCancelled()) {
-                            dispatchCancellationSignal(cancellationSignal);
-                        } else {
-                            cancellationSink.set(cancellationSignal);
-                        }
+                        cancellationSink.set(cancellationSignal);
                         return createCredentialFuture;
                     } finally {
                         Binder.restoreCallingIdentity(originalCallingUidToken);
                     }
                 }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
 
-        futureRef.set(connectThenExecute);
         connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
                 handleExecutionResponse(result, error, cancellationSink, callback)));
 
@@ -236,7 +218,6 @@
             ProviderCallbacks<Void> callback) {
         Log.i(TAG, "In onClearCredentialState in RemoteCredentialService");
         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
-        AtomicReference<CompletableFuture<Void>> futureRef = new AtomicReference<>();
 
         CompletableFuture<Void> connectThenExecute =
                 postAsync(service -> {
@@ -263,19 +244,13 @@
                                                         errorMsg));
                                     }
                                 });
-                        CompletableFuture<Void> future = futureRef.get();
-                        if (future != null && future.isCancelled()) {
-                            dispatchCancellationSignal(cancellationSignal);
-                        } else {
-                            cancellationSink.set(cancellationSignal);
-                        }
+                        cancellationSink.set(cancellationSignal);
                         return clearCredentialFuture;
                     } finally {
                         Binder.restoreCallingIdentity(originalCallingUidToken);
                     }
                 }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
 
-        futureRef.set(connectThenExecute);
         connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
                 handleExecutionResponse(result, error, cancellationSink, callback)));
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 3ff802c..6b0e330 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.face.aidl;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -30,11 +32,15 @@
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.content.ComponentName;
+import android.hardware.biometrics.common.AuthenticateReason;
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.WakeReason;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.Face;
 import android.hardware.face.FaceAuthenticateOptions;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.testing.TestableContext;
@@ -69,6 +75,8 @@
 
     private static final int USER_ID = 12;
     private static final long OP_ID = 32;
+    private static final int WAKE_REASON = WakeReason.LIFT;
+    private static final int AUTH_REASON = AuthenticateReason.Face.ASSISTANT_VISIBLE;
 
     @Rule
     public final TestableContext mContext = new TestableContext(
@@ -126,8 +134,13 @@
         InOrder order = inOrder(mHal, mBiometricContext);
         order.verify(mBiometricContext).updateContext(
                 mOperationContextCaptor.capture(), anyBoolean());
-        order.verify(mHal).authenticateWithContext(
-                eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext()));
+
+        final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
+        order.verify(mHal).authenticateWithContext(eq(OP_ID), same(aidlContext));
+        assertThat(aidlContext.wakeReason).isEqualTo(WAKE_REASON);
+        assertThat(aidlContext.authenticateReason.getFaceAuthenticateReason())
+                .isEqualTo(AUTH_REASON);
+
         verify(mHal, never()).authenticate(anyLong());
     }
 
@@ -156,8 +169,11 @@
         final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
         final FaceAuthenticateOptions options = new FaceAuthenticateOptions.Builder()
                 .setOpPackageName("test-owner")
-                .setUserId(5)
+                .setUserId(USER_ID)
                 .setSensorId(9)
+                .setWakeReason(PowerManager.WAKE_REASON_LIFT)
+                .setAuthenticateReason(
+                        FaceAuthenticateOptions.AUTHENTICATE_REASON_ASSISTANT_VISIBLE)
                 .build();
         return new FaceAuthenticationClient(mContext, () -> aidl, mToken,
                 2 /* requestId */, mClientMonitorCallbackConverter, OP_ID,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index c4c5505..0abfa7e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.face.aidl;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.inOrder;
@@ -24,9 +26,13 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.hardware.biometrics.common.AuthenticateReason;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.WakeReason;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.FaceAuthenticateOptions;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.testing.TestableContext;
@@ -55,6 +61,8 @@
 public class FaceDetectClientTest {
 
     private static final int USER_ID = 12;
+    private static final int WAKE_REASON = WakeReason.POWER_BUTTON;
+    private static final int AUTH_REASON = AuthenticateReason.Face.OCCLUDING_APP_REQUESTED;
 
     @Rule
     public final TestableContext mContext = new TestableContext(
@@ -103,8 +111,13 @@
         InOrder order = inOrder(mHal, mBiometricContext);
         order.verify(mBiometricContext).updateContext(
                 mOperationContextCaptor.capture(), anyBoolean());
-        order.verify(mHal).detectInteractionWithContext(
-                same(mOperationContextCaptor.getValue().toAidlContext()));
+
+        final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
+        order.verify(mHal).detectInteractionWithContext(same(aidlContext));
+        assertThat(aidlContext.wakeReason).isEqualTo(WAKE_REASON);
+        assertThat(aidlContext.authenticateReason.getFaceAuthenticateReason())
+                .isEqualTo(AUTH_REASON);
+
         verify(mHal, never()).detectInteraction();
     }
 
@@ -118,6 +131,9 @@
                         .setUserId(USER_ID)
                         .setSensorId(5)
                         .setOpPackageName("own-it")
+                        .setWakeReason(PowerManager.WAKE_REASON_POWER_BUTTON)
+                        .setAuthenticateReason(
+                                FaceAuthenticateOptions.AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED)
                         .build(),
                 mBiometricLogger, mBiometricContext,
                 false /* isStrongBiometric */, null /* sensorPrivacyManager */);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index f0f975c..c664500 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -36,6 +36,7 @@
 import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.common.AuthenticateReason;
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.fingerprint.ISession;
@@ -165,8 +166,12 @@
         InOrder order = inOrder(mHal, mBiometricContext);
         order.verify(mBiometricContext).updateContext(
                 mOperationContextCaptor.capture(), anyBoolean());
-        order.verify(mHal).authenticateWithContext(
-                eq(OP_ID), same(mOperationContextCaptor.getValue().toAidlContext()));
+
+        final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
+        order.verify(mHal).authenticateWithContext(eq(OP_ID), same(aidlContext));
+        assertThat(aidlContext.authenticateReason.getFingerprintAuthenticateReason())
+                .isEqualTo(AuthenticateReason.Fingerprint.UNKNOWN);
+
         verify(mHal, never()).authenticate(anyLong());
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index e741e44..c20cc39 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.inOrder;
@@ -24,6 +26,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.hardware.biometrics.common.AuthenticateReason;
+import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -108,8 +112,12 @@
         InOrder order = inOrder(mHal, mBiometricContext);
         order.verify(mBiometricContext).updateContext(
                 mOperationContextCaptor.capture(), anyBoolean());
-        order.verify(mHal).detectInteractionWithContext(
-                same(mOperationContextCaptor.getValue().toAidlContext()));
+
+        final OperationContext aidlContext = mOperationContextCaptor.getValue().toAidlContext();
+        order.verify(mHal).detectInteractionWithContext(same(aidlContext));
+        assertThat(aidlContext.authenticateReason.getFingerprintAuthenticateReason())
+                .isEqualTo(AuthenticateReason.Fingerprint.UNKNOWN);
+
         verify(mHal, never()).detectInteraction();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index c6a7fbc..ee4b839 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -572,41 +572,14 @@
         mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, true, 9000, 9000);
         mBatteryStatsImpl.noteBluetoothScanResultsFromSourceLocked(ws, 42, 9000, 9000);
 
-
-
-        final Parcel uidTrafficParcel1 = Parcel.obtain();
-        final Parcel uidTrafficParcel2 = Parcel.obtain();
-
-        uidTrafficParcel1.writeInt(10042);
-        uidTrafficParcel1.writeLong(3000);
-        uidTrafficParcel1.writeLong(4000);
-        uidTrafficParcel1.setDataPosition(0);
-        uidTrafficParcel2.writeInt(10043);
-        uidTrafficParcel2.writeLong(5000);
-        uidTrafficParcel2.writeLong(8000);
-        uidTrafficParcel2.setDataPosition(0);
-
-        List<UidTraffic> uidTrafficList = ImmutableList.of(
-                UidTraffic.CREATOR.createFromParcel(uidTrafficParcel1),
-                UidTraffic.CREATOR.createFromParcel(uidTrafficParcel2));
-
-        final Parcel btActivityEnergyInfoParcel = Parcel.obtain();
-        btActivityEnergyInfoParcel.writeLong(1000);
-        btActivityEnergyInfoParcel.writeInt(
-                BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE);
-        btActivityEnergyInfoParcel.writeLong(9000);
-        btActivityEnergyInfoParcel.writeLong(8000);
-        btActivityEnergyInfoParcel.writeLong(12000);
-        btActivityEnergyInfoParcel.writeLong(0);
-        btActivityEnergyInfoParcel.writeTypedList(uidTrafficList);
-        btActivityEnergyInfoParcel.setDataPosition(0);
-
-        BluetoothActivityEnergyInfo info = BluetoothActivityEnergyInfo.CREATOR
-                .createFromParcel(btActivityEnergyInfoParcel);
-
-        uidTrafficParcel1.recycle();
-        uidTrafficParcel2.recycle();
-        btActivityEnergyInfoParcel.recycle();
+        BluetoothActivityEnergyInfo info = createBluetoothActivityEnergyInfo(
+                /* timestamp= */ 1000,
+                /* controllerTxTimeMs= */ 9000,
+                /* controllerRxTimeMs= */ 8000,
+                /* controllerIdleTimeMs= */ 12000,
+                /* controllerEnergyUsed= */ 0,
+                createUidTraffic(/* appUid= */ 10042, /* rxBytes= */ 3000, /* txBytes= */ 4000),
+                createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 5000, /* txBytes= */ 8000));
 
         mBatteryStatsImpl.updateBluetoothStateLocked(info, -1, 1000, 1000);
 
@@ -622,4 +595,105 @@
         assertThat(uidStats.rxTimeMs).isEqualTo(7375);  // Some scan time is treated as RX
         assertThat(uidStats.txTimeMs).isEqualTo(7666);  // Some scan time is treated as TX
     }
+
+    /** A regression test for b/266128651 */
+    @Test
+    public void testGetNetworkActivityBytes_multipleUpdates() {
+        when(mPowerProfile.getAveragePower(
+                PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE)).thenReturn(3.0);
+        mBatteryStatsImpl.setOnBatteryInternal(true);
+        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+        BluetoothActivityEnergyInfo info1 = createBluetoothActivityEnergyInfo(
+                /* timestamp= */ 10000,
+                /* controllerTxTimeMs= */ 9000,
+                /* controllerRxTimeMs= */ 8000,
+                /* controllerIdleTimeMs= */ 2000,
+                /* controllerEnergyUsed= */ 0,
+                createUidTraffic(/* appUid= */ 10042, /* rxBytes= */ 3000, /* txBytes= */ 4000),
+                createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 5000, /* txBytes= */ 8000));
+
+        mBatteryStatsImpl.updateBluetoothStateLocked(info1, -1, 1000, 1000);
+
+        long totalRx1 = mBatteryStatsImpl.getNetworkActivityBytes(
+                BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+        long totalTx1 = mBatteryStatsImpl.getNetworkActivityBytes(
+                BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+
+        assertThat(totalRx1).isEqualTo(8000);  // 3000 + 5000
+        assertThat(totalTx1).isEqualTo(12000);  // 4000 + 8000
+
+        BluetoothActivityEnergyInfo info2 = createBluetoothActivityEnergyInfo(
+                /* timestamp= */ 20000,
+                /* controllerTxTimeMs= */ 19000,
+                /* controllerRxTimeMs= */ 18000,
+                /* controllerIdleTimeMs= */ 3000,
+                /* controllerEnergyUsed= */ 0,
+                createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 6000, /* txBytes= */ 9500),
+                createUidTraffic(/* appUid= */ 10044, /* rxBytes= */ 7000, /* txBytes= */ 9000));
+
+        mBatteryStatsImpl.updateBluetoothStateLocked(info2, -1, 2000, 2000);
+
+        long totalRx2 = mBatteryStatsImpl.getNetworkActivityBytes(
+                BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+        long totalTx2 = mBatteryStatsImpl.getNetworkActivityBytes(
+                BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+
+        assertThat(totalRx2).isEqualTo(16000);  // 3000 + 6000 (updated) + 7000 (new)
+        assertThat(totalTx2).isEqualTo(22500);  // 4000 + 9500 (updated) + 9000 (new)
+
+        BluetoothActivityEnergyInfo info3 = createBluetoothActivityEnergyInfo(
+                /* timestamp= */ 30000,
+                /* controllerTxTimeMs= */ 20000,
+                /* controllerRxTimeMs= */ 20000,
+                /* controllerIdleTimeMs= */ 4000,
+                /* controllerEnergyUsed= */ 0,
+                createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 7000, /* txBytes= */ 9900),
+                createUidTraffic(/* appUid= */ 10044, /* rxBytes= */ 8000, /* txBytes= */ 10000));
+
+        mBatteryStatsImpl.updateBluetoothStateLocked(info3, -1, 2000, 2000);
+
+        long totalRx3 = mBatteryStatsImpl.getNetworkActivityBytes(
+                BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+        long totalTx3 = mBatteryStatsImpl.getNetworkActivityBytes(
+                BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+
+        assertThat(totalRx3).isEqualTo(18000);  // 3000 + 7000 (updated) + 8000 (updated)
+        assertThat(totalTx3).isEqualTo(23900);  // 4000 + 9900 (updated) + 10000 (updated)
+    }
+
+    private UidTraffic createUidTraffic(int appUid, long rxBytes, long txBytes) {
+        final Parcel parcel = Parcel.obtain();
+        parcel.writeInt(appUid); // mAppUid
+        parcel.writeLong(rxBytes); // mRxBytes
+        parcel.writeLong(txBytes); // mTxBytes
+        parcel.setDataPosition(0);
+        UidTraffic uidTraffic = UidTraffic.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return uidTraffic;
+    }
+
+    private BluetoothActivityEnergyInfo createBluetoothActivityEnergyInfo(
+            long timestamp,
+            long controllerTxTimeMs,
+            long controllerRxTimeMs,
+            long controllerIdleTimeMs,
+            long controllerEnergyUsed,
+            UidTraffic... uidTraffic) {
+        Parcel parcel = Parcel.obtain();
+        parcel.writeLong(timestamp); // mTimestamp
+        parcel.writeInt(
+                BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE); // mBluetoothStackState
+        parcel.writeLong(controllerTxTimeMs); // mControllerTxTimeMs;
+        parcel.writeLong(controllerRxTimeMs); // mControllerRxTimeMs;
+        parcel.writeLong(controllerIdleTimeMs); // mControllerIdleTimeMs;
+        parcel.writeLong(controllerEnergyUsed); // mControllerEnergyUsed;
+        parcel.writeTypedList(ImmutableList.copyOf(uidTraffic)); // mUidTraffic
+        parcel.setDataPosition(0);
+
+        BluetoothActivityEnergyInfo info =
+                BluetoothActivityEnergyInfo.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return info;
+    }
 }
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
index 2cdb945..7deb8c7 100644
--- a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
@@ -86,7 +86,7 @@
         mFile = testContext.getFileStreamPath("tracing_test.dat");
         //noinspection ResultOfMethodCallIgnored
         mFile.delete();
-        mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader);
+        mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader, 1024);
     }
 
     @After
diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp
index e53ba3e..a1df878 100644
--- a/tools/codegen/Android.bp
+++ b/tools/codegen/Android.bp
@@ -9,7 +9,7 @@
 
 java_binary_host {
     name: "codegen_cli",
-    manifest: "manifest.txt",
+    main_class: "com.android.codegen.MainKt",
     srcs: [
         "src/**/*.kt",
     ],
diff --git a/tools/codegen/BUILD.bazel b/tools/codegen/BUILD.bazel
deleted file mode 100644
index c14046d..0000000
--- a/tools/codegen/BUILD.bazel
+++ /dev/null
@@ -1,21 +0,0 @@
-# TODO(b/245731902): auto-generate these with bp2build.
-load("@rules_kotlin//kotlin:jvm_library.bzl", "kt_jvm_library")
-
-java_binary(
-    name = "codegen_cli",
-    main_class = "com.android.codegen.MainKt",
-    runtime_deps = [
-        ":codegen_cli_kt_lib",
-    ],
-)
-
-kt_jvm_library(
-    name = "codegen_cli_kt_lib",
-    srcs = glob(["src/**/*.kt"]),
-    deps = ["//external/javaparser"],
-)
-
-kt_jvm_library(
-    name = "codegen-version-info",
-    srcs = glob(["src/**/SharedConstants.kt"]),
-)
diff --git a/tools/codegen/manifest.txt b/tools/codegen/manifest.txt
deleted file mode 100644
index 6e1018b..0000000
--- a/tools/codegen/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-class: com.android.codegen.MainKt