Merge "Adjust caption observer for desktop transition" into tm-qpr-dev
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index c59d757..257ad71 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -315,17 +315,17 @@
     int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
 
     /**
-     * Whether the FingerprintAcquired message is a signal to turn off HBM
+     * Whether the FingerprintAcquired message is a signal to disable the UDFPS display mode.
+     * We want to disable the UDFPS mode as soon as possible to conserve power and provide better
+     * UX. For example, prolonged high-brightness illumination of optical sensors can be unpleasant
+     * to the user, can cause long term display burn-in, and can drain the battery faster.
      */
-    static boolean shouldTurnOffHbm(@FingerprintAcquired int acquiredInfo) {
+    static boolean shouldDisableUdfpsDisplayMode(@FingerprintAcquired int acquiredInfo) {
         switch (acquiredInfo) {
             case FINGERPRINT_ACQUIRED_START:
-                // Authentication just began
+                // Keep the UDFPS mode because the authentication just began.
                 return false;
             case FINGERPRINT_ACQUIRED_GOOD:
-                // Good image captured. Turn off HBM. Success/Reject comes after, which is when
-                // hideUdfpsOverlay will be called.
-                return true;
             case FINGERPRINT_ACQUIRED_PARTIAL:
             case FINGERPRINT_ACQUIRED_INSUFFICIENT:
             case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
@@ -334,11 +334,12 @@
             case FINGERPRINT_ACQUIRED_IMMOBILE:
             case FINGERPRINT_ACQUIRED_TOO_BRIGHT:
             case FINGERPRINT_ACQUIRED_VENDOR:
-                // Bad image captured. Turn off HBM. Matcher will not run, so there's no need to
-                // keep HBM on.
+                // Disable the UDFPS mode because the image capture has finished. The overlay
+                // can be hidden later, once the authentication result arrives.
                 return true;
             case FINGERPRINT_ACQUIRED_UNKNOWN:
             default:
+                // Keep the UDFPS mode in case of an unknown message.
                 return false;
         }
     }
diff --git a/core/java/com/android/internal/jank/TEST_MAPPING b/core/java/com/android/internal/jank/TEST_MAPPING
new file mode 100644
index 0000000..4e00ff1
--- /dev/null
+++ b/core/java/com/android/internal/jank/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.jank"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ],
+      "file_patterns": [
+        "core/java/com/android/internal/jank/.*",
+        "core/tests/coretests/src/com/android/internal/jank/.*"
+      ]
+    }
+  ]
+}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 14a6d5e..d3f9e0a 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -16,6 +16,27 @@
 
 import static android.os.Trace.TRACE_TAG_APP;
 
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -30,6 +51,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.EventLogTags;
 import com.android.internal.os.BackgroundThread;
 
@@ -103,14 +125,14 @@
     public static final int ACTION_START_RECENTS_ANIMATION = 8;
 
     /**
-     * Time it takes the sensor to detect rotation.
-     */
-    public static final int ACTION_ROTATE_SCREEN_SENSOR = 9;
-
-    /**
      * Time it takes to for the camera based algorithm to rotate the screen.
      */
-    public static final int ACTION_ROTATE_SCREEN_CAMERA_CHECK = 10;
+    public static final int ACTION_ROTATE_SCREEN_CAMERA_CHECK = 9;
+
+    /**
+     * Time it takes the sensor to detect rotation.
+     */
+    public static final int ACTION_ROTATE_SCREEN_SENSOR = 10;
 
     /**
      * Time it takes to start unlock animation .
@@ -143,9 +165,14 @@
     public static final int ACTION_LOAD_SHARE_SHEET = 16;
 
     /**
+     * Time it takes for showing the selection toolbar.
+     */
+    public static final int ACTION_SHOW_SELECTION_TOOLBAR = 17;
+
+    /**
      * Time it takes to show AOD display after folding the device.
      */
-    public static final int ACTION_FOLD_TO_AOD = 17;
+    public static final int ACTION_FOLD_TO_AOD = 18;
 
     private static final int[] ACTIONS_ALL = {
         ACTION_EXPAND_PANEL,
@@ -157,14 +184,15 @@
         ACTION_ROTATE_SCREEN,
         ACTION_FACE_WAKE_AND_UNLOCK,
         ACTION_START_RECENTS_ANIMATION,
-        ACTION_ROTATE_SCREEN_SENSOR,
         ACTION_ROTATE_SCREEN_CAMERA_CHECK,
+        ACTION_ROTATE_SCREEN_SENSOR,
         ACTION_LOCKSCREEN_UNLOCK,
         ACTION_USER_SWITCH,
         ACTION_SWITCH_DISPLAY_UNFOLD,
         ACTION_UDFPS_ILLUMINATE,
         ACTION_SHOW_BACK_ARROW,
         ACTION_LOAD_SHARE_SHEET,
+        ACTION_SHOW_SELECTION_TOOLBAR,
         ACTION_FOLD_TO_AOD,
     };
 
@@ -179,39 +207,42 @@
         ACTION_ROTATE_SCREEN,
         ACTION_FACE_WAKE_AND_UNLOCK,
         ACTION_START_RECENTS_ANIMATION,
-        ACTION_ROTATE_SCREEN_SENSOR,
         ACTION_ROTATE_SCREEN_CAMERA_CHECK,
+        ACTION_ROTATE_SCREEN_SENSOR,
         ACTION_LOCKSCREEN_UNLOCK,
         ACTION_USER_SWITCH,
         ACTION_SWITCH_DISPLAY_UNFOLD,
         ACTION_UDFPS_ILLUMINATE,
         ACTION_SHOW_BACK_ARROW,
         ACTION_LOAD_SHARE_SHEET,
-        ACTION_FOLD_TO_AOD
+        ACTION_SHOW_SELECTION_TOOLBAR,
+        ACTION_FOLD_TO_AOD,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Action {
     }
 
-    private static final int[] STATSD_ACTION = new int[]{
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
-            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD
+    @VisibleForTesting
+    public static final int[] STATSD_ACTION = new int[] {
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR,
+            UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD,
     };
 
     private static LatencyTracker sLatencyTracker;
@@ -269,43 +300,45 @@
     public static String getNameOfAction(int atomsProtoAction) {
         // Defined in AtomsProto.java
         switch (atomsProtoAction) {
-            case 0:
+            case UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION:
                 return "UNKNOWN";
-            case 1:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL:
                 return "ACTION_EXPAND_PANEL";
-            case 2:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS:
                 return "ACTION_TOGGLE_RECENTS";
-            case 3:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK:
                 return "ACTION_FINGERPRINT_WAKE_AND_UNLOCK";
-            case 4:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL:
                 return "ACTION_CHECK_CREDENTIAL";
-            case 5:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED:
                 return "ACTION_CHECK_CREDENTIAL_UNLOCKED";
-            case 6:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN:
                 return "ACTION_TURN_ON_SCREEN";
-            case 7:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN:
                 return "ACTION_ROTATE_SCREEN";
-            case 8:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK:
                 return "ACTION_FACE_WAKE_AND_UNLOCK";
-            case 9:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION:
                 return "ACTION_START_RECENTS_ANIMATION";
-            case 10:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK:
                 return "ACTION_ROTATE_SCREEN_CAMERA_CHECK";
-            case 11:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR:
                 return "ACTION_ROTATE_SCREEN_SENSOR";
-            case 12:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK:
                 return "ACTION_LOCKSCREEN_UNLOCK";
-            case 13:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH:
                 return "ACTION_USER_SWITCH";
-            case 14:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD:
                 return "ACTION_SWITCH_DISPLAY_UNFOLD";
-            case 15:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE:
                 return "ACTION_UDFPS_ILLUMINATE";
-            case 16:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW:
                 return "ACTION_SHOW_BACK_ARROW";
-            case 17:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET:
                 return "ACTION_LOAD_SHARE_SHEET";
-            case 19:
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR:
+                return "ACTION_SHOW_SELECTION_TOOLBAR";
+            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD:
                 return "ACTION_FOLD_TO_AOD";
             default:
                 throw new IllegalArgumentException("Invalid action");
diff --git a/core/java/com/android/internal/util/TEST_MAPPING b/core/java/com/android/internal/util/TEST_MAPPING
index 5881c51..7970dd8 100644
--- a/core/java/com/android/internal/util/TEST_MAPPING
+++ b/core/java/com/android/internal/util/TEST_MAPPING
@@ -1,7 +1,22 @@
 {
   "presubmit": [
     {
-       "name": "ScreenshotHelperTests"
+      "name": "ScreenshotHelperTests"
+    },
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.util.LatencyTrackerTest"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ],
+      "file_patterns": ["LatencyTracker.java"]
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index f24c666..88aa4de 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2937,13 +2937,14 @@
 
     for (const auto &audioProfile : audioProfiles) {
         jobject jAudioProfile;
-        jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &audioProfile, false);
-        if (jStatus == AUDIO_JAVA_BAD_VALUE) {
+        jint jConvertProfileStatus = convertAudioProfileFromNative(
+                                        env, &jAudioProfile, &audioProfile, false);
+        if (jConvertProfileStatus == AUDIO_JAVA_BAD_VALUE) {
             // skipping Java layer unsupported audio formats
             continue;
         }
-        if (jStatus != AUDIO_JAVA_SUCCESS) {
-            return jStatus;
+        if (jConvertProfileStatus != AUDIO_JAVA_SUCCESS) {
+            return jConvertProfileStatus;
         }
         env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile);
         env->DeleteLocalRef(jAudioProfile);
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 17d0b51..b94bd83 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4877,9 +4877,6 @@
         -->
     </array>
 
-    <!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
-    <integer name="config_udfps_illumination_transition_ms">50</integer>
-
     <!-- Indicates whether device has a power button fingerprint sensor. -->
     <bool name="config_is_powerbutton_fps" translatable="false" >false</bool>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6c6a0a3..546bb21 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2706,7 +2706,6 @@
   <java-symbol type="bool" name="allow_test_udfps" />
   <java-symbol type="array" name="config_udfps_sensor_props" />
   <java-symbol type="array" name="config_sfps_sensor_props" />
-  <java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
   <java-symbol type="bool" name="config_is_powerbutton_fps" />
   <java-symbol type="array" name="config_udfps_enroll_stage_thresholds" />
   <java-symbol type="array" name="config_sfps_enroll_stage_thresholds" />
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
index ddc05e0..7338c3a 100644
--- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -204,19 +204,6 @@
         assertEquals(100, Color.alpha(bitmap.getPixel(50, 50)));
     }
 
-    @Test
-    public void testSetBounds() throws Exception {
-        mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
-        mIconDrawable.setBounds(0, 0, 100, 100);
-
-        assertEquals(new Rect(-25, -25, 125, 125), mBackgroundDrawable.getBounds());
-        assertEquals(new Rect(-25, -25, 125, 125), mForegroundDrawable.getBounds());
-
-        mIconDrawable.setBounds(10, 10, 110, 110);
-        assertEquals(new Rect(-15, -15, 135, 135), mBackgroundDrawable.getBounds());
-        assertEquals(new Rect(-15, -15, 135, 135), mForegroundDrawable.getBounds());
-    }
-
     //
     // Utils
     //
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 1519e48..ecb281b 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -262,6 +262,8 @@
                         && f.getType() == int.class)
                 .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
 
+        assertThat(enumsMap.size() - 1).isEqualTo(cujs.size());
+
         cujs.forEach(f -> {
             final int cuj = getIntFieldChecked(f);
             final String cujName = f.getName();
@@ -279,7 +281,9 @@
                     .that(expectedEnumName.equals(enumName))
                     .isTrue();
             mExpect
-                    .withMessage(formatSimple("getNameOfCuj(%d) not matches %s", cuj, cujName))
+                    .withMessage(
+                            formatSimple("getNameOfCuj(%d) not matches: %s, expected=%s",
+                                    cuj, cujName, expectedNameOfCuj))
                     .that(cujName.equals(expectedNameOfCuj))
                     .isTrue();
         });
diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
new file mode 100644
index 0000000..e384e69
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static android.text.TextUtils.formatSimple;
+
+import static com.android.internal.util.LatencyTracker.STATSD_ACTION;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@SmallTest
+public class LatencyTrackerTest {
+    private static final String ENUM_NAME_PREFIX = "UIACTION_LATENCY_REPORTED__ACTION__";
+
+    @Rule
+    public final Expect mExpect = Expect.create();
+
+    @Test
+    public void testCujsMapToEnumsCorrectly() {
+        List<Field> actions = Arrays.stream(LatencyTracker.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith("ACTION_")
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .collect(Collectors.toList());
+
+        Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
+                .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
+                        && Modifier.isStatic(f.getModifiers())
+                        && f.getType() == int.class)
+                .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
+
+        assertThat(enumsMap.size() - 1).isEqualTo(actions.size());
+
+        actions.forEach(f -> {
+            final int action = getIntFieldChecked(f);
+            final String actionName = f.getName();
+            final String expectedEnumName = formatSimple("%s%s", ENUM_NAME_PREFIX, actionName);
+            final int enumKey = STATSD_ACTION[action];
+            final String enumName = enumsMap.get(enumKey);
+            final String expectedActionName = LatencyTracker.getNameOfAction(enumKey);
+            mExpect
+                    .withMessage(formatSimple(
+                            "%s (%d) not matches %s (%d)", actionName, action, enumName, enumKey))
+                    .that(expectedEnumName.equals(enumName))
+                    .isTrue();
+            mExpect
+                    .withMessage(
+                            formatSimple("getNameOfAction(%d) not matches: %s, expected=%s",
+                                    enumKey, actionName, expectedActionName))
+                    .that(actionName.equals(expectedActionName))
+                    .isTrue();
+        });
+    }
+
+    @Test
+    public void testCujTypeEnumCorrectlyDefined() throws Exception {
+        List<Field> cujEnumFields =
+                Arrays.stream(LatencyTracker.class.getDeclaredFields())
+                        .filter(field -> field.getName().startsWith("ACTION_")
+                                && Modifier.isStatic(field.getModifiers())
+                                && field.getType() == int.class)
+                        .collect(Collectors.toList());
+
+        HashSet<Integer> allValues = new HashSet<>();
+        for (Field field : cujEnumFields) {
+            int fieldValue = field.getInt(null);
+            assertWithMessage(
+                    "Field %s must have a mapping to a value in STATSD_ACTION",
+                    field.getName())
+                    .that(fieldValue < STATSD_ACTION.length)
+                    .isTrue();
+            assertWithMessage("All CujType values must be unique. Field %s repeats existing value.",
+                    field.getName())
+                    .that(allValues.add(fieldValue))
+                    .isTrue();
+        }
+    }
+
+    private int getIntFieldChecked(Field field) {
+        try {
+            return field.getInt(null);
+        } catch (IllegalAccessException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 47de37d..688425a 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -26,6 +26,8 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -37,6 +39,8 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.PathParser;
@@ -102,8 +106,7 @@
     private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
 
     /**
-     * Unused path.
-     * TODO: Remove once the layoutLib is updated
+     * Clip path defined in R.string.config_icon_mask.
      */
     private static Path sMask;
 
@@ -111,10 +114,9 @@
      * Scaled mask based on the view bounds.
      */
     private final Path mMask;
-    private final Path mMaskTransformed;
+    private final Path mMaskScaleOnly;
     private final Matrix mMaskMatrix;
     private final Region mTransparentRegion;
-    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
     /**
      * Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
@@ -129,10 +131,19 @@
      */
     LayerState mLayerState;
 
+    private Shader mLayersShader;
+    private Bitmap mLayersBitmap;
+
     private final Rect mTmpOutRect = new Rect();
     private Rect mHotspotBounds;
     private boolean mMutated;
 
+    private boolean mSuspendChildInvalidation;
+    private boolean mChildRequestedInvalidation;
+    private final Canvas mCanvas;
+    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
+        Paint.FILTER_BITMAP_FLAG);
+
     /**
      * Constructor used for xml inflation.
      */
@@ -146,16 +157,19 @@
      */
     AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
         mLayerState = createConstantState(state, res);
-        // config_icon_mask from context bound resource may have been changed using
+        // config_icon_mask from context bound resource may have been chaged using
         // OverlayManager. Read that one first.
         Resources r = ActivityThread.currentActivityThread() == null
                 ? Resources.getSystem()
                 : ActivityThread.currentActivityThread().getApplication().getResources();
-        mMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
-        mMaskTransformed = new Path();
+        // TODO: either make sMask update only when config_icon_mask changes OR
+        // get rid of it all-together in layoutlib
+        sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
+        mMask = new Path(sMask);
+        mMaskScaleOnly = new Path(mMask);
         mMaskMatrix = new Matrix();
+        mCanvas = new Canvas();
         mTransparentRegion = new Region();
-        mPaint.setColor(Color.BLACK);
     }
 
     private ChildDrawable createChildDrawable(Drawable drawable) {
@@ -266,7 +280,7 @@
      * @return the mask path object used to clip the drawable
      */
     public Path getIconMask() {
-        return mMaskTransformed;
+        return mMask;
     }
 
     /**
@@ -308,47 +322,92 @@
         if (bounds.isEmpty()) {
             return;
         }
-        // Set the child layer bounds bigger than the view port size
-        // by {@link #DEFAULT_VIEW_PORT_SCALE}
-        float cX = bounds.exactCenterX();
-        float cY = bounds.exactCenterY();
-        float insetWidth = bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2);
-        float insetHeight = bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2);
-        final Rect outRect = mTmpOutRect;
-        outRect.set(
-                (int) (cX - insetWidth),
-                (int) (cY - insetHeight),
-                (int) (cX + insetWidth),
-                (int) (cY + insetHeight));
+        updateLayerBounds(bounds);
+    }
+
+    private void updateLayerBounds(Rect bounds) {
+        if (bounds.isEmpty()) {
+            return;
+        }
+        try {
+            suspendChildInvalidation();
+            updateLayerBoundsInternal(bounds);
+            updateMaskBoundsInternal(bounds);
+        } finally {
+            resumeChildInvalidation();
+        }
+    }
+
+    /**
+     * Set the child layer bounds bigger than the view port size by {@link #DEFAULT_VIEW_PORT_SCALE}
+     */
+    private void updateLayerBoundsInternal(Rect bounds) {
+        int cX = bounds.width() / 2;
+        int cY = bounds.height() / 2;
 
         for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
             final ChildDrawable r = mLayerState.mChildren[i];
-            if (r.mDrawable != null) {
-                r.mDrawable.setBounds(outRect);
+            final Drawable d = r.mDrawable;
+            if (d == null) {
+                continue;
             }
+
+            int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            final Rect outRect = mTmpOutRect;
+            outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
+
+            d.setBounds(outRect);
+        }
+    }
+
+    private void updateMaskBoundsInternal(Rect b) {
+        // reset everything that depends on the view bounds
+        mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+        sMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+        mMaskMatrix.postTranslate(b.left, b.top);
+        sMask.transform(mMaskMatrix, mMask);
+
+        if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+                || mLayersBitmap.getHeight() != b.height()) {
+            mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
         }
 
-        // Update the clipping mask
-        mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE);
-        mMaskMatrix.postTranslate(bounds.left, bounds.top);
-        mMask.transform(mMaskMatrix, mMaskTransformed);
-
-        // Clear the transparent region, it is calculated lazily
+        mPaint.setShader(null);
         mTransparentRegion.setEmpty();
+        mLayersShader = null;
     }
 
     @Override
     public void draw(Canvas canvas) {
-        int saveCount = canvas.save();
-        canvas.clipPath(mMaskTransformed);
-        canvas.drawPaint(mPaint);
-        if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
-            mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(canvas);
+        if (mLayersBitmap == null) {
+            return;
         }
-        if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
-            mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(canvas);
+        if (mLayersShader == null) {
+            mCanvas.setBitmap(mLayersBitmap);
+            mCanvas.drawColor(Color.BLACK);
+            if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
+                mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas);
+            }
+            if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
+                mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(mCanvas);
+            }
+            mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
+            mPaint.setShader(mLayersShader);
         }
-        canvas.restoreToCount(saveCount);
+        if (mMaskScaleOnly != null) {
+            Rect bounds = getBounds();
+            canvas.translate(bounds.left, bounds.top);
+            canvas.drawPath(mMaskScaleOnly, mPaint);
+            canvas.translate(-bounds.left, -bounds.top);
+        }
+    }
+
+    @Override
+    public void invalidateSelf() {
+        mLayersShader = null;
+        super.invalidateSelf();
     }
 
     @Override
@@ -541,9 +600,37 @@
         return false;
     }
 
+    /**
+     * Temporarily suspends child invalidation.
+     *
+     * @see #resumeChildInvalidation()
+     */
+    private void suspendChildInvalidation() {
+        mSuspendChildInvalidation = true;
+    }
+
+    /**
+     * Resumes child invalidation after suspension, immediately performing an
+     * invalidation if one was requested by a child during suspension.
+     *
+     * @see #suspendChildInvalidation()
+     */
+    private void resumeChildInvalidation() {
+        mSuspendChildInvalidation = false;
+
+        if (mChildRequestedInvalidation) {
+            mChildRequestedInvalidation = false;
+            invalidateSelf();
+        }
+    }
+
     @Override
     public void invalidateDrawable(@NonNull Drawable who) {
-        invalidateSelf();
+        if (mSuspendChildInvalidation) {
+            mChildRequestedInvalidation = true;
+        } else {
+            invalidateSelf();
+        }
     }
 
     @Override
@@ -627,13 +714,6 @@
     @Override
     public void setAlpha(int alpha) {
         mPaint.setAlpha(alpha);
-        final ChildDrawable[] array = mLayerState.mChildren;
-        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
-            final Drawable dr = array[i].mDrawable;
-            if (dr != null) {
-                dr.setAlpha(alpha);
-            }
-        }
     }
 
     @Override
@@ -736,6 +816,10 @@
             }
         }
 
+        if (changed) {
+            updateLayerBounds(getBounds());
+        }
+
         return changed;
     }
 
@@ -751,6 +835,10 @@
             }
         }
 
+        if (changed) {
+            updateLayerBounds(getBounds());
+        }
+
         return changed;
     }
 
@@ -891,7 +979,6 @@
         int mDensity;
 
         // The density to use when inflating/looking up the children drawables. A value of 0 means
-
         // use the system's density.
         int mSrcDensityOverride = 0;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 435d8ea..62bf517 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -17,6 +17,8 @@
 package com.android.wm.shell.draganddrop;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -263,6 +265,9 @@
             mStarter.startShortcut(packageName, id, position, opts, user);
         } else {
             final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
+            // Put BAL flags to avoid activity start aborted.
+            opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
+            opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
             mStarter.startIntent(launchIntent, null /* fillIntent */, position, opts);
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f170e77..1a52d8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -324,6 +324,19 @@
         return mPipTransitionController;
     }
 
+    /**
+     * Returns true if the PiP window is currently being animated.
+     */
+    public boolean isAnimating() {
+        // TODO(b/183746978) move this to PipAnimationController, and inject that in PipController
+        PipAnimationController.PipTransitionAnimator animator =
+                mPipAnimationController.getCurrentAnimator();
+        if (animator != null && animator.isRunning()) {
+            return true;
+        }
+        return false;
+    }
+
     public Rect getCurrentOrAnimatingBounds() {
         PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getCurrentAnimator();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index 6dd02e4..84071e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -54,13 +54,8 @@
                 ? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas()
                 : pipBoundsState.getBounds();
         float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
-        int verticalGravity;
+        int verticalGravity = Gravity.BOTTOM;
         int horizontalGravity;
-        if (snapFraction < 1.5f || snapFraction >= 3.5f) {
-            verticalGravity = Gravity.NO_GRAVITY;
-        } else {
-            verticalGravity = Gravity.BOTTOM;
-        }
         if (snapFraction >= 0.5f && snapFraction < 2.5f) {
             horizontalGravity = Gravity.RIGHT;
         } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 3d879b6..bc8191d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -149,7 +149,42 @@
     private final Rect mTmpInsetBounds = new Rect();
     private final int mEnterAnimationDuration;
 
-    private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback;
+    private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback =
+            this::onKeepClearAreasChangedCallback;
+
+    private void onKeepClearAreasChangedCallback() {
+        if (!mEnablePipKeepClearAlgorithm) {
+            // early bail out if the keep clear areas feature is disabled
+            return;
+        }
+        // if there is another animation ongoing, wait for it to finish and try again
+        if (mPipTaskOrganizer.isAnimating()) {
+            mMainExecutor.removeCallbacks(
+                    mMovePipInResponseToKeepClearAreasChangeCallback);
+            mMainExecutor.executeDelayed(
+                    mMovePipInResponseToKeepClearAreasChangeCallback,
+                    PIP_KEEP_CLEAR_AREAS_DELAY);
+            return;
+        }
+        updatePipPositionForKeepClearAreas();
+    }
+
+    private void updatePipPositionForKeepClearAreas() {
+        if (!mEnablePipKeepClearAlgorithm) {
+            // early bail out if the keep clear areas feature is disabled
+            return;
+        }
+        // only move if already in pip, other transitions account for keep clear areas
+        if (mPipTransitionState.hasEnteredPip()) {
+            Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
+                    mPipBoundsAlgorithm);
+            // only move if the bounds are actually different
+            if (destBounds != mPipBoundsState.getBounds()) {
+                mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
+                        mEnterAnimationDuration, null);
+            }
+        }
+    }
 
     private boolean mIsInFixedRotation;
     private PipAnimationListener mPinnedStackAnimationRecentsCallback;
@@ -302,6 +337,9 @@
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
             mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
             mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
+            if (imeVisible) {
+                updatePipPositionForKeepClearAreas();
+            }
         }
 
         @Override
@@ -414,15 +452,6 @@
 
         mEnterAnimationDuration = mContext.getResources()
                 .getInteger(R.integer.config_pipEnterAnimationDuration);
-        mMovePipInResponseToKeepClearAreasChangeCallback = () -> {
-            // only move if already in pip, other transitions account for keep clear areas
-            if (mPipTransitionState.hasEnteredPip()) {
-                Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
-                        mPipBoundsAlgorithm);
-                mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
-                        mEnterAnimationDuration, null);
-            }
-        };
         mPipParamsChangedForwarder = pipParamsChangedForwarder;
         mDisplayInsetsController = displayInsetsController;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 84d9217..1f3f31e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -34,6 +34,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.util.Size;
 import android.view.DisplayCutout;
@@ -70,6 +71,9 @@
     private static final String TAG = "PipTouchHandler";
     private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
 
+    private static final boolean ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
+            SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false);
+
     // Allow PIP to resize to a slightly bigger state upon touch
     private boolean mEnableResize;
     private final Context mContext;
@@ -426,6 +430,9 @@
             if (mTouchState.isUserInteracting()) {
                 // Defer the update of the current movement bounds until after the user finishes
                 // touching the screen
+            } else if (ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
+                // Ignore moving PiP if keep clear algorithm is enabled, since IME and shelf height
+                // now are accounted for in the keep clear algorithm calculations
             } else {
                 final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
                 final Rect toMovementBounds = new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 025e559..3758471 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -154,7 +154,8 @@
     private StageCoordinator mStageCoordinator;
     // Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
     // outside the bounds of the roots by being reparented into a higher level fullscreen container
-    private SurfaceControl mSplitTasksContainerLayer;
+    private SurfaceControl mGoingToRecentsTasksLayer;
+    private SurfaceControl mStartingSplitTasksLayer;
 
     public SplitScreenController(Context context,
             ShellInit shellInit,
@@ -509,19 +510,53 @@
     }
 
     RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
+        if (ENABLE_SHELL_TRANSITIONS) return null;
+
         if (isSplitScreenVisible()) {
             // Evict child tasks except the top visible one under split root to ensure it could be
             // launched as full screen when switching to it on recents.
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
             mSyncQueue.queue(wct);
+        } else {
+            return null;
         }
-        return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
+
+        SurfaceControl.Transaction t = mTransactionPool.acquire();
+        if (mGoingToRecentsTasksLayer != null) {
+            t.remove(mGoingToRecentsTasksLayer);
+        }
+        mGoingToRecentsTasksLayer = reparentSplitTasksForAnimation(apps, t,
+                "SplitScreenController#onGoingToRecentsLegacy" /* callsite */);
+        t.apply();
+        mTransactionPool.release(t);
+
+        return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
     }
 
     RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
+        if (ENABLE_SHELL_TRANSITIONS) return null;
+
+        int openingApps = 0;
+        for (int i = 0; i < apps.length; ++i) {
+            if (apps[i].mode == MODE_OPENING) openingApps++;
+        }
+        if (openingApps < 2) {
+            // Not having enough apps to enter split screen
+            return null;
+        }
+
+        SurfaceControl.Transaction t = mTransactionPool.acquire();
+        if (mStartingSplitTasksLayer != null) {
+            t.remove(mStartingSplitTasksLayer);
+        }
+        mStartingSplitTasksLayer = reparentSplitTasksForAnimation(apps, t,
+                "SplitScreenController#onStartingSplitLegacy" /* callsite */);
+        t.apply();
+        mTransactionPool.release(t);
+
         try {
-            return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
+            return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
         } finally {
             for (RemoteAnimationTarget appTarget : apps) {
                 if (appTarget.leash != null) {
@@ -531,45 +566,23 @@
         }
     }
 
-    private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
-            boolean enterSplitScreen) {
-        if (ENABLE_SHELL_TRANSITIONS) return null;
-
-        if (enterSplitScreen) {
-            int openingApps = 0;
-            for (int i = 0; i < apps.length; ++i) {
-                if (apps[i].mode == MODE_OPENING) openingApps++;
-            }
-            if (openingApps < 2) {
-                // Not having enough apps to enter split screen
-                return null;
-            }
-        } else if (!isSplitScreenVisible()) {
-            return null;
-        }
-
-        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
-        if (mSplitTasksContainerLayer != null) {
-            // Remove the previous layer before recreating
-            transaction.remove(mSplitTasksContainerLayer);
-        }
+    private SurfaceControl reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
+            SurfaceControl.Transaction t, String callsite) {
         final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
                 .setContainerLayer()
                 .setName("RecentsAnimationSplitTasks")
                 .setHidden(false)
-                .setCallsite("SplitScreenController#onGoingtoRecentsLegacy");
+                .setCallsite(callsite);
         mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
-        mSplitTasksContainerLayer = builder.build();
+        final SurfaceControl splitTasksLayer = builder.build();
 
         for (int i = 0; i < apps.length; ++i) {
             final RemoteAnimationTarget appTarget = apps[i];
-            transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
-            transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
+            t.reparent(appTarget.leash, splitTasksLayer);
+            t.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
                     appTarget.screenSpaceBounds.top);
         }
-        transaction.apply();
-        mTransactionPool.release(transaction);
-        return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
+        return splitTasksLayer;
     }
     /**
      * Sets drag info to be logged when splitscreen is entered.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index db35b48..25793ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,8 +18,6 @@
 
 import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -1109,9 +1107,6 @@
 
     private void addActivityOptions(Bundle opts, StageTaskListener stage) {
         opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
-        // Put BAL flags to avoid activity start aborted.
-        opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
-        opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
     }
 
     void updateActivityOptions(Bundle opts, @SplitPosition int position) {
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
index f239a8d..420ae70 100644
--- a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
@@ -15,11 +15,45 @@
   -->
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#000000"
+                    android:valueTo="#ffffff"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#000000"
+                    android:valueTo="#ffffff"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
     <target android:name="time_group">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="183"
+                    android:duration="333"
                     android:propertyName="translateX"
                     android:startOffset="0"
                     android:valueFrom="0"
@@ -38,13 +72,29 @@
                 <group
                     android:name="_R_G_L_0_G"
                     android:translateX="12"
-                    android:translateY="12">
-                    <path
-                        android:name="_R_G_L_0_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#ffffff"
-                        android:fillType="nonZero"
-                        android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c  M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+                    android:translateY="12.469">
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                        android:scaleX="1"
+                        android:scaleY="1">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#000000"
+                            android:fillType="nonZero"
+                            android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+                        android:scaleX="1"
+                        android:scaleY="1">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_1"
+                            android:fillAlpha="1"
+                            android:fillColor="#000000"
+                            android:fillType="nonZero"
+                            android:pathData=" M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c " />
+                    </group>
                 </group>
             </group>
             <group android:name="time_group" />
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
index d46fafa..57d7902 100644
--- a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
@@ -15,35 +15,213 @@
   -->
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:aapt="http://schemas.android.com/aapt">
-    <target android:name="_R_G_L_1_G">
+    <target android:name="_R_G_L_0_G_D_0_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="667"
-                    android:pathData="M 12.125,34.75C 12.104,30.958 12.021,15.792 12,12"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="0">
+                    android:duration="250"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#ffffff"
+                    android:valueTo="#000000"
+                    android:valueType="colorType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_0_G">
+    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="517"
-                    android:pathData="M 12,12C 12.021,8.312 12.104,-6.436999999999999 12.125,-10.125"
-                    android:propertyName="translateXY"
-                    android:propertyXName="translateX"
-                    android:propertyYName="translateY"
-                    android:startOffset="0">
+                    android:duration="383"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1.1500000000000001"
+                    android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="383"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1.1500000000000001"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="767"
+                    android:propertyName="scaleX"
+                    android:startOffset="383"
+                    android:valueFrom="1.1500000000000001"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="767"
+                    android:propertyName="scaleY"
+                    android:startOffset="383"
+                    android:valueFrom="1.1500000000000001"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+                    android:valueTo="M-1.74 -2.93 C-1.74,-2.93 -2.06,-7.3 -2.06,-7.3 C-2.14,-8.99 -1.15,-10 0,-10 C1.15,-10 2.08,-8.88 2.09,-7.3 C2.09,-7.3 1.74,-3.09 1.74,-3.09 C1.74,-3.09 9.44,2.79 9.44,2.79 C9.44,2.79 9.44,4.79 9.44,4.79 C9.44,4.79 1.69,1.57 1.69,1.57 C1.69,1.57 -1.74,-2.93 -1.74,-2.93c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="pathData"
+                    android:startOffset="200"
+                    android:valueFrom="M-1.74 -2.93 C-1.74,-2.93 -2.06,-7.3 -2.06,-7.3 C-2.14,-8.99 -1.15,-10 0,-10 C1.15,-10 2.08,-8.88 2.09,-7.3 C2.09,-7.3 1.74,-3.09 1.74,-3.09 C1.74,-3.09 9.44,2.79 9.44,2.79 C9.44,2.79 9.44,4.79 9.44,4.79 C9.44,4.79 1.69,1.57 1.69,1.57 C1.69,1.57 -1.74,-2.93 -1.74,-2.93c "
+                    android:valueTo="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="pathData"
+                    android:startOffset="417"
+                    android:valueFrom="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+                    android:valueTo="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="250"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#ffffff"
+                    android:valueTo="#000000"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="383"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1.1500000000000001"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="383"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1.1500000000000001"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="767"
+                    android:propertyName="scaleX"
+                    android:startOffset="383"
+                    android:valueFrom="1.1500000000000001"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="767"
+                    android:propertyName="scaleY"
+                    android:startOffset="383"
+                    android:valueFrom="1.1500000000000001"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M1.51 1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.49 1.51,1.49c "
+                    android:valueTo="M1.69 1.58 C1.69,1.58 -1.74,-2.92 -1.74,-2.92 C-1.74,-2.92 -9.44,2.79 -9.44,2.79 C-9.44,2.79 -9.44,4.79 -9.44,4.79 C-9.44,4.79 -1.67,1.42 -1.67,1.42 C-1.67,1.42 -1.11,7.28 -1.11,7.28 C-1.11,7.28 -2.94,8.78 -2.94,8.78 C-2.94,8.78 -2.92,10 -2.92,10 C-2.92,10 0,9 0,9 C0,9 2.92,10 2.92,10 C2.92,10 2.91,8.78 2.91,8.78 C2.91,8.78 1.08,7.28 1.08,7.28 C1.08,7.28 1.69,1.58 1.69,1.58c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="pathData"
+                    android:startOffset="200"
+                    android:valueFrom="M1.69 1.58 C1.69,1.58 -1.74,-2.92 -1.74,-2.92 C-1.74,-2.92 -9.44,2.79 -9.44,2.79 C-9.44,2.79 -9.44,4.79 -9.44,4.79 C-9.44,4.79 -1.67,1.42 -1.67,1.42 C-1.67,1.42 -1.11,7.28 -1.11,7.28 C-1.11,7.28 -2.94,8.78 -2.94,8.78 C-2.94,8.78 -2.92,10 -2.92,10 C-2.92,10 0,9 0,9 C0,9 2.92,10 2.92,10 C2.92,10 2.91,8.78 2.91,8.78 C2.91,8.78 1.08,7.28 1.08,7.28 C1.08,7.28 1.69,1.58 1.69,1.58c "
+                    android:valueTo="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="pathData"
+                    android:startOffset="417"
+                    android:valueFrom="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c "
+                    android:valueTo="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -53,7 +231,7 @@
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="683"
+                    android:duration="1167"
                     android:propertyName="translateX"
                     android:startOffset="0"
                     android:valueFrom="0"
@@ -70,26 +248,31 @@
             android:viewportWidth="24">
             <group android:name="_R_G">
                 <group
-                    android:name="_R_G_L_1_G"
-                    android:translateX="12.125"
-                    android:translateY="34.75">
-                    <path
-                        android:name="_R_G_L_1_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#ffffff"
-                        android:fillType="nonZero"
-                        android:pathData=" M10 4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.5 1.5,-8.5 C1.5,-9.33 0.83,-10 0,-10 C-0.83,-10 -1.5,-9.33 -1.5,-8.5 C-1.5,-8.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4c " />
-                </group>
-                <group
                     android:name="_R_G_L_0_G"
                     android:translateX="12"
-                    android:translateY="12">
-                    <path
-                        android:name="_R_G_L_0_G_D_0_P_0"
-                        android:fillAlpha="1"
-                        android:fillColor="#ffffff"
-                        android:fillType="nonZero"
-                        android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c  M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+                    android:translateY="12.469">
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                        android:scaleX="1"
+                        android:scaleY="1">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#ffffff"
+                            android:fillType="nonZero"
+                            android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+                        android:scaleX="1"
+                        android:scaleY="1">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_1"
+                            android:fillAlpha="1"
+                            android:fillColor="#ffffff"
+                            android:fillType="nonZero"
+                            android:pathData=" M1.51 1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.49 1.51,1.49c " />
+                    </group>
                 </group>
             </group>
             <group android:name="time_group" />
diff --git a/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml b/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml
new file mode 100644
index 0000000..157e67a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c M0 8.62 C-0.42,8.62 -2.78,8.82 -3.07,8.54 C-3.36,8.24 -3.37,-1.87 -3.37,-2.28 C-4.25,-2.69 -4.29,-5.2 -4.01,-5.48 C-3.71,-5.78 -0.42,-5.75 0,-5.75 C0.42,-5.75 4.39,-5.78 4.36,-5.36 C4.22,-2.97 4.47,-3.03 3.34,-1.78 C3.07,-1.48 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+                    android:valueTo="M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c M0 8.62 C-0.42,8.62 -2.24,8.83 -2.54,8.55 C-2.83,8.25 -2.86,9.1 -2.86,8.69 C-2.86,8.27 -3.02,8.85 -2.74,8.57 C-2.44,8.26 -0.43,8.31 -0.02,8.31 C0.4,8.31 2.4,8.17 2.68,8.47 C2.98,8.75 2.93,8.33 2.93,8.75 C2.93,9.17 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1000"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:pivotY="24"
+                    android:scaleX="0.33332999999999996"
+                    android:scaleY="0.33332999999999996"
+                    android:translateX="12"
+                    android:translateY="-4">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#1f1f1f"
+                        android:fillType="nonZero"
+                        android:pathData=" M-12 -15 C-12,-15 12,-15 12,-15 C12,-15 12,-13.8 12,-13.8 C12,-13.8 6,-4.8 6,-4.8 C6,-4.8 6,24 6,24 C6,24 -6,24 -6,24 C-6,24 -6,-4.8 -6,-4.8 C-6,-4.8 -12,-13.8 -12,-13.8 C-12,-13.8 -12,-15 -12,-15c  M12 -21 C12,-21 -12,-21 -12,-21 C-12,-21 -12,-24 -12,-24 C-12,-24 12,-24 12,-24 C12,-24 12,-21 12,-21c  M-12 -3 C-12,-3 -12,30 -12,30 C-12,30 12,30 12,30 C12,30 12,-3 12,-3 C12,-3 18,-12 18,-12 C18,-12 18,-30 18,-30 C18,-30 -18,-30 -18,-30 C-18,-30 -18,-12 -18,-12 C-18,-12 -12,-3 -12,-3c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#1f1f1f"
+                        android:fillType="nonZero"
+                        android:pathData=" M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml b/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml
new file mode 100644
index 0000000..22f5e00
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c M0 8.62 C-0.42,8.62 -2.24,8.83 -2.54,8.55 C-2.83,8.25 -2.86,9.1 -2.86,8.69 C-2.86,8.27 -3.02,8.85 -2.74,8.57 C-2.44,8.26 -0.43,8.31 -0.02,8.31 C0.4,8.31 2.4,8.17 2.68,8.47 C2.98,8.75 2.93,8.33 2.93,8.75 C2.93,9.17 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+                    android:valueTo="M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c M0 8.62 C-0.42,8.62 -2.78,8.82 -3.07,8.54 C-3.36,8.24 -3.37,-1.87 -3.37,-2.28 C-4.25,-2.69 -4.29,-5.2 -4.01,-5.48 C-3.71,-5.78 -0.42,-5.75 0,-5.75 C0.42,-5.75 4.39,-5.78 4.36,-5.36 C4.22,-2.97 4.47,-3.03 3.34,-1.78 C3.07,-1.48 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1000"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:pivotY="24"
+                    android:scaleX="0.33332999999999996"
+                    android:scaleY="0.33332999999999996"
+                    android:translateX="12"
+                    android:translateY="-4">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#1f1f1f"
+                        android:fillType="nonZero"
+                        android:pathData=" M-12 -15 C-12,-15 12,-15 12,-15 C12,-15 12,-13.8 12,-13.8 C12,-13.8 6,-4.8 6,-4.8 C6,-4.8 6,24 6,24 C6,24 -6,24 -6,24 C-6,24 -6,-4.8 -6,-4.8 C-6,-4.8 -12,-13.8 -12,-13.8 C-12,-13.8 -12,-15 -12,-15c  M12 -21 C12,-21 -12,-21 -12,-21 C-12,-21 -12,-24 -12,-24 C-12,-24 12,-24 12,-24 C12,-24 12,-21 12,-21c  M-12 -3 C-12,-3 -12,30 -12,30 C-12,30 12,30 12,30 C12,30 12,-3 12,-3 C12,-3 18,-12 18,-12 C18,-12 18,-30 18,-30 C18,-30 -18,-30 -18,-30 C-18,-30 -18,-12 -18,-12 C-18,-12 -12,-3 -12,-3c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#1f1f1f"
+                        android:fillType="nonZero"
+                        android:pathData=" M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml b/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml
new file mode 100644
index 0000000..b3bdd36
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#000000"
+                    android:valueTo="#edf2eb"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c "
+                    android:valueTo="M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="90"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#000000"
+                    android:valueTo="#edf2eb"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#000000"
+                    android:valueTo="#edf2eb"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_2">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#000000"
+                    android:valueTo="#edf2eb"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_3">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#000000"
+                    android:valueTo="#edf2eb"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1000"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:rotation="90"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#000000"
+                        android:fillType="nonZero"
+                        android:pathData=" M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#000000"
+                            android:fillType="nonZero"
+                            android:pathData=" M6.6 -4.47 C6.6,-4.47 8.04,-5.91 8.04,-5.91 C9.26,-4.26 9.99,-2.21 10,0.01 C10,2.23 9.26,4.27 8.04,5.93 C8.04,5.93 6.6,4.49 6.6,4.49 C7.47,3.2 7.99,1.66 7.99,0 C7.99,-1.65 7.47,-3.19 6.6,-4.47c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_1"
+                            android:fillAlpha="1"
+                            android:fillColor="#000000"
+                            android:fillType="nonZero"
+                            android:pathData=" M-8.01 0 C-8.01,1.67 -7.49,3.22 -6.61,4.5 C-6.61,4.5 -8.04,5.93 -8.04,5.93 C-9.27,4.27 -10,2.22 -10,0 C-10,-2.22 -9.27,-4.26 -8.05,-5.92 C-8.05,-5.92 -6.62,-4.49 -6.62,-4.49 C-7.49,-3.21 -8.01,-1.66 -8.01,0c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_2"
+                            android:fillAlpha="1"
+                            android:fillColor="#000000"
+                            android:fillType="nonZero"
+                            android:pathData=" M-0.01 8 C1.66,8 3.2,7.48 4.48,6.61 C4.48,6.61 5.91,8.05 5.91,8.05 C4.25,9.27 2.21,10 -0.01,10 C-2.22,10 -4.26,9.27 -5.92,8.05 C-5.92,8.05 -4.48,6.61 -4.48,6.61 C-3.21,7.48 -1.67,8 -0.01,8c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_3"
+                            android:fillAlpha="1"
+                            android:fillColor="#000000"
+                            android:fillType="nonZero"
+                            android:pathData=" M-5.93 -8.04 C-4.27,-9.27 -2.23,-10 -0.01,-10 C2.22,-10 4.26,-9.27 5.94,-8.03 C5.94,-8.03 4.5,-6.59 4.5,-6.59 C3.21,-7.47 1.67,-7.99 0,-7.99 C-1.67,-7.99 -3.21,-7.47 -4.49,-6.6 C-4.49,-6.6 -5.93,-8.04 -5.93,-8.04c " />
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml b/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml
new file mode 100644
index 0000000..facbef5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#edf2eb"
+                    android:valueTo="#000000"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c "
+                    android:valueTo="M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="90"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#edf2eb"
+                    android:valueTo="#000000"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="180"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#edf2eb"
+                    android:valueTo="#000000"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="180"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_2">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#edf2eb"
+                    android:valueTo="#000000"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="180"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_3">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="fillColor"
+                    android:startOffset="0"
+                    android:valueFrom="#edf2eb"
+                    android:valueTo="#000000"
+                    android:valueType="colorType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="180"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1000"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:rotation="0"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#edf2eb"
+                        android:fillType="nonZero"
+                        android:pathData=" M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#edf2eb"
+                            android:fillType="nonZero"
+                            android:pathData=" M6.6 -4.47 C6.6,-4.47 8.04,-5.91 8.04,-5.91 C9.26,-4.26 9.99,-2.21 10,0.01 C10,2.23 9.26,4.27 8.04,5.93 C8.04,5.93 6.6,4.49 6.6,4.49 C7.47,3.2 7.99,1.66 7.99,0 C7.99,-1.65 7.47,-3.19 6.6,-4.47c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_1"
+                            android:fillAlpha="1"
+                            android:fillColor="#edf2eb"
+                            android:fillType="nonZero"
+                            android:pathData=" M-8.01 0 C-8.01,1.67 -7.49,3.22 -6.61,4.5 C-6.61,4.5 -8.04,5.93 -8.04,5.93 C-9.27,4.27 -10,2.22 -10,0 C-10,-2.22 -9.27,-4.26 -8.05,-5.92 C-8.05,-5.92 -6.62,-4.49 -6.62,-4.49 C-7.49,-3.21 -8.01,-1.66 -8.01,0c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_2"
+                            android:fillAlpha="1"
+                            android:fillColor="#edf2eb"
+                            android:fillType="nonZero"
+                            android:pathData=" M-0.01 8 C1.66,8 3.2,7.48 4.48,6.61 C4.48,6.61 5.91,8.05 5.91,8.05 C4.25,9.27 2.21,10 -0.01,10 C-2.22,10 -4.26,9.27 -5.92,8.05 C-5.92,8.05 -4.48,6.61 -4.48,6.61 C-3.21,7.48 -1.67,8 -0.01,8c " />
+                    </group>
+                    <group
+                        android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0"
+                        android:rotation="0">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_3"
+                            android:fillAlpha="1"
+                            android:fillColor="#edf2eb"
+                            android:fillType="nonZero"
+                            android:pathData=" M-5.93 -8.04 C-4.27,-9.27 -2.23,-10 -0.01,-10 C2.22,-10 4.26,-9.27 5.94,-8.03 C5.94,-8.03 4.5,-6.59 4.5,-6.59 C3.21,-7.47 1.67,-7.99 0,-7.99 C-1.67,-7.99 -3.21,-7.47 -4.49,-6.6 C-4.49,-6.6 -5.93,-8.04 -5.93,-8.04c " />
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a3b6cfc..3ba78ef 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -56,6 +56,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.util.DisplayUtils;
 import android.util.Log;
 import android.util.RotationUtils;
 import android.util.SparseBooleanArray;
@@ -118,8 +119,6 @@
     private final Provider<UdfpsController> mUdfpsControllerFactory;
     private final Provider<SidefpsController> mSidefpsControllerFactory;
 
-    @NonNull private Point mStableDisplaySize = new Point();
-
     private final Display mDisplay;
     private float mScaleFactor = 1f;
     // sensor locations without any resolution scaling nor rotation adjustments:
@@ -291,7 +290,6 @@
                 }
             });
             mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
-            mUdfpsController.setHalControlsIllumination(mUdfpsProps.get(0).halControlsIllumination);
             mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
         }
 
@@ -509,10 +507,11 @@
      */
     private void updateSensorLocations() {
         mDisplay.getDisplayInfo(mCachedDisplayInfo);
-
+        final Display.Mode maxDisplayMode =
+                DisplayUtils.getMaximumResolutionDisplayMode(mCachedDisplayInfo.supportedModes);
         final float scaleFactor = android.util.DisplayUtils.getPhysicalPixelDisplaySizeRatio(
-                mStableDisplaySize.x, mStableDisplaySize.y, mCachedDisplayInfo.getNaturalWidth(),
-                mCachedDisplayInfo.getNaturalHeight());
+                maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(),
+                mCachedDisplayInfo.getNaturalWidth(), mCachedDisplayInfo.getNaturalHeight());
         if (scaleFactor == Float.POSITIVE_INFINITY) {
             mScaleFactor = 1f;
         } else {
@@ -769,7 +768,6 @@
                     mFingerprintAuthenticatorsRegisteredCallback);
         }
 
-        mStableDisplaySize = mDisplayManager.getStableDisplaySize();
         mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
         mOrientationListener.enable();
         updateSensorLocations();
@@ -1149,7 +1147,6 @@
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         final AuthDialog dialog = mCurrentDialog;
-        pw.println("  stableDisplaySize=" + mStableDisplaySize);
         pw.println("  mCachedDisplayInfo=" + mCachedDisplayInfo);
         pw.println("  mScaleFactor=" + mScaleFactor);
         pw.println("  faceAuthSensorLocationDefault=" + mFaceSensorLocationDefault);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index d7bf261..4fee083 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -23,7 +23,6 @@
 import android.graphics.Point
 import android.hardware.biometrics.BiometricFingerprintConstants
 import android.hardware.biometrics.BiometricSourceType
-import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -95,7 +94,6 @@
     public override fun onViewAttached() {
         authController.addCallback(authControllerCallback)
         updateRippleColor()
-        updateSensorLocation()
         updateUdfpsDependentParams()
         udfpsController?.addCallback(udfpsControllerCallback)
         configurationController.addCallback(configurationChangedListener)
@@ -237,7 +235,11 @@
     }
 
     private fun showDwellRipple() {
-        mView.startDwellRipple(statusBarStateController.isDozing)
+        updateSensorLocation()
+        fingerprintSensorLocation?.let {
+            mView.setFingerprintSensorLocation(it, udfpsRadius)
+            mView.startDwellRipple(statusBarStateController.isDozing)
+        }
     }
 
     private val keyguardUpdateMonitorCallback =
@@ -264,7 +266,7 @@
             acquireInfo: Int
         ) {
             if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
-                    BiometricFingerprintConstants.shouldTurnOffHbm(acquireInfo) &&
+                    BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquireInfo) &&
                     acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
                 // received an 'acquiredBad' message, so immediately retract
                 mView.retractDwellRipple()
@@ -291,13 +293,6 @@
     private val udfpsControllerCallback =
         object : UdfpsController.Callback {
             override fun onFingerDown() {
-                if (fingerprintSensorLocation == null) {
-                    Log.e("AuthRipple", "fingerprintSensorLocation=null onFingerDown. " +
-                            "Skip showing dwell ripple")
-                    return
-                }
-
-                mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
                 showDwellRipple()
             }
 
@@ -310,12 +305,10 @@
         object : AuthController.Callback {
             override fun onAllAuthenticatorsRegistered() {
                 updateUdfpsDependentParams()
-                updateSensorLocation()
             }
 
             override fun onUdfpsLocationChanged() {
                 updateUdfpsDependentParams()
-                updateSensorLocation()
             }
         }
 
@@ -345,13 +338,11 @@
                                 "\n\tudfpsRadius=$udfpsRadius")
                     }
                     "fingerprint" -> {
-                        updateSensorLocation()
                         pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
                         showUnlockRipple(BiometricSourceType.FINGERPRINT)
                     }
                     "face" -> {
                         // note: only shows when about to proceed to the home screen
-                        updateSensorLocation()
                         pw.println("face ripple sensorLocation=$faceSensorLocation")
                         showUnlockRipple(BiometricSourceType.FACE)
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 9281eb8..ad96612 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -54,13 +54,13 @@
         getDrawable().onSensorRectUpdated(bounds);
     }
 
-    void onIlluminationStarting() {
-        getDrawable().setIlluminationShowing(true);
+    void onDisplayConfiguring() {
+        getDrawable().setDisplayConfigured(true);
         getDrawable().invalidateSelf();
     }
 
-    void onIlluminationStopped() {
-        getDrawable().setIlluminationShowing(false);
+    void onDisplayUnconfigured() {
+        getDrawable().setDisplayConfigured(false);
         getDrawable().invalidateSelf();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 742c65c..3ad2bef 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -31,10 +31,10 @@
 /**
  * Handles:
  * 1. registering for listeners when its view is attached and unregistering on view detached
- * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide
+ * 2. pausing UDFPS when FingerprintManager may still be running but we temporarily want to hide
  * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
  * 3. sending events to its view including:
- * - illumination events
+ * - enabling and disabling of the UDFPS display mode
  * - sensor position changes
  * - doze time event
  */
@@ -167,19 +167,20 @@
     }
 
     /**
-     * Udfps has started illuminating and the fingerprint manager is working on authenticating.
+     * The display began transitioning into the UDFPS mode and the fingerprint manager started
+     * authenticating.
      */
-    fun onIlluminationStarting() {
-        view.onIlluminationStarting()
+    fun onDisplayConfiguring() {
+        view.onDisplayConfiguring()
         view.postInvalidate()
     }
 
     /**
-     * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
-     * authenticate.
+     * The display transitioned away from the UDFPS mode and the fingerprint manager stopped
+     * authenticating.
      */
-    fun onIlluminationStopped() {
-        view.onIlluminationStopped()
+    fun onDisplayUnconfigured() {
+        view.onDisplayUnconfigured()
         view.postInvalidate()
     }
 
@@ -197,4 +198,4 @@
      * Called when a view should announce an accessibility event.
      */
     open fun doAnnounceForAccessibility(str: String) {}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 7f2680b..36287f5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -86,7 +86,7 @@
 
 /**
  * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
- * and coordinates triggering of the high-brightness mode (HBM).
+ * and toggles the UDFPS display mode.
  *
  * Note that the current architecture is designed so that a single {@link UdfpsController}
  * controls/manages all UDFPS sensors. In other words, a single controller is registered with
@@ -123,7 +123,7 @@
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
     @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    @Nullable private final UdfpsHbmProvider mHbmProvider;
+    @Nullable private final UdfpsDisplayModeProvider mUdfpsDisplayMode;
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final SystemClock mSystemClock;
     @NonNull private final UnlockedScreenOffAnimationController
@@ -135,7 +135,6 @@
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting int mSensorId;
-    private boolean mHalControlsIllumination;
     @VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
     // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
     @Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@@ -147,10 +146,9 @@
     private int mActivePointerId = -1;
     // The timestamp of the most recent touch log.
     private long mTouchLogTime;
-    // Sensor has a capture (good or bad) for this touch. Do not need to illuminate for this
-    // particular touch event anymore. In other words, do not illuminate until user lifts and
-    // touches the sensor area again.
-    // TODO: We should probably try to make touch/illumination things more of a FSM
+    // Sensor has a capture (good or bad) for this touch. No need to enable the UDFPS display mode
+    // anymore for this particular touch event. In other words, do not enable the UDFPS mode until
+    // the user touches the sensor area again.
     private boolean mAcquiredReceived;
 
     // The current request from FingerprintService. Null if no current request.
@@ -211,8 +209,8 @@
                             mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
                             mLockscreenShadeTransitionController, mConfigurationController,
                             mSystemClock, mKeyguardStateController,
-                            mUnlockedScreenOffAnimationController, mHalControlsIllumination,
-                            mHbmProvider, requestId, reason, callback,
+                            mUnlockedScreenOffAnimationController,
+                            mUdfpsDisplayMode, requestId, reason, callback,
                             (view, event, fromUdfpsView) -> onTouch(requestId, event,
                                     fromUdfpsView), mActivityLaunchAnimator)));
         }
@@ -236,7 +234,7 @@
                 int sensorId,
                 @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo
         ) {
-            if (BiometricFingerprintConstants.shouldTurnOffHbm(acquiredInfo)) {
+            if (BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquiredInfo)) {
                 boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD;
                 mFgExecutor.execute(() -> {
                     if (mOverlay == null) {
@@ -247,7 +245,7 @@
                     mAcquiredReceived = true;
                     final UdfpsView view = mOverlay.getOverlayView();
                     if (view != null) {
-                        view.stopIllumination(); // turn off HBM
+                        view.unconfigureDisplay();
                     }
                     if (acquiredGood) {
                         mOverlay.onAcquiredGood();
@@ -292,7 +290,7 @@
     /**
      * Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
      *
-     * @param sensorId sensor for which the overlay is getting updated.
+     * @param sensorId      sensor for which the overlay is getting updated.
      * @param overlayParams See {@link UdfpsOverlayParams}.
      */
     public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
@@ -321,11 +319,6 @@
         mAuthControllerUpdateUdfpsLocation = r;
     }
 
-    // TODO(b/229290039): UDFPS controller should manage its properties on its own. Remove this.
-    public void setHalControlsIllumination(boolean value) {
-        mHalControlsIllumination = value;
-    }
-
     /**
      * Calculate the pointer speed given a velocity tracker and the pointer id.
      * This assumes that the velocity tracker has already been passed all relevant motion events.
@@ -369,8 +362,8 @@
     }
 
     /**
-     * @param x coordinate
-     * @param y coordinate
+     * @param x                   coordinate
+     * @param y                   coordinate
      * @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else,
      *                            calculate from the display dimensions in portrait orientation
      */
@@ -423,7 +416,7 @@
         }
 
         final UdfpsView udfpsView = mOverlay.getOverlayView();
-        final boolean isIlluminationRequested = udfpsView.isIlluminationRequested();
+        final boolean isDisplayConfigured = udfpsView.isDisplayConfigured();
         boolean handled = false;
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_OUTSIDE:
@@ -507,7 +500,7 @@
                                 "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
                                 minor, major, v, exceedsVelocityThreshold);
                         final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
-                        if (!isIlluminationRequested && !mAcquiredReceived
+                        if (!isDisplayConfigured && !mAcquiredReceived
                                 && !exceedsVelocityThreshold) {
 
                             final float scale = mOverlayParams.getScaleFactor();
@@ -598,7 +591,7 @@
             @NonNull VibratorHelper vibrator,
             @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
             @NonNull UdfpsShell udfpsShell,
-            @NonNull Optional<UdfpsHbmProvider> hbmProvider,
+            @NonNull Optional<UdfpsDisplayModeProvider> udfpsDisplayMode,
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull DisplayManager displayManager,
             @Main Handler mainHandler,
@@ -630,7 +623,7 @@
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
-        mHbmProvider = hbmProvider.orElse(null);
+        mUdfpsDisplayMode = udfpsDisplayMode.orElse(null);
         screenLifecycle.addObserver(mScreenObserver);
         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
         mConfigurationController = configurationController;
@@ -804,15 +797,14 @@
     }
 
     /**
-     * Cancel updfs scan affordances - ability to hide the HbmSurfaceView (white circle) before
-     * user explicitly lifts their finger. Generally, this should be called whenever udfps fails
-     * or errors.
+     * Cancel UDFPS affordances - ability to hide the UDFPS overlay before the user explicitly
+     * lifts their finger. Generally, this should be called on errors in the authentication flow.
      *
      * The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give
      * ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually.
      * This should be called when authentication either succeeds or fails. Failing to cancel the
-     * scan will leave the screen in high brightness mode and will show the HbmSurfaceView until
-     * the user lifts their finger.
+     * scan will leave the display in the UDFPS mode until the user lifts their finger. On optical
+     * sensors, this can result in illumination persisting for longer than necessary.
      */
     void onCancelUdfps() {
         if (mOverlay != null && mOverlay.getOverlayView() != null) {
@@ -874,7 +866,7 @@
         Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
         final UdfpsView view = mOverlay.getOverlayView();
         if (view != null) {
-            view.startIllumination(() -> {
+            view.configureDisplay(() -> {
                 if (mAlternateTouchProvider != null) {
                     mBiometricExecutor.execute(() -> {
                         mAlternateTouchProvider.onUiReady();
@@ -914,8 +906,8 @@
             }
         }
         mOnFingerDown = false;
-        if (view.isIlluminationRequested()) {
-            view.stopIllumination();
+        if (view.isDisplayConfigured()) {
+            view.unconfigureDisplay();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index ec72057..1c62f8a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -77,8 +77,7 @@
     private val systemClock: SystemClock,
     private val keyguardStateController: KeyguardStateController,
     private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
-    private val halControlsIllumination: Boolean,
-    private var hbmProvider: UdfpsHbmProvider,
+    private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
     val requestId: Long,
     @ShowReason val requestReason: Int,
     private val controllerCallback: IUdfpsOverlayControllerCallback,
@@ -102,8 +101,8 @@
         fitInsetsTypes = 0
         gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
         layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
-        flags =
-            (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+        flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
+          WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
         privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
         // Avoid announcing window title.
         accessibilityTitle = " "
@@ -140,8 +139,7 @@
                     R.layout.udfps_view, null, false
                 ) as UdfpsView).apply {
                     overlayParams = params
-                    halControlsIllumination = this@UdfpsControllerOverlay.halControlsIllumination
-                    setHbmProvider(hbmProvider)
+                    setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
                     val animation = inflateUdfpsAnimation(this, controller)
                     if (animation != null) {
                         animation.init()
@@ -250,8 +248,8 @@
         val wasShowing = isShowing
 
         overlayView?.apply {
-            if (isIlluminationRequested) {
-                stopIllumination()
+            if (isDisplayConfigured) {
+                unconfigureDisplay()
             }
             windowManager.removeView(this)
             setOnTouchListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java
new file mode 100644
index 0000000..c6957ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface for toggling the optimal display mode for the under-display fingerprint sensor
+ * (UDFPS). For example, the implementation might change the refresh rate and activate a
+ * high-brightness mode.
+ */
+public interface UdfpsDisplayModeProvider {
+
+    /**
+     * Enables the optimal display mode for UDFPS. The mode will persist until
+     * {@link #disable(Runnable)} is called.
+     *
+     * This call must be made from the UI thread. The callback, if provided, will also be invoked
+     * from the UI thread.
+     *
+     * @param onEnabled A runnable that will be executed once the mode is enabled.
+     */
+    void enable(@Nullable Runnable onEnabled);
+
+    /**
+     * Disables the mode that was enabled by {@link #enable(Runnable)}.
+     *
+     * The call must be made from the UI thread. The callback, if provided, will also be invoked
+     * from the UI thread.
+     *
+     * @param onDisabled A runnable that will be executed once mode is disabled.
+     */
+    void disable(@Nullable Runnable onDisabled);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
index ee112b4..511b4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
@@ -51,7 +51,7 @@
             invalidateSelf()
         }
 
-    var isIlluminationShowing: Boolean = false
+    var isDisplayConfigured: Boolean = false
         set(showing) {
             if (field == showing) {
                 return
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 1317492..1e35958 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -197,7 +197,7 @@
 
     @Override
     public void draw(@NonNull Canvas canvas) {
-        if (isIlluminationShowing()) {
+        if (isDisplayConfigured()) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
index 1afa36b..9f6b6d7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
@@ -23,7 +23,7 @@
  */
 class UdfpsFpDrawable(context: Context) : UdfpsDrawable(context) {
     override fun draw(canvas: Canvas) {
-        if (isIlluminationShowing) {
+        if (isDisplayConfigured) {
             return
         }
         fingerprintDrawable.draw(canvas)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
deleted file mode 100644
index f26dd5f..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.Nullable;
-
-/**
- * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
- * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
- * illumination is no longer necessary.
- */
-public interface UdfpsHbmProvider {
-
-    /**
-     * UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
-     *
-     * This method is a no-op when some type of HBM is already enabled.
-     *
-     * This method must be called from the UI thread. The callback, if provided, will also be
-     * invoked from the UI thread.
-     *
-     * @param onHbmEnabled A runnable that will be executed once HBM is enabled.
-     *
-     * TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense.
-     *     This only makes sense now because vendor code may rely on the side effects of enableHbm.
-     */
-    void enableHbm(boolean halControlsIllumination, @Nullable Runnable onHbmEnabled);
-
-    /**
-     * UdfpsView will call this to disable HBM when illumination is no longer needed.
-     *
-     * This method will disable HBM if HBM is enabled. Otherwise, if HBM is already disabled,
-     * this method is a no-op.
-     *
-     * The call must be made from the UI thread. The callback, if provided, will also be invoked
-     * from the UI thread.
-     *
-     * @param onHbmDisabled A runnable that will be executed once HBM is disabled.
-     */
-    void disableHbm(@Nullable Runnable onHbmDisabled);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
deleted file mode 100644
index f85e936..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.Nullable;
-
-/**
- * Interface that should be implemented by UI's that need to coordinate user touches,
- * views/animations, and modules that start/stop display illumination.
- */
-interface UdfpsIlluminator {
-    /**
-     * @param hbmProvider Invoked when HBM should be enabled or disabled.
-     */
-    void setHbmProvider(@Nullable UdfpsHbmProvider hbmProvider);
-
-    /**
-     * Invoked when illumination should start.
-     * @param onIlluminatedRunnable Invoked when the display has been illuminated.
-     */
-    void startIllumination(@Nullable Runnable onIlluminatedRunnable);
-
-    /**
-     * Invoked when illumination should end.
-     */
-    void stopIllumination();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index f28fedb..bc274a0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -101,11 +101,11 @@
     }
 
     @Override
-    void onIlluminationStarting() {
+    void onDisplayConfiguring() {
     }
 
     @Override
-    void onIlluminationStopped() {
+    void onDisplayUnconfigured() {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
index 245c225..a15456d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
@@ -36,12 +36,12 @@
 class UdfpsView(
     context: Context,
     attrs: AttributeSet?
-) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator {
+) : FrameLayout(context, attrs), DozeReceiver {
 
     // sensorRect may be bigger than the sensor. True sensor dimensions are defined in
     // overlayParams.sensorBounds
     private val sensorRect = RectF()
-    private var hbmProvider: UdfpsHbmProvider? = null
+    private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
     private val debugTextPaint = Paint().apply {
         isAntiAlias = true
         color = Color.BLUE
@@ -56,19 +56,12 @@
             a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f)
         }
 
-    private val onIlluminatedDelayMs = context.resources.getInteger(
-        com.android.internal.R.integer.config_udfps_illumination_transition_ms
-    ).toLong()
-
     /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
     var animationViewController: UdfpsAnimationViewController<*>? = null
 
     /** Parameters that affect the position and size of the overlay. */
     var overlayParams = UdfpsOverlayParams()
 
-    /** Whether the HAL is responsible for enabling and disabling of LHBM. */
-    var halControlsIllumination: Boolean = true
-
     /** Debug message. */
     var debugMessage: String? = null
         set(value) {
@@ -76,12 +69,12 @@
             postInvalidate()
         }
 
-    /** When [startIllumination] has been called but not stopped via [stopIllumination]. */
-    var isIlluminationRequested: Boolean = false
+    /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
+    var isDisplayConfigured: Boolean = false
         private set
 
-    override fun setHbmProvider(provider: UdfpsHbmProvider?) {
-        hbmProvider = provider
+    fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
+        mUdfpsDisplayMode = udfpsDisplayModeProvider
     }
 
     // Don't propagate any touch events to the child views.
@@ -124,7 +117,7 @@
 
     override fun onDraw(canvas: Canvas) {
         super.onDraw(canvas)
-        if (!isIlluminationRequested) {
+        if (!isDisplayConfigured) {
             if (!debugMessage.isNullOrEmpty()) {
                 canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
             }
@@ -147,36 +140,15 @@
             !(animationViewController?.shouldPauseAuth() ?: false)
     }
 
-    /**
-     * Start and run [onIlluminatedRunnable] when the first illumination frame reaches the panel.
-     */
-    override fun startIllumination(onIlluminatedRunnable: Runnable?) {
-        isIlluminationRequested = true
-        animationViewController?.onIlluminationStarting()
-        doIlluminate(onIlluminatedRunnable)
+    fun configureDisplay(onDisplayConfigured: Runnable) {
+        isDisplayConfigured = true
+        animationViewController?.onDisplayConfiguring()
+        mUdfpsDisplayMode?.enable(onDisplayConfigured)
     }
 
-    private fun doIlluminate(onIlluminatedRunnable: Runnable?) {
-        // TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense.
-        // This only makes sense now because vendor code may rely on the side effects of enableHbm.
-        hbmProvider?.enableHbm(halControlsIllumination) {
-            if (onIlluminatedRunnable != null) {
-                if (halControlsIllumination) {
-                    onIlluminatedRunnable.run()
-                } else {
-                    // No framework API can reliably tell when a frame reaches the panel. A timeout
-                    // is the safest solution.
-                    postDelayed(onIlluminatedRunnable, onIlluminatedDelayMs)
-                }
-            } else {
-                Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null")
-            }
-        }
-    }
-
-    override fun stopIllumination() {
-        isIlluminationRequested = false
-        animationViewController?.onIlluminationStopped()
-        hbmProvider?.disableHbm(null /* onHbmDisabled */)
+    fun unconfigureDisplay() {
+        isDisplayConfigured = false
+        animationViewController?.onDisplayUnconfigured()
+        mUdfpsDisplayMode?.disable(null /* onDisabled */)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index c21e36a..7e499eb 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -119,15 +119,12 @@
  */
 public class ClipboardOverlayController {
     private static final String TAG = "ClipboardOverlayCtrlr";
-    private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
 
     /** Constants for screenshot/copy deconflicting */
     public static final String SCREENSHOT_ACTION = "com.android.systemui.SCREENSHOT";
     public static final String SELF_PERMISSION = "com.android.systemui.permission.SELF";
     public static final String COPY_OVERLAY_ACTION = "com.android.systemui.COPY";
 
-    private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
-
     private static final int CLIPBOARD_DEFAULT_TIMEOUT_MILLIS = 6000;
     private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
     private static final int FONT_SEARCH_STEP_PX = 4;
@@ -383,7 +380,7 @@
                     mTextPreview);
             accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
         }
-        Intent remoteCopyIntent = getRemoteCopyIntent(clipData);
+        Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
         // Only show remote copy if it's available.
         PackageManager packageManager = mContext.getPackageManager();
         if (packageManager.resolveActivity(
@@ -500,41 +497,19 @@
 
     private void editImage(Uri uri) {
         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
-        String editorPackage = mContext.getString(R.string.config_screenshotEditor);
-        Intent editIntent = new Intent(Intent.ACTION_EDIT);
-        if (!TextUtils.isEmpty(editorPackage)) {
-            editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
-        }
-        editIntent.setDataAndType(uri, "image/*");
-        editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        editIntent.putExtra(EXTRA_EDIT_SOURCE_CLIPBOARD, true);
-        mContext.startActivity(editIntent);
+        mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
         animateOut();
     }
 
     private void editText() {
         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
-        Intent editIntent = new Intent(mContext, EditTextActivity.class);
-        editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        mContext.startActivity(editIntent);
+        mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
         animateOut();
     }
 
     private void shareContent(ClipData clip) {
         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
-        Intent shareIntent = new Intent(Intent.ACTION_SEND);
-        shareIntent.setDataAndType(
-                clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
-        shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
-        if (clip.getItemAt(0).getUri() != null) {
-            shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
-            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        }
-        Intent chooserIntent = Intent.createChooser(shareIntent, null)
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
-                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        mContext.startActivity(chooserIntent);
+        mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
         animateOut();
     }
 
@@ -667,20 +642,6 @@
                 mContext.getString(R.string.clipboard_edit), null);
     }
 
-    private Intent getRemoteCopyIntent(ClipData clipData) {
-        Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
-
-        String remoteCopyPackage = mContext.getString(R.string.config_remoteCopyPackage);
-        if (!TextUtils.isEmpty(remoteCopyPackage)) {
-            nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
-        }
-
-        nearbyIntent.setClipData(clipData);
-        nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        return nearbyIntent;
-    }
-
     private void animateIn() {
         if (mAccessibilityManager.isEnabled()) {
             mDismissButton.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
new file mode 100644
index 0000000..3d5e601
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.systemui.R;
+
+class IntentCreator {
+    private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
+    private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
+
+    static Intent getTextEditorIntent(Context context) {
+        Intent intent = new Intent(context, EditTextActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        return intent;
+    }
+
+    static Intent getShareIntent(ClipData clipData, Context context) {
+        Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+        // From the ACTION_SEND docs:
+        //   "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
+        //    MIME type of the data in EXTRA_STREAM"
+        if (clipData.getItemAt(0).getUri() != null) {
+            shareIntent.setDataAndType(
+                    clipData.getItemAt(0).getUri(), clipData.getDescription().getMimeType(0));
+            shareIntent.putExtra(Intent.EXTRA_STREAM, clipData.getItemAt(0).getUri());
+            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        } else {
+            shareIntent.putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context));
+            shareIntent.setType("text/plain");
+        }
+        Intent chooserIntent = Intent.createChooser(shareIntent, null)
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+        return chooserIntent;
+    }
+
+    static Intent getImageEditIntent(Uri uri, Context context) {
+        String editorPackage = context.getString(R.string.config_screenshotEditor);
+        Intent editIntent = new Intent(Intent.ACTION_EDIT);
+        if (!TextUtils.isEmpty(editorPackage)) {
+            editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+        }
+        editIntent.setDataAndType(uri, "image/*");
+        editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        editIntent.putExtra(EXTRA_EDIT_SOURCE_CLIPBOARD, true);
+        return editIntent;
+    }
+
+    static Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+        Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
+
+        String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
+        if (!TextUtils.isEmpty(remoteCopyPackage)) {
+            nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
+        }
+
+        nearbyIntent.setClipData(clipData);
+        nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        return nearbyIntent;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 318529b..0469152 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -30,7 +30,7 @@
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
 import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
-import com.android.systemui.biometrics.UdfpsHbmProvider;
+import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
 import com.android.systemui.biometrics.dagger.BiometricsModule;
 import com.android.systemui.classifier.FalsingModule;
 import com.android.systemui.controls.dagger.ControlsModule;
@@ -197,7 +197,7 @@
     abstract CentralSurfaces optionalCentralSurfaces();
 
     @BindsOptionalOf
-    abstract UdfpsHbmProvider optionalUdfpsHbmProvider();
+    abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider();
 
     @BindsOptionalOf
     abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 57bea67..6e4c858 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -24,6 +24,7 @@
 import android.content.ComponentName;
 import android.content.res.Configuration;
 import android.metrics.LogMaker;
+import android.util.Log;
 import android.view.View;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -61,6 +62,7 @@
  */
 public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewController<T>
         implements Dumpable{
+    private static final String TAG = "QSPanelControllerBase";
     protected final QSTileHost mHost;
     private final QSCustomizerController mQsCustomizerController;
     private final boolean mUsingMediaPlayer;
@@ -90,6 +92,13 @@
                 public void onConfigurationChange(Configuration newConfig) {
                     mShouldUseSplitNotificationShade =
                             LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+                    // Logging to aid the investigation of b/216244185.
+                    Log.d(TAG,
+                            "onConfigurationChange: "
+                                    + "mShouldUseSplitNotificationShade="
+                                    + mShouldUseSplitNotificationShade + ", "
+                                    + "newConfig.windowConfiguration="
+                                    + newConfig.windowConfiguration);
                     mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
                             mView.getDumpableTag());
                     onConfigurationChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 73b0896..a747926 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -43,11 +43,12 @@
 
 import javax.inject.Inject;
 
-/** Quick settings tile: Control flashlight **/
+/**
+ * Quick settings tile: Control flashlight
+ **/
 public class FlashlightTile extends QSTileImpl<BooleanState> implements
         FlashlightController.FlashlightListener {
 
-    private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_flashlight);
     private final FlashlightController mFlashlightController;
 
     @Inject
@@ -116,19 +117,15 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        if (state.slash == null) {
-            state.slash = new SlashState();
-        }
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         state.secondaryLabel = "";
         state.stateDescription = "";
         if (!mFlashlightController.isAvailable()) {
-            state.icon = mIcon;
-            state.slash.isSlashed = true;
             state.secondaryLabel = mContext.getString(
                     R.string.quick_settings_flashlight_camera_in_use);
             state.stateDescription = state.secondaryLabel;
             state.state = Tile.STATE_UNAVAILABLE;
+            state.icon = ResourceIcon.get(R.drawable.qs_flashlight_icon_off);
             return;
         }
         if (arg instanceof Boolean) {
@@ -140,11 +137,11 @@
         } else {
             state.value = mFlashlightController.isEnabled();
         }
-        state.icon = mIcon;
-        state.slash.isSlashed = !state.value;
         state.contentDescription = mContext.getString(R.string.quick_settings_flashlight_label);
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+        state.icon = ResourceIcon.get(state.value
+                ? R.drawable.qs_flashlight_icon_on : R.drawable.qs_flashlight_icon_off);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 75fb393..f63f044 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -120,7 +120,9 @@
         state.value = isRecording || isStarting;
         state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.label = mContext.getString(R.string.quick_settings_screen_record_label);
-        state.icon = ResourceIcon.get(R.drawable.ic_screenrecord);
+        state.icon = ResourceIcon.get(state.value
+                ? R.drawable.qs_screen_record_icon_on
+                : R.drawable.qs_screen_record_icon_off);
         // Show expand icon when clicking will open a dialog
         state.forceExpandIcon = state.state == Tile.STATE_INACTIVE;
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 0275152..ae980f5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -328,8 +328,7 @@
     @After
     public void tearDown() {
         mMockitoSession.finishMocking();
-        mKeyguardUpdateMonitor.removeCallback(mTestCallback);
-        mKeyguardUpdateMonitor.destroy();
+        cleanupKeyguardUpdateMonitor();
     }
 
     @Test
@@ -351,6 +350,7 @@
 
     @Test
     public void testSimStateInitialized() {
+        cleanupKeyguardUpdateMonitor();
         final int subId = 3;
         final int state = TelephonyManager.SIM_STATE_ABSENT;
 
@@ -1205,7 +1205,9 @@
 
     @Test
     public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
-        mFaceManager = null;
+        cleanupKeyguardUpdateMonitor();
+        mSpiedContext.addMockSystemService(FaceManager.class, null);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
 
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
@@ -1258,6 +1260,7 @@
 
     @Test
     public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+        cleanupKeyguardUpdateMonitor();
         // This disables face auth
         when(mUserManager.isPrimaryUser()).thenReturn(false);
         mKeyguardUpdateMonitor =
@@ -1587,9 +1590,9 @@
 
     @Test
     public void testFingerAcquired_wakesUpPowerManager() {
-        mContext.getOrCreateTestableResources().addOverride(
+        cleanupKeyguardUpdateMonitor();
+        mSpiedContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.kg_wake_on_acquire_start, true);
-        mSpiedContext = spy(mContext);
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
         fingerprintAcquireStart();
 
@@ -1598,15 +1601,23 @@
 
     @Test
     public void testFingerAcquired_doesNotWakeUpPowerManager() {
-        mContext.getOrCreateTestableResources().addOverride(
+        cleanupKeyguardUpdateMonitor();
+        mSpiedContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.kg_wake_on_acquire_start, false);
-        mSpiedContext = spy(mContext);
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
         fingerprintAcquireStart();
 
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
     }
 
+    private void cleanupKeyguardUpdateMonitor() {
+        if (mKeyguardUpdateMonitor != null) {
+            mKeyguardUpdateMonitor.removeCallback(mTestCallback);
+            mKeyguardUpdateMonitor.destroy();
+            mKeyguardUpdateMonitor = null;
+        }
+    }
+
     private void faceAuthLockedOut() {
         mKeyguardUpdateMonitor.mFaceAuthenticationCallback
                 .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
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 44ef922..37bb0c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Point
 import android.hardware.biometrics.BiometricSourceType
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
@@ -76,6 +77,7 @@
     @Mock private lateinit var udfpsController: UdfpsController
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var lightRevealScrim: LightRevealScrim
+    @Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
 
     @Before
     fun setUp() {
@@ -86,6 +88,7 @@
                 .startMocking()
 
         `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
+        `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp))
         `when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
 
         controller = AuthRippleController(
@@ -132,7 +135,7 @@
             false /* isStrongBiometric */)
 
         // THEN update sensor location and show ripple
-        verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
+        verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
         verify(rippleView).startUnlockedRipple(any())
     }
 
@@ -155,7 +158,7 @@
                 false /* isStrongBiometric */)
 
         // THEN update sensor location and show ripple
-        verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
+        verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
         verify(rippleView).startUnlockedRipple(any())
     }
 
@@ -342,4 +345,23 @@
         captor.value.onUiModeChanged()
         verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
     }
+
+    @Test
+    fun testUdfps_onFingerDown_showDwellRipple() {
+        // GIVEN view is already attached
+        controller.onViewAttached()
+        val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
+        verify(udfpsController).addCallback(captor.capture())
+
+        // GIVEN fp is updated to Point(5, 5)
+        val fpsLocation = Point(5, 5)
+        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
+
+        // WHEN finger is down
+        captor.value.onFingerDown()
+
+        // THEN update sensor location and show ripple
+        verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
+        verify(rippleView).startDwellRipple(false)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index cb8358d..5c564e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -17,23 +17,13 @@
 package com.android.systemui.biometrics
 
 import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
-import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
+import android.hardware.biometrics.BiometricOverlayConstants.*
 import android.hardware.fingerprint.FingerprintManager
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
-import android.view.LayoutInflater
-import android.view.MotionEvent
-import android.view.View
-import android.view.Surface
+import android.view.*
 import android.view.Surface.Rotation
-import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -65,7 +55,6 @@
 import org.mockito.junit.MockitoJUnit
 import org.mockito.Mockito.`when` as whenever
 
-private const val HAL_CONTROLS_ILLUMINATION = true
 private const val REQUEST_ID = 2L
 
 // Dimensions for the current display resolution.
@@ -95,8 +84,9 @@
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var systemClock: SystemClock
     @Mock private lateinit var keyguardStateController: KeyguardStateController
-    @Mock private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
-    @Mock private lateinit var hbmProvider: UdfpsHbmProvider
+    @Mock private lateinit var unlockedScreenOffAnimationController:
+            UnlockedScreenOffAnimationController
+    @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
     @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
     @Mock private lateinit var udfpsController: UdfpsController
     @Mock private lateinit var udfpsView: UdfpsView
@@ -130,8 +120,9 @@
             statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager,
             keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
             configurationController, systemClock, keyguardStateController,
-            unlockedScreenOffAnimationController, HAL_CONTROLS_ILLUMINATION, hbmProvider,
-            REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator)
+            unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
+            controllerCallback, onTouch, activityLaunchAnimator
+        )
         block()
     }
 
@@ -246,7 +237,7 @@
         val didShow = controllerOverlay.show(udfpsController, overlayParams)
 
         verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
-        verify(udfpsView).setHbmProvider(eq(hbmProvider))
+        verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
         verify(udfpsView).animationViewController = any()
         verify(udfpsView).addView(any())
 
@@ -351,12 +342,12 @@
     }
 
     @Test
-    fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) {
-        whenever(udfpsView.isIlluminationRequested).thenReturn(true)
+    fun unconfigureDisplayOnHide() = withReason(REASON_AUTH_BP) {
+        whenever(udfpsView.isDisplayConfigured).thenReturn(true)
 
         controllerOverlay.show(udfpsController, overlayParams)
         controllerOverlay.hide()
-        verify(udfpsView).stopIllumination()
+        verify(udfpsView).unconfigureDisplay()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 09dc8e4..08b1c28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -125,7 +125,7 @@
     @Mock
     private WindowManager mWindowManager;
     @Mock
-    private UdfpsHbmProvider mHbmProvider;
+    private UdfpsDisplayModeProvider mDisplayModeProvider;
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
@@ -193,7 +193,7 @@
     private IUdfpsOverlayController mOverlayController;
     @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
     @Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
-    @Captor private ArgumentCaptor<Runnable> mOnIlluminatedRunnableCaptor;
+    @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
     @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
     private ScreenLifecycle.Observer mScreenObserver;
 
@@ -256,7 +256,7 @@
                 mVibrator,
                 mUdfpsHapticsSimulator,
                 mUdfpsShell,
-                Optional.of(mHbmProvider),
+                Optional.of(mDisplayModeProvider),
                 mKeyguardStateController,
                 mDisplayManager,
                 mHandler,
@@ -506,7 +506,7 @@
         final float expectedMajor = touchMajor / scaleFactor;
 
         // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
         when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
 
         // Show the overlay.
@@ -584,7 +584,7 @@
     @Test
     public void fingerDown() throws RemoteException {
         // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
         when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
 
@@ -611,12 +611,12 @@
         verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
                 anyFloat(), anyFloat());
         verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-        // AND illumination begins
-        verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
+        // AND display configuration begins
+        verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
         verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
         verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
-        // AND onIlluminatedRunnable notifies FingerprintManager about onUiReady
-        mOnIlluminatedRunnableCaptor.getValue().run();
+        // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+        mOnDisplayConfiguredCaptor.getValue().run();
         mBiometricsExecutor.runAllReady();
         InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
         inOrder.verify(mAlternateTouchProvider).onUiReady();
@@ -634,10 +634,10 @@
         // WHEN fingerprint is requested because of AOD interrupt
         mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
         mFgExecutor.runAllReady();
-        // THEN illumination begins
-        // AND onIlluminatedRunnable that notifies FingerprintManager is set
-        verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
-        mOnIlluminatedRunnableCaptor.getValue().run();
+        // THEN display configuration begins
+        // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+        verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
+        mOnDisplayConfiguredCaptor.getValue().run();
         mBiometricsExecutor.runAllReady();
         verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
                 eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
@@ -655,11 +655,11 @@
         mFgExecutor.runAllReady();
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
         // WHEN it is cancelled
         mUdfpsController.onCancelUdfps();
-        // THEN the illumination is hidden
-        verify(mUdfpsView).stopIllumination();
+        // THEN the display is unconfigured
+        verify(mUdfpsView).unconfigureDisplay();
     }
 
     @Test
@@ -672,12 +672,12 @@
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
         mFgExecutor.runAllReady();
-        when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
         // WHEN it times out
         mFgExecutor.advanceClockToNext();
         mFgExecutor.runAllReady();
-        // THEN the illumination is hidden
-        verify(mUdfpsView).stopIllumination();
+        // THEN the display is unconfigured
+        verify(mUdfpsView).unconfigureDisplay();
     }
 
     @Test
@@ -692,8 +692,8 @@
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
 
-        // THEN no illumination because screen is off
-        verify(mUdfpsView, never()).startIllumination(any());
+        // THEN display doesn't get configured because it's off
+        verify(mUdfpsView, never()).configureDisplay(any());
     }
 
     @Test
@@ -709,14 +709,14 @@
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
 
-        // THEN no illumination because screen is off
-        verify(mUdfpsView, never()).startIllumination(any());
+        // THEN display doesn't get configured because it's off
+        verify(mUdfpsView, never()).configureDisplay(any());
     }
 
     @Test
     public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
         // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
         when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
 
         // GIVEN that the overlay is showing and a11y touch exploration enabled
@@ -751,7 +751,7 @@
     @Test
     public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
         // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
         when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
 
         // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 0327cfc..b78c063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -36,13 +36,12 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.nullable
 import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
 
 private const val SENSOR_X = 50
 private const val SENSOR_Y = 250
@@ -57,7 +56,7 @@
     var rule = MockitoJUnit.rule()
 
     @Mock
-    lateinit var hbmProvider: UdfpsHbmProvider
+    lateinit var hbmProvider: UdfpsDisplayModeProvider
     @Mock
     lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
 
@@ -66,13 +65,11 @@
     @Before
     fun setup() {
         context.setTheme(R.style.Theme_AppCompat)
-        context.orCreateTestableResources.addOverride(
-            com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0)
         view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
         view.animationViewController = animationViewController
         val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
         view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
-        view.setHbmProvider(hbmProvider)
+        view.setUdfpsDisplayModeProvider(hbmProvider)
         ViewUtils.attachView(view)
     }
 
@@ -143,27 +140,27 @@
     @Test
     fun startAndStopIllumination() {
         val onDone: Runnable = mock()
-        view.startIllumination(onDone)
+        view.configureDisplay(onDone)
 
         val illuminator = withArgCaptor<Runnable> {
-            verify(hbmProvider).enableHbm(anyBoolean(), capture())
+            verify(hbmProvider).enable(capture())
         }
 
-        assertThat(view.isIlluminationRequested).isTrue()
-        verify(animationViewController).onIlluminationStarting()
-        verify(animationViewController, never()).onIlluminationStopped()
+        assertThat(view.isDisplayConfigured).isTrue()
+        verify(animationViewController).onDisplayConfiguring()
+        verify(animationViewController, never()).onDisplayUnconfigured()
         verify(onDone, never()).run()
 
         // fake illumination event
         illuminator.run()
         waitForLooper()
         verify(onDone).run()
-        verify(hbmProvider, never()).disableHbm(any())
+        verify(hbmProvider, never()).disable(any())
 
-        view.stopIllumination()
-        assertThat(view.isIlluminationRequested).isFalse()
-        verify(animationViewController).onIlluminationStopped()
-        verify(hbmProvider).disableHbm(nullable(Runnable::class.java))
+        view.unconfigureDisplay()
+        assertThat(view.isDisplayConfigured).isFalse()
+        verify(animationViewController).onDisplayUnconfigured()
+        verify(hbmProvider).disable(nullable(Runnable::class.java))
     }
 
     private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
new file mode 100644
index 0000000..08fe7c4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentCreatorTest extends SysuiTestCase {
+    private static final int EXTERNAL_INTENT_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK
+            | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION;
+
+    @Test
+    public void test_getTextEditorIntent() {
+        Intent intent = IntentCreator.getTextEditorIntent(getContext());
+        assertEquals(new ComponentName(getContext(), EditTextActivity.class),
+                intent.getComponent());
+        assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    }
+
+    @Test
+    public void test_getRemoteCopyIntent() {
+        getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
+                "");
+
+        ClipData clipData = ClipData.newPlainText("Test", "Test Item");
+        Intent intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+
+        assertEquals(null, intent.getComponent());
+        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+        assertEquals(clipData, intent.getClipData());
+
+        // Try again with a remote copy component
+        ComponentName fakeComponent = new ComponentName("com.android.remotecopy",
+                "com.android.remotecopy.RemoteCopyActivity");
+        getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
+                fakeComponent.flattenToString());
+
+        intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+        assertEquals(fakeComponent, intent.getComponent());
+    }
+
+    @Test
+    public void test_getImageEditIntent() {
+        getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
+                "");
+        Uri fakeUri = Uri.parse("content://foo");
+        Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+
+        assertEquals(Intent.ACTION_EDIT, intent.getAction());
+        assertEquals("image/*", intent.getType());
+        assertEquals(null, intent.getComponent());
+        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+
+        // try again with an editor component
+        ComponentName fakeComponent = new ComponentName("com.android.remotecopy",
+                "com.android.remotecopy.RemoteCopyActivity");
+        getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
+                fakeComponent.flattenToString());
+        intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+        assertEquals(fakeComponent, intent.getComponent());
+    }
+
+    @Test
+    public void test_getShareIntent_plaintext() {
+        ClipData clipData = ClipData.newPlainText("Test", "Test Item");
+        Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+        Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+        assertEquals("Test Item", target.getStringExtra(Intent.EXTRA_TEXT));
+        assertEquals("text/plain", target.getType());
+    }
+
+    @Test
+    public void test_getShareIntent_html() {
+        ClipData clipData = ClipData.newHtmlText("Test", "Some HTML",
+                "<b>Some HTML</b>");
+        Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+        Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+        assertEquals("Some HTML", target.getStringExtra(Intent.EXTRA_TEXT));
+        assertEquals("text/plain", target.getType());
+    }
+
+    @Test
+    public void test_getShareIntent_image() {
+        Uri uri = Uri.parse("content://something");
+        ClipData clipData = new ClipData("Test", new String[]{"image/png"},
+                new ClipData.Item(uri));
+        Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+        assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+        assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+        Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+        assertEquals(uri, target.getData());
+        assertEquals("image/png", target.getType());
+    }
+
+    // Assert that the given flags are set
+    private void assertFlags(Intent intent, int flags) {
+        assertTrue((intent.getFlags() & flags) == flags);
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
new file mode 100644
index 0000000..d0f851b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -0,0 +1,108 @@
+package com.android.systemui.qs.tiles
+
+import android.content.Context
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.FlashlightController
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class FlashlightTileTest : SysuiTestCase() {
+
+    @Mock private lateinit var mockContext: Context
+
+    @Mock private lateinit var qsLogger: QSLogger
+
+    @Mock private lateinit var qsHost: QSTileHost
+
+    @Mock private lateinit var metricsLogger: MetricsLogger
+
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
+
+    @Mock private lateinit var activityStarter: ActivityStarter
+
+    @Mock private lateinit var flashlightController: FlashlightController
+
+    private val uiEventLogger = UiEventLoggerFake()
+    private val falsingManager = FalsingManagerFake()
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var tile: FlashlightTile
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+
+        Mockito.`when`(qsHost.context).thenReturn(mockContext)
+        Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+        tile =
+            FlashlightTile(
+                qsHost,
+                testableLooper.looper,
+                Handler(testableLooper.looper),
+                falsingManager,
+                metricsLogger,
+                statusBarStateController,
+                activityStarter,
+                qsLogger,
+                flashlightController
+            )
+    }
+
+    @Test
+    fun testIcon_whenFlashlightEnabled_isOnState() {
+        Mockito.`when`(flashlightController.isAvailable).thenReturn(true)
+        Mockito.`when`(flashlightController.isEnabled).thenReturn(true)
+        val state = QSTile.BooleanState()
+
+        tile.handleUpdateState(state, /* arg= */ null)
+
+        Truth.assertThat(state.icon)
+            .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_on))
+    }
+
+    @Test
+    fun testIcon_whenFlashlightDisabled_isOffState() {
+        Mockito.`when`(flashlightController.isAvailable).thenReturn(true)
+        Mockito.`when`(flashlightController.isEnabled).thenReturn(false)
+        val state = QSTile.BooleanState()
+
+        tile.handleUpdateState(state, /* arg= */ null)
+
+        Truth.assertThat(state.icon)
+            .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+    }
+
+    @Test
+    fun testIcon_whenFlashlightUnavailable_isOffState() {
+        Mockito.`when`(flashlightController.isAvailable).thenReturn(false)
+        val state = QSTile.BooleanState()
+
+        tile.handleUpdateState(state, /* arg= */ null)
+
+        Truth.assertThat(state.icon)
+            .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index e6bd396..30debdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -41,9 +41,11 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -229,4 +231,38 @@
 
         assertFalse(mTile.getState().forceExpandIcon);
     }
+
+    @Test
+    public void testIcon_whenRecording_isOnState() {
+        when(mController.isStarting()).thenReturn(false);
+        when(mController.isRecording()).thenReturn(true);
+        QSTile.BooleanState state = new QSTile.BooleanState();
+
+        mTile.handleUpdateState(state, /* arg= */ null);
+
+        assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+    }
+
+    @Test
+    public void testIcon_whenStarting_isOnState() {
+        when(mController.isStarting()).thenReturn(true);
+        when(mController.isRecording()).thenReturn(false);
+        QSTile.BooleanState state = new QSTile.BooleanState();
+
+        mTile.handleUpdateState(state, /* arg= */ null);
+
+        assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+    }
+
+    @Test
+    public void testIcon_whenRecordingOff_isOffState() {
+        when(mController.isStarting()).thenReturn(false);
+        when(mController.isRecording()).thenReturn(false);
+        QSTile.BooleanState state = new QSTile.BooleanState();
+
+        mTile.handleUpdateState(state, /* arg= */ null);
+
+        assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_off));
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 6ce9cff..002ef29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -34,6 +34,7 @@
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.util.ScreenshotHelper
 import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
@@ -65,6 +66,7 @@
 private const val TASK_ID = 1
 
 @RunWith(AndroidTestingRunner::class)
+@SmallTest
 class TakeScreenshotServiceTest : SysuiTestCase() {
 
     private val application = mock<Application>()
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 99c8495..6417db0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1109,7 +1109,8 @@
             final List<AccessibilityServiceInfo> result = new ArrayList<>(serviceCount);
             for (int i = 0; i < serviceCount; ++i) {
                 final AccessibilityServiceConnection service = services.get(i);
-                if ((service.mFeedbackType & feedbackType) != 0) {
+                if ((service.mFeedbackType & feedbackType) != 0
+                        || feedbackType == AccessibilityServiceInfo.FEEDBACK_ALL_MASK) {
                     result.add(service.getServiceInfo());
                 }
             }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index b56654f..a71f51a 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4593,9 +4593,8 @@
                 final PackageManager pm = mContext.getPackageManager();
                 final String supplementalPackageName = pm.getSdkSandboxPackageName();
                 if (Objects.equals(packageName, supplementalPackageName)) {
-                    int supplementalAppId = pm.getPackageUid(supplementalPackageName,
-                            PackageManager.PackageInfoFlags.of(0));
-                    uid = UserHandle.getUid(UserHandle.getUserId(uid), supplementalAppId);
+                    uid = pm.getPackageUidAsUser(supplementalPackageName,
+                            PackageManager.PackageInfoFlags.of(0), UserHandle.getUserId(uid));
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 // Shouldn't happen for the supplemental package
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 27550d9..3d00686 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -273,8 +273,8 @@
         @Override
         public boolean onDoubleTapEvent(MotionEvent e) {
             if (e.getAction() == MotionEvent.ACTION_UP) {
-                mDoubleTapCallbackX.accept((int) e.getX());
-                mDoubleTapCallbackY.accept((int) e.getY());
+                mDoubleTapCallbackX.accept((int) e.getRawX());
+                mDoubleTapCallbackY.accept((int) e.getRawY());
                 return true;
             }
             return false;
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index e7d95e4..58974ee 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.ParcelUuid;
+import android.provider.DeviceConfig;
 
 import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
@@ -57,6 +58,9 @@
 public final class AnomalyReporter {
     private static final String TAG = "AnomalyReporter";
 
+    private static final String KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED =
+            "is_telephony_anomaly_report_enabled";
+
     private static Context sContext = null;
 
     private static Map<UUID, Integer> sEvents = new ConcurrentHashMap<>();
@@ -101,6 +105,12 @@
      * @param carrierId the carrier of the id associated with this event.
      */
     public static void reportAnomaly(@NonNull UUID eventId, String description, int carrierId) {
+        // Don't report if the server-side flag isn't loaded, as it implies other anomaly report
+        // related config hasn't loaded.
+        boolean isAnomalyReportEnabledFromServer = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TELEPHONY, KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED, false);
+        if (!isAnomalyReportEnabledFromServer) return;
+
         if (sContext == null) {
             Rlog.w(TAG, "AnomalyReporter not yet initialized, dropping event=" + eventId);
             return;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d07d809..7740845 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3426,10 +3426,20 @@
      * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
      * See {@link #createSubscriptionGroup(List)} for more details.
      *
-     * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
-     * permission or had carrier privilege permission on the subscription.
+     * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE}
+     * or carrier privilege permission on the subscription.
      * {@link TelephonyManager#hasCarrierPrivileges()}
      *
+     * <p>Starting with API level 33, this method will return an empty List if the caller does
+     * not have access to device identifiers.
+     * This method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the app has carrier privilege permission.
+     *     {@link TelephonyManager#hasCarrierPrivileges()}
+     *     <li>If the app has {@link android.Manifest.permission#READ_PHONE_STATE} permission and
+     *     access to device identifiers.
+     * </ul>
+     *
      * @throws IllegalStateException if Telephony service is in bad state.
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b6f8652..c79fe14 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -190,6 +190,9 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long CALLBACK_ON_MORE_ERROR_CODE_CHANGE = 130595455L;
 
+    // Null IMEI anomaly uuid
+    private static final UUID IMEI_ANOMALY_UUID = UUID.fromString(
+            "83905f14-6455-450c-be29-8206f0427fe9");
     /**
      * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)}
      * into the ResultReceiver Bundle.
@@ -2132,7 +2135,11 @@
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     public String getImei() {
-        return getImei(getSlotIndex());
+        String imei = getImei(getSlotIndex());
+        if (imei == null) {
+            AnomalyReporter.reportAnomaly(IMEI_ANOMALY_UUID, "getImei: IMEI is null.");
+        }
+        return imei;
     }
 
     /**
@@ -2175,7 +2182,10 @@
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     public String getImei(int slotIndex) {
         ITelephony telephony = getITelephony();
-        if (telephony == null) return null;
+        if (telephony == null) {
+            AnomalyReporter.reportAnomaly(IMEI_ANOMALY_UUID, "getImei: telephony is null");
+            return null;
+        }
 
         try {
             return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getAttributionTag());