Merge "Add Gainmap bitmap & imagedecoder"
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index a976de3..2ab8ac0 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -58,6 +58,19 @@
             + "180℃で揚げ、衣がきつね色になったら引き上げて、油を切る。"
             + "盛り付けて出来上がり。";
 
+    public static final String KO_TEXT_SHORT = "모든 인류 구성원의 천부의 존엄성과 동등하고 양도할 수 없는 권리를";
+    // About 530 chars
+    public static final String KO_TEXT_LONG = "모든 인류 구성원의 천부의 존엄성과 동등하고 양도할 수 없는 권리를"
+            + " 인정하는 것이 세계의 자유, 정의 및 평화의 기초이며, 인권에 대한 무시와 경멸이 인류의 양심을 격분시키는"
+            + " 만행을 초래하였으며, 인간이 언론과 신앙의 자유, 그리고 공포와 결핍으로부터의 자유를 누릴 수 있는 세계의"
+            + " 도래가 모든 사람들의 지고한 열망으로서 천명되어 왔으며, 인간이 폭정과 억압에 대항하는 마지막 수단으로서"
+            + " 반란을 일으키도록 강요받지 않으려면, 법에 의한 통치에 의하여 인권이 보호되어야 하는 것이 필수적이며,"
+            + "국가간에 우호관계의 발전을 증진하는 것이 필수적이며, 국제연합의 모든 사람들은 그 헌장에서 기본적 인권,"
+            + " 인간의 존엄과 가치, 그리고 남녀의 동등한 권리에 대한 신념을 재확인하였으며, 보다 폭넓은 "
+            + "자유속에서 사회적 진보와 보다 나은 생활수준을 증진하기로 다짐하였고, 회원국들은 국제연합과 협력하여 인권과"
+            + " 기본적 자유의 보편적 존중과 준수를 증진할 것을 스스로 서약하였으며, 이러한 권리와 자유에 대한 공통의"
+            + " 이해가 이 서약의 완전한 이행을 위하여 가장 중요하므로,";
+
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
@@ -560,4 +573,118 @@
                     .build();
         }
     }
+
+    @Test
+    public void testCreate_KOText_Phrase_Short() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final String text = KO_TEXT_SHORT;
+        final LineBreakConfig config = new LineBreakConfig.Builder()
+                .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+                .build();
+        final TextPaint paint = new TextPaint(PAINT);
+        paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+            StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+                    .setLineBreakConfig(config)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_KOText_Phrase_Long() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final String text = KO_TEXT_LONG;
+        final LineBreakConfig config = new LineBreakConfig.Builder()
+                .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+                .build();
+        final TextPaint paint = new TextPaint(PAINT);
+        paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+            StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+                    .setLineBreakConfig(config)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_KOText_Phrase_LongLong() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final String text = KO_TEXT_LONG.repeat(20);  // 250 * 20 = 7000 chars
+        final LineBreakConfig config = new LineBreakConfig.Builder()
+                .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+                .build();
+        final TextPaint paint = new TextPaint(PAINT);
+        paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+            StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+                    .setLineBreakConfig(config)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_KOText_NoPhrase_Short() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final String text = KO_TEXT_SHORT;
+        final LineBreakConfig config = new LineBreakConfig.Builder()
+                .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+                .build();
+        final TextPaint paint = new TextPaint(PAINT);
+        paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+            StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+                    .setLineBreakConfig(config)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_KOText_NoPhrase_Long() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final String text = KO_TEXT_LONG;
+        final LineBreakConfig config = new LineBreakConfig.Builder()
+                .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+                .build();
+        final TextPaint paint = new TextPaint(PAINT);
+        paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+            StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+                    .setLineBreakConfig(config)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_KOText_NoPhrase_LongLong() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final String text = KO_TEXT_LONG.repeat(20);  // 520 * 20 = 10400 chars
+        final LineBreakConfig config = new LineBreakConfig.Builder()
+                .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+                .build();
+        final TextPaint paint = new TextPaint(PAINT);
+        paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+            StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+                    .setLineBreakConfig(config)
+                    .build();
+        }
+    }
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index 978713a..2b1616d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -64,6 +64,8 @@
     field public static final String BLUETOOTH_SCAN = "android.permission.BLUETOOTH_SCAN";
     field public static final String BODY_SENSORS = "android.permission.BODY_SENSORS";
     field public static final String BODY_SENSORS_BACKGROUND = "android.permission.BODY_SENSORS_BACKGROUND";
+    field public static final String BODY_SENSORS_WRIST_TEMPERATURE = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE";
+    field public static final String BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND";
     field public static final String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED";
     field public static final String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
     field public static final String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY";
@@ -4845,6 +4847,7 @@
     field public static final String OPSTR_ADD_VOICEMAIL = "android:add_voicemail";
     field public static final String OPSTR_ANSWER_PHONE_CALLS = "android:answer_phone_calls";
     field public static final String OPSTR_BODY_SENSORS = "android:body_sensors";
+    field public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE = "android:body_sensors_wrist_temperature";
     field public static final String OPSTR_CALL_PHONE = "android:call_phone";
     field public static final String OPSTR_CAMERA = "android:camera";
     field public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -6736,6 +6739,7 @@
     method public static void writePendingIntentOrNullToParcel(@Nullable android.app.PendingIntent, @NonNull android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
+    field public static final int FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT = 16777216; // 0x1000000
     field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
     field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
     field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
@@ -12738,7 +12742,7 @@
     field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40
     field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
     field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
-    field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
+    field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
     field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
     field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
     field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
@@ -13343,10 +13347,22 @@
     field @NonNull public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
   }
 
+  public final class CredentialDescription implements android.os.Parcelable {
+    ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
+    method public int describeContents();
+    method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
+    method @NonNull public String getFlattenedRequestString();
+    method @NonNull public String getType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
+  }
+
   public final class CredentialManager {
     method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>);
     method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
     method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
+    method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest);
+    method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest);
   }
 
   public class GetCredentialException extends java.lang.Exception {
@@ -13370,6 +13386,7 @@
     method public boolean isSystemProviderRequired();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialOption> CREATOR;
+    field public static final String FLATTENED_REQUEST = "android.credentials.GetCredentialOption.FLATTENED_REQUEST_STRING";
   }
 
   public final class GetCredentialRequest implements android.os.Parcelable {
@@ -13395,6 +13412,24 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialResponse> CREATOR;
   }
 
+  public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
+    ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+    ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.Set<android.credentials.CredentialDescription>);
+    method public int describeContents();
+    method @NonNull public java.util.Set<android.credentials.CredentialDescription> getCredentialDescriptions();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
+  }
+
+  public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
+    ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+    ctor public UnregisterCredentialDescriptionRequest(@NonNull java.util.List<android.credentials.CredentialDescription>);
+    method public int describeContents();
+    method @NonNull public java.util.List<android.credentials.CredentialDescription> getCredentialDescriptions();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
+  }
+
 }
 
 package android.database {
@@ -48637,7 +48672,7 @@
     field public float density;
     field public int densityDpi;
     field public int heightPixels;
-    field public float scaledDensity;
+    field @Deprecated public float scaledDensity;
     field public int widthPixels;
     field public float xdpi;
     field public float ydpi;
@@ -49551,6 +49586,7 @@
     field public static final int HDR_TYPE_HDR10 = 2; // 0x2
     field public static final int HDR_TYPE_HDR10_PLUS = 4; // 0x4
     field public static final int HDR_TYPE_HLG = 3; // 0x3
+    field public static final int HDR_TYPE_INVALID = -1; // 0xffffffff
     field public static final float INVALID_LUMINANCE = -1.0f;
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b6232cd..5f2f623 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -30,6 +30,7 @@
     field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
+    field public static final String MODIFY_HDR_CONVERSION_MODE = "android.permission.MODIFY_HDR_CONVERSION_MODE";
     field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
     field public static final String MODIFY_USER_PREFERRED_DISPLAY_MODE = "android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE";
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
@@ -132,6 +133,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int);
     method public void holdLock(android.os.IBinder, int);
     method public static boolean isHighEndGfx();
+    method public void notifySystemPropertiesChanged();
     method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
     method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
     method public static void resumeAppSwitches() throws android.os.RemoteException;
@@ -983,59 +985,6 @@
 
 }
 
-package android.credentials {
-
-  public final class CredentialDescription implements android.os.Parcelable {
-    ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
-    method public int describeContents();
-    method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
-    method @NonNull public String getFlattenedRequestString();
-    method @NonNull public String getType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
-  }
-
-  public final class CredentialManager {
-    method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.RegisterCredentialDescriptionException>);
-    method public void unRegisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.UnregisterCredentialDescriptionException>);
-  }
-
-  public class RegisterCredentialDescriptionException extends java.lang.Exception {
-    ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String);
-    ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
-    ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
-    ctor public RegisterCredentialDescriptionException(@NonNull String);
-    field @NonNull public final String errorType;
-  }
-
-  public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
-    ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
-    ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.List<android.credentials.CredentialDescription>);
-    method public int describeContents();
-    method @NonNull public java.util.List<android.credentials.CredentialDescription> getCredentialDescriptions();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
-    field public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
-  }
-
-  public class UnregisterCredentialDescriptionException extends java.lang.Exception {
-    ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String);
-    ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
-    ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
-    ctor public UnregisterCredentialDescriptionException(@NonNull String);
-    field @NonNull public final String errorType;
-  }
-
-  public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
-    ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
-    method public int describeContents();
-    method @NonNull public android.credentials.CredentialDescription getCredentialDescription();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
-  }
-
-}
-
 package android.credentials.ui {
 
   public final class CreateCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable {
@@ -1392,11 +1341,14 @@
     method public boolean areUserDisabledHdrTypesAllowed();
     method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearGlobalUserPreferredDisplayMode();
     method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
+    method @NonNull public android.hardware.display.HdrConversionMode getHdrConversionMode();
+    method @NonNull public int[] getSupportedHdrOutputTypes();
     method @NonNull public int[] getUserDisabledHdrTypes();
     method public boolean isMinimalPostProcessingRequested(int);
     method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE) public void setHdrConversionMode(@NonNull android.hardware.display.HdrConversionMode);
     method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
@@ -1409,6 +1361,19 @@
     field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
   }
 
+  public final class HdrConversionMode implements android.os.Parcelable {
+    ctor public HdrConversionMode(int, int);
+    ctor public HdrConversionMode(int);
+    method public int describeContents();
+    method public int getConversionMode();
+    method public int getPreferredHdrOutputType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.HdrConversionMode> CREATOR;
+    field public static final int HDR_CONVERSION_FORCE = 3; // 0x3
+    field public static final int HDR_CONVERSION_PASSTHROUGH = 1; // 0x1
+    field public static final int HDR_CONVERSION_SYSTEM = 2; // 0x2
+  }
+
 }
 
 package android.hardware.fingerprint {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b626493..c4d6ad7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5346,6 +5346,20 @@
     }
 
     /**
+     * Checks if the process represented by the given {@code pid} is frozen.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    public boolean isProcessFrozen(int pid) {
+        try {
+            return getService().isProcessFrozen(pid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @return The reason code of whether or not the given UID should be exempted from background
      * restrictions here.
      *
@@ -5367,6 +5381,31 @@
     }
 
     /**
+     * Notifies {@link #getRunningAppProcesses app processes} that the system properties
+     * have changed.
+     *
+     * @see SystemProperties#addChangeCallback
+     *
+     * @hide
+     */
+    @TestApi
+    public void notifySystemPropertiesChanged() {
+        // Note: this cannot use {@link ServiceManager#listServices()} to notify all the services,
+        // as that is not available from tests.
+        final var binder = ActivityManager.getService().asBinder();
+        if (binder != null) {
+            var data = Parcel.obtain();
+            try {
+                binder.transact(IBinder.SYSPROPS_TRANSACTION, data, null /* reply */,
+                        0 /* flags */);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            data.recycle();
+        }
+    }
+
+    /**
      * A subset of immutable pending intent information suitable for caching on the client side.
      *
      * @hide
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 84320ca..c89a769 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1448,9 +1448,13 @@
     public static final int OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
             AppProtoEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD;
 
+    /** @hide Access to wrist temperature sensors. */
+    public static final int OP_BODY_SENSORS_WRIST_TEMPERATURE =
+            AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 132;
+    public static final int _NUM_OP = 133;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -2030,6 +2034,10 @@
     public static final String OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
             "android:capture_consentless_bugreport_on_userdebug_build";
 
+    /** Access to wrist temperature body sensors. */
+    public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE =
+            "android:body_sensors_wrist_temperature";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2128,6 +2136,7 @@
             OP_READ_MEDIA_VISUAL_USER_SELECTED,
             OP_FOREGROUND_SERVICE_SPECIAL_USE,
             OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
+            OP_BODY_SENSORS_WRIST_TEMPERATURE,
     };
 
     static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2541,7 +2550,12 @@
                 OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
                 "CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD")
                 .setPermission(Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD)
-                .build()
+                .build(),
+        new AppOpInfo.Builder(OP_BODY_SENSORS_WRIST_TEMPERATURE,
+                OPSTR_BODY_SENSORS_WRIST_TEMPERATURE,
+                "BODY_SENSORS_WRIST_TEMPERATURE")
+                .setPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE)
+                .setDefaultMode(AppOpsManager.MODE_ALLOWED).build()
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index c19a865..20d19c1 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -350,6 +350,7 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION),
                 new RegularPermission(Manifest.permission.BODY_SENSORS),
+                new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE),
                 new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
             }, false)
     );
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 0866d94..9dc8ce6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -779,6 +779,10 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
     boolean isModernBroadcastQueueEnabled();
 
+    /** Checks if the process represented by the given pid is frozen. */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
+    boolean isProcessFrozen(int pid);
+
     /**
      * @return The reason code of whether or not the given UID should be exempted from background
      * restrictions here.
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index c58e627..dd44531 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -186,6 +186,7 @@
                     FLAG_IMMUTABLE,
                     FLAG_MUTABLE,
                     FLAG_MUTABLE_UNAUDITED,
+                    FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
 
                     Intent.FILL_IN_ACTION,
                     Intent.FILL_IN_DATA,
@@ -280,6 +281,21 @@
     public static final int FLAG_MUTABLE_UNAUDITED = FLAG_MUTABLE;
 
     /**
+     * Flag indicating that the created PendingIntent with {@link #FLAG_MUTABLE}
+     * is allowed to have an unsafe implicit Intent within. <p>Starting with
+     * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, for apps that
+     * target SDK {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+     * higher, creation of a PendingIntent with {@link #FLAG_MUTABLE} and an
+     * implicit Intent within will throw an {@link IllegalArgumentException}
+     * for security reasons. To bypass this check, use
+     * {@link #FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT} when creating a PendingIntent.
+     * However, it is strongly recommended to not to use this flag and make the
+     * Intent explicit or the PendingIntent immutable, thereby making the Intent
+     * safe.
+     */
+    public static final int FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT = 1<<24;
+
+    /**
      * Exception thrown when trying to send through a PendingIntent that
      * has been canceled or is otherwise no longer able to execute the request.
      */
@@ -418,12 +434,13 @@
         //   This will be changed to a throw of an exception on the server side once we finish
         //   migrating to safer PendingIntents b/262253127.
         // - Otherwise, warn that it will be blocked from target SDK U.
-        if (isNewMutableImplicitPendingIntent(flags, intent)) {
+        if (isNewMutableDisallowedImplicitPendingIntent(flags, intent)) {
             if (Compatibility.isChangeEnabled(BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT)) {
                 String msg = packageName + ": Targeting U+ (version "
                         + Build.VERSION_CODES.UPSIDE_DOWN_CAKE + " and above) disallows"
                         + " creating or retrieving a PendingIntent with FLAG_MUTABLE,"
-                        + " an implicit Intent within and without FLAG_NO_CREATE for"
+                        + " an implicit Intent within and without FLAG_NO_CREATE and"
+                        + " FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT for"
                         + " security reasons. To retrieve an already existing"
                         + " PendingIntent, use FLAG_NO_CREATE, however, to create a"
                         + " new PendingIntent with an implicit Intent use"
@@ -441,11 +458,15 @@
     }
 
     /** @hide */
-    public static boolean isNewMutableImplicitPendingIntent(int flags, @NonNull Intent intent) {
+    public static boolean isNewMutableDisallowedImplicitPendingIntent(int flags,
+            @NonNull Intent intent) {
         boolean isFlagNoCreateSet = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
         boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
         boolean isImplicit = (intent.getComponent() == null) && (intent.getPackage() == null);
-        return !isFlagNoCreateSet && isFlagMutableSet && isImplicit;
+        boolean isFlagAllowUnsafeImplicitIntentSet =
+                (flags & PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT) != 0;
+        return !isFlagNoCreateSet && isFlagMutableSet && isImplicit
+                && !isFlagAllowUnsafeImplicitIntentSet;
     }
 
     /**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5a153ce..e8f0a89 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -339,10 +339,17 @@
                         final ProviderInfo cpi = mContext.getPackageManager()
                                 .resolveContentProvider(uri.getAuthority(),
                                 PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
+                        final int callingUserId = UserHandle.getUserId(callingUid);
+                        final Uri userUri = (mSingleUser
+                                && !UserHandle.isSameUser(mMyUid, callingUid))
+                                ? maybeAddUserId(uri, callingUserId) : uri;
                         if (cpi.forceUriPermissions
                                 && mInterface.checkUriPermission(uri,
                                 callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                                != PermissionChecker.PERMISSION_GRANTED) {
+                                != PermissionChecker.PERMISSION_GRANTED
+                                && getContext().checkUriPermission(userUri, Binder.getCallingPid(),
+                                callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                                != PackageManager.PERMISSION_GRANTED) {
                             FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
                                     enumCheckUriPermission,
                                     callingUid, uri.getAuthority(), type);
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index 1e0deff..37b1778 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.compat.annotation.UnsupportedAppUsage;
+import android.os.BadParcelableException;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
@@ -92,18 +93,20 @@
             data.writeInt(i);
             try {
                 retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+                reply.readException();
+                while (i < N && reply.readInt() != 0) {
+                    listElementClass = readVerifyAndAddElement(creator, reply, loader,
+                            listElementClass);
+                    if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
+                    i++;
+                }
             } catch (RemoteException e) {
-                Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
-                return;
+                throw new BadParcelableException(
+                        "Failure retrieving array; only received " + i + " of " + N, e);
+            } finally {
+                reply.recycle();
+                data.recycle();
             }
-            while (i < N && reply.readInt() != 0) {
-                listElementClass = readVerifyAndAddElement(creator, reply, loader,
-                        listElementClass);
-                if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
-                i++;
-            }
-            reply.recycle();
-            data.recycle();
         }
     }
 
@@ -201,22 +204,29 @@
                                     + Binder.getCallingPid() + ", sender=" + this);
                         }
 
-                        while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
-                            reply.writeInt(1);
+                        try {
+                            reply.writeNoException();
+                            while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+                                reply.writeInt(1);
 
-                            final T parcelable = mList.get(i);
-                            verifySameType(listElementClass, parcelable.getClass());
-                            writeElement(parcelable, reply, callFlags);
+                                final T parcelable = mList.get(i);
+                                verifySameType(listElementClass, parcelable.getClass());
+                                writeElement(parcelable, reply, callFlags);
 
-                            if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
-                            i++;
-                        }
-                        if (i < N) {
-                            if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
-                            reply.writeInt(0);
-                        } else {
-                            if (DEBUG) Log.d(TAG, "Transfer complete, clearing mList reference");
+                                if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+                                i++;
+                            }
+                            if (i < N) {
+                                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
+                                reply.writeInt(0);
+                            } else {
+                                if (DEBUG) Log.d(TAG, "Transfer done, clearing mList reference");
+                                mList = null;
+                            }
+                        } catch (RuntimeException e) {
+                            if (DEBUG) Log.d(TAG, "Transfer failed, clearing mList reference");
                             mList = null;
+                            throw e;
                         }
                         return true;
                     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e8a355f..5209c14 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2400,6 +2400,14 @@
     public static final int DELETE_FAILED_APP_PINNED = -7;
 
     /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * for any child profile with {@link UserProperties#getDeleteAppWithParent()} as true.
+     * @hide
+     */
+    public static final int DELETE_FAILED_FOR_CHILD_PROFILE = -8;
+
+    /**
      * Return code that is passed to the {@link IPackageMoveObserver} when the
      * package has been successfully moved by the system.
      *
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index a408ea6..f3209f9 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -323,6 +323,7 @@
      * permissions:
      * {@link android.Manifest.permission#ACTIVITY_RECOGNITION},
      * {@link android.Manifest.permission#BODY_SENSORS},
+     * {@link android.Manifest.permission#BODY_SENSORS_WRIST_TEMPERATURE},
      * {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}.
      */
     @RequiresPermission(
@@ -332,6 +333,7 @@
             anyOf = {
                 Manifest.permission.ACTIVITY_RECOGNITION,
                 Manifest.permission.BODY_SENSORS,
+                Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE,
                 Manifest.permission.HIGH_SAMPLING_RATE_SENSORS,
             }
     )
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 824d15c..77b1954 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -61,6 +61,7 @@
             "mediaSharedWithParent";
     private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
             "credentialShareableWithParent";
+    private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
 
     /** Index values of each property (to indicate whether they are present in this object). */
     @IntDef(prefix = "INDEX_", value = {
@@ -73,7 +74,8 @@
             INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL,
             INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
             INDEX_MEDIA_SHARED_WITH_PARENT,
-            INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT
+            INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
+            INDEX_DELETE_APP_WITH_PARENT,
     })
     @Retention(RetentionPolicy.SOURCE)
     private @interface PropertyIndex {
@@ -88,6 +90,7 @@
     private static final int INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY = 7;
     private static final int INDEX_MEDIA_SHARED_WITH_PARENT = 8;
     private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
+    private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
     /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
     private long mPropertiesPresent = 0;
 
@@ -312,6 +315,7 @@
             setCrossProfileIntentFilterAccessControl(
                     orig.getCrossProfileIntentFilterAccessControl());
             setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy());
+            setDeleteAppWithParent(orig.getDeleteAppWithParent());
         }
         if (hasManagePermission) {
             // Add items that require MANAGE_USERS or stronger.
@@ -418,6 +422,24 @@
     private boolean mStartWithParent;
 
     /**
+     * Returns whether an app in the profile should be deleted when the same package in
+     * the parent user is being deleted.
+     * This only applies for users that have parents (i.e. for profiles).
+     * @hide
+     */
+    public boolean getDeleteAppWithParent() {
+        if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) return mDeleteAppWithParent;
+        if (mDefaultProperties != null) return mDefaultProperties.mDeleteAppWithParent;
+        throw new SecurityException("You don't have permission to query deleteAppWithParent");
+    }
+    /** @hide */
+    public void setDeleteAppWithParent(boolean val) {
+        this.mDeleteAppWithParent = val;
+        setPresent(INDEX_DELETE_APP_WITH_PARENT);
+    }
+    private boolean mDeleteAppWithParent;
+
+    /**
      * Return whether, and how, select user restrictions or device policies should be inherited
      * from other user.
      *
@@ -609,6 +631,7 @@
                 + getCrossProfileIntentResolutionStrategy()
                 + ", mMediaSharedWithParent=" + isMediaSharedWithParent()
                 + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
+                + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
                 + "}";
     }
 
@@ -634,6 +657,7 @@
         pw.println(prefix + "    mMediaSharedWithParent=" + isMediaSharedWithParent());
         pw.println(prefix + "    mCredentialShareableWithParent="
                 + isCredentialShareableWithParent());
+        pw.println(prefix + "    mDeleteAppWithParent=" + getDeleteAppWithParent());
     }
 
     /**
@@ -697,6 +721,9 @@
                 case ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT:
                     setCredentialShareableWithParent(parser.getAttributeBoolean(i));
                     break;
+                case ATTR_DELETE_APP_WITH_PARENT:
+                    setDeleteAppWithParent(parser.getAttributeBoolean(i));
+                    break;
                 default:
                     Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
             }
@@ -752,6 +779,10 @@
             serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT,
                     mCredentialShareableWithParent);
         }
+        if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) {
+            serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT,
+                    mDeleteAppWithParent);
+        }
     }
 
     // For use only with an object that has already had any permission-lacking fields stripped out.
@@ -768,6 +799,7 @@
         dest.writeInt(mCrossProfileIntentResolutionStrategy);
         dest.writeBoolean(mMediaSharedWithParent);
         dest.writeBoolean(mCredentialShareableWithParent);
+        dest.writeBoolean(mDeleteAppWithParent);
     }
 
     /**
@@ -788,6 +820,7 @@
         mCrossProfileIntentResolutionStrategy = source.readInt();
         mMediaSharedWithParent = source.readBoolean();
         mCredentialShareableWithParent = source.readBoolean();
+        mDeleteAppWithParent = source.readBoolean();
     }
 
     @Override
@@ -825,6 +858,7 @@
                 CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT;
         private boolean mMediaSharedWithParent = false;
         private boolean mCredentialShareableWithParent = false;
+        private boolean mDeleteAppWithParent = false;
 
         public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
             mShowInLauncher = showInLauncher;
@@ -886,6 +920,12 @@
             return this;
         }
 
+        /** Sets the value for {@link #mDeleteAppWithParent}*/
+        public Builder setDeleteAppWithParent(boolean deleteAppWithParent) {
+            mDeleteAppWithParent = deleteAppWithParent;
+            return this;
+        }
+
         /** Builds a UserProperties object with *all* values populated. */
         public UserProperties build() {
             return new UserProperties(
@@ -898,7 +938,8 @@
                     mCrossProfileIntentFilterAccessControl,
                     mCrossProfileIntentResolutionStrategy,
                     mMediaSharedWithParent,
-                    mCredentialShareableWithParent);
+                    mCredentialShareableWithParent,
+                    mDeleteAppWithParent);
         }
     } // end Builder
 
@@ -912,8 +953,8 @@
             @CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
             @CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
             boolean mediaSharedWithParent,
-            boolean credentialShareableWithParent) {
-
+            boolean credentialShareableWithParent,
+            boolean deleteAppWithParent) {
         mDefaultProperties = null;
         setShowInLauncher(showInLauncher);
         setStartWithParent(startWithParent);
@@ -925,5 +966,6 @@
         setCrossProfileIntentResolutionStrategy(crossProfileIntentResolutionStrategy);
         setMediaSharedWithParent(mediaSharedWithParent);
         setCredentialShareableWithParent(credentialShareableWithParent);
+        setDeleteAppWithParent(deleteAppWithParent);
     }
 }
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 0def59f..335975b 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -98,6 +98,14 @@
     /**
      * Current user preference for the scaling factor for fonts, relative
      * to the base density scaling.
+     *
+     * <p>Note: Please do not use this to hardcode font size equations. The equation for font
+     * scaling is now non-linear; this coefficient is no longer used as a direct multiplier to
+     * determine font size. It exists for informational purposes only.
+     *
+     * <p>Please use {@link android.util.TypedValue#applyDimension(int, float, DisplayMetrics)} or
+     * {@link android.util.TypedValue#deriveDimension(int, float, DisplayMetrics)} to convert
+     * between scaled font size dimensions and pixels.
      */
     public float fontScale;
 
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index ec6a396..1a2a0e5 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -17,7 +17,6 @@
 package android.credentials;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.credentials.CredentialEntry;
@@ -31,9 +30,7 @@
 
 /**
  * Represents the type and contained data fields of a {@link Credential}.
- * @hide
  */
-@TestApi
 public final class CredentialDescription implements Parcelable {
 
     /**
@@ -59,8 +56,11 @@
      *
      * @param type the type of the credential returned.
      * @param flattenedRequestString flattened JSON string that will be matched with requests.
-     * @param credentialEntries a list of {@link  CredentialEntry}s that have been returned
-     *                          to the developer upon credential creation.
+     * @param credentialEntries a list of {@link CredentialEntry}s that are to be shown on the
+     *                          account selector if a credential matches with this description.
+     *                          Each entry contains information to be displayed within an
+     *                          entry on the UI, as well as a {@link android.app.PendingIntent}
+     *                          that will be invoked if the user selects this entry.
      *
      * @throws IllegalArgumentException If type is empty.
      */
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 232d063..1db14a3 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -314,53 +313,37 @@
     }
 
     /**
-     *  Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
-     * a CredentialProvider has. This registry will then be used by
-     * {@link #executeGetCredential(GetCredentialRequest, Activity,
-            * CancellationSignal, Executor, OutcomeReceiver)} to determine where to
-     * fetch the requested {@link Credential} from.
-     *
+     * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
+     * a CredentialProvider has. This registry will then be used to determine where to
+     * fetch the requested {@link Credential} from. Not all credential types will be supported.
+     * The distinction will be made by the JetPack layer. For the types that are supported,
+     * JetPack will add a new key-value pair into {@link GetCredentialRequest}. These will not
+     * be persistent on the device. The Credential Providers will need to call this API again
+     * upon device reboot.
      *
      * @param request the request data
-     * @param cancellationSignal an optional signal that allows for cancelling this call
-     * @param executor the callback will take place on this {@link Executor}
-     * @param callback the callback invoked when the request succeeds or fails
      *
      * @throws {@link  UnsupportedOperationException} if the feature has not been enabled.
+     * @throws {@link  com.android.server.credentials.NonCredentialProviderCallerException}
+     * if the calling package name is not also listed as a Credential Provider.
+     * @throws {@link  IllegalArgumentException} if the calling Credential Provider can not handle
+     * one or more of the Credential Types that are sent for registration.
      *
-     * @hide
      */
-    @TestApi
     public void registerCredentialDescription(
-            @NonNull RegisterCredentialDescriptionRequest request,
-            @Nullable CancellationSignal cancellationSignal,
-            @CallbackExecutor @NonNull Executor executor,
-            @NonNull OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {
+            @NonNull RegisterCredentialDescriptionRequest request) {
 
         if (!isCredentialDescriptionApiEnabled()) {
             throw new UnsupportedOperationException("This API is not currently supported.");
         }
 
-        requireNonNull(executor, "executor must not be null");
-        requireNonNull(callback, "callback must not be null");
+        requireNonNull(request, "request must not be null");
 
-        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
-            Log.w(TAG, "executeCreateCredential already canceled");
-            return;
-        }
-
-        ICancellationSignal cancelRemote = null;
         try {
-            cancelRemote = mService.registerCredentialDescription(request,
-                    new RegisterCredentialDescriptionTransport(executor, callback),
-                    mContext.getOpPackageName());
+            mService.registerCredentialDescription(request, mContext.getOpPackageName());
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
-
-        if (cancellationSignal != null && cancelRemote != null) {
-            cancellationSignal.setRemote(cancelRemote);
-        }
     }
 
 
@@ -370,45 +353,25 @@
      *
      *
      * @param request the request data
-     * @param cancellationSignal an optional signal that allows for cancelling this call
-     * @param executor the callback will take place on this {@link Executor}
-     * @param callback the callback invoked when the request succeeds or fails
      *
      * @throws {@link  UnsupportedOperationException} if the feature has not been enabled.
      *
-     * @hide
      */
-    @TestApi
-    public void unRegisterCredentialDescription(
-            @NonNull UnregisterCredentialDescriptionRequest request,
-            @Nullable CancellationSignal cancellationSignal,
-            @CallbackExecutor @NonNull Executor executor,
-            @NonNull OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {
+    public void unregisterCredentialDescription(
+            @NonNull UnregisterCredentialDescriptionRequest request) {
 
         if (!isCredentialDescriptionApiEnabled()) {
             throw new UnsupportedOperationException("This API is not currently supported.");
         }
 
-        requireNonNull(executor, "executor must not be null");
-        requireNonNull(callback, "callback must not be null");
+        requireNonNull(request, "request must not be null");
 
-        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
-            Log.w(TAG, "executeCreateCredential already canceled");
-            return;
-        }
-
-        ICancellationSignal cancelRemote = null;
         try {
-            cancelRemote = mService.unRegisterCredentialDescription(request,
-                    new UnregisterCredentialDescriptionTransport(executor, callback),
-                    mContext.getOpPackageName());
+            mService.unregisterCredentialDescription(request, mContext.getOpPackageName());
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
 
-        if (cancellationSignal != null && cancelRemote != null) {
-            cancellationSignal.setRemote(cancelRemote);
-        }
     }
 
     private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
@@ -572,54 +535,4 @@
                     () -> mCallback.onError(new SetEnabledProvidersException(errorType, message)));
         }
     }
-
-    private static class RegisterCredentialDescriptionTransport
-            extends IRegisterCredentialDescriptionCallback.Stub {
-
-        private final Executor mExecutor;
-        private final OutcomeReceiver<Void, RegisterCredentialDescriptionException> mCallback;
-
-        private RegisterCredentialDescriptionTransport(Executor executor,
-                OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {
-            mExecutor = executor;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onResponse() {
-            mCallback.onResult(null);
-        }
-
-        @Override
-        public void onError(String errorCode, String message) {
-            mExecutor.execute(
-                    () -> mCallback.onError(new RegisterCredentialDescriptionException(errorCode,
-                            message)));
-        }
-    }
-
-    private static class UnregisterCredentialDescriptionTransport
-            extends IUnregisterCredentialDescriptionCallback.Stub {
-
-        private final Executor mExecutor;
-        private final OutcomeReceiver<Void, UnregisterCredentialDescriptionException> mCallback;
-
-        private UnregisterCredentialDescriptionTransport(Executor executor,
-                OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {
-            mExecutor = executor;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onResponse() {
-            mCallback.onResult(null);
-        }
-
-        @Override
-        public void onError(String errorCode, String message) {
-            mExecutor.execute(
-                    () -> mCallback.onError(new UnregisterCredentialDescriptionException(errorCode,
-                            message)));
-        }
-    }
 }
diff --git a/core/java/android/credentials/GetCredentialOption.java b/core/java/android/credentials/GetCredentialOption.java
index 55daf86..f2895c7 100644
--- a/core/java/android/credentials/GetCredentialOption.java
+++ b/core/java/android/credentials/GetCredentialOption.java
@@ -32,6 +32,14 @@
 public final class GetCredentialOption implements Parcelable {
 
     /**
+     * Bundle key to the flattened version of the JSON request string. Framework will use this key
+     * to determine which types of Credentials will utilize Credential Registry when filtering
+     * Credential Providers to ping.
+     */
+    public static final String FLATTENED_REQUEST = "android.credentials"
+            + ".GetCredentialOption.FLATTENED_REQUEST_STRING";
+
+    /**
      * The requested credential type.
      */
     @NonNull
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index 75b3d0c..4ca3124 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -27,8 +27,6 @@
 import android.credentials.ICreateCredentialCallback;
 import android.credentials.IGetCredentialCallback;
 import android.credentials.IListEnabledProvidersCallback;
-import android.credentials.IRegisterCredentialDescriptionCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
 import android.credentials.ISetEnabledProvidersCallback;
 import android.os.ICancellationSignal;
 
@@ -49,8 +47,8 @@
 
     void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
 
-    @nullable ICancellationSignal registerCredentialDescription(in RegisterCredentialDescriptionRequest request, in IRegisterCredentialDescriptionCallback callback, String callingPackage);
+    void registerCredentialDescription(in RegisterCredentialDescriptionRequest request, String callingPackage);
 
-    @nullable ICancellationSignal unRegisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, in IUnregisterCredentialDescriptionCallback callback, String callingPackage);
+    void unregisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, String callingPackage);
 }
 
diff --git a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl b/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
deleted file mode 100644
index b30a12a..0000000
--- a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.credentials;
-
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IUnregisterCredentialDescriptionCallback {
-    oneway void onResponse();
-    oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionException.java b/core/java/android/credentials/RegisterCredentialDescriptionException.java
deleted file mode 100644
index d19bb8e..0000000
--- a/core/java/android/credentials/RegisterCredentialDescriptionException.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.os.CancellationSignal;
-import android.os.OutcomeReceiver;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Represents an error encountered during the {@link
- * CredentialManager#registerCredentialDescription(RegisterCredentialDescriptionRequest,
- * CancellationSignal, Executor, OutcomeReceiver)} operation.
- *
- * @hide
- */
-@TestApi
-public class RegisterCredentialDescriptionException extends Exception {
-
-    @NonNull public final String errorType;
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public RegisterCredentialDescriptionException(@NonNull String errorType,
-            @Nullable String message) {
-        this(errorType, message, null);
-    }
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public RegisterCredentialDescriptionException(
-            @NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
-        super(message, cause);
-        this.errorType =
-                Preconditions
-                        .checkStringNotEmpty(errorType, "errorType must not be empty");
-    }
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public RegisterCredentialDescriptionException(@NonNull String errorType,
-            @Nullable Throwable cause) {
-        this(errorType, null, cause);
-    }
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public RegisterCredentialDescriptionException(@NonNull String errorType) {
-        this(errorType, null, null);
-    }
-}
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
index f257ac5..b5f3610 100644
--- a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
@@ -19,7 +19,6 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -28,20 +27,18 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 
 /**
  * A request to register a {@link ComponentName} that contains an actively provisioned
  * {@link Credential} represented by a {@link CredentialDescription}.
  *
- * @hide
  */
-@TestApi
 public final class RegisterCredentialDescriptionRequest implements Parcelable {
 
-    public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
-
     @NonNull
     private final List<CredentialDescription> mCredentialDescriptions;
 
@@ -51,7 +48,7 @@
     }
 
     public RegisterCredentialDescriptionRequest(
-            @NonNull List<CredentialDescription> credentialDescriptions) {
+            @NonNull Set<CredentialDescription> credentialDescriptions) {
         mCredentialDescriptions = new ArrayList<>(requireNonNull(credentialDescriptions));
     }
 
@@ -89,7 +86,7 @@
     }
 
     @NonNull
-    public List<CredentialDescription> getCredentialDescriptions() {
-        return mCredentialDescriptions;
+    public Set<CredentialDescription> getCredentialDescriptions() {
+        return new HashSet<>(mCredentialDescriptions);
     }
 }
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionException.java b/core/java/android/credentials/UnregisterCredentialDescriptionException.java
deleted file mode 100644
index a16915a..0000000
--- a/core/java/android/credentials/UnregisterCredentialDescriptionException.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.os.CancellationSignal;
-import android.os.OutcomeReceiver;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Represents an error encountered during the {@link
- * CredentialManager#registerCredentialDescription(RegisterCredentialDescriptionRequest,
- * CancellationSignal, Executor, OutcomeReceiver)} operation.
- *
- * @hide
- */
-@TestApi
-public class UnregisterCredentialDescriptionException extends Exception {
-
-    @NonNull public final String errorType;
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public UnregisterCredentialDescriptionException(@NonNull String errorType,
-            @Nullable String message) {
-        this(errorType, message, null);
-    }
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public UnregisterCredentialDescriptionException(
-            @NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
-        super(message, cause);
-        this.errorType =
-                Preconditions
-                        .checkStringNotEmpty(errorType, "errorType must not be empty");
-    }
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public UnregisterCredentialDescriptionException(@NonNull String errorType,
-            @Nullable Throwable cause) {
-        this(errorType, null, cause);
-    }
-
-    /**
-     * Constructs a {@link RegisterCredentialDescriptionException}.
-     *
-     * @throws IllegalArgumentException If errorType is empty.
-     */
-    public UnregisterCredentialDescriptionException(@NonNull String errorType) {
-        this(errorType, null, null);
-    }
-}
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
index 6cf40e7..cfda474 100644
--- a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
@@ -19,38 +19,45 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.AnnotationValidations;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 
 /**
  * A request to unregister a {@link ComponentName} that contains an actively provisioned
  * {@link Credential} represented by a {@link CredentialDescription}. *
  *
- * @hide
  */
-@TestApi
 public final class UnregisterCredentialDescriptionRequest implements Parcelable {
 
     @NonNull
-    private final CredentialDescription mCredentialDescription;
+    private final List<CredentialDescription> mCredentialDescriptions;
 
-    public UnregisterCredentialDescriptionRequest(@NonNull CredentialDescription
-            credentialDescription) {
-        mCredentialDescription = requireNonNull(credentialDescription);
+    public UnregisterCredentialDescriptionRequest(
+            @NonNull CredentialDescription credentialDescription) {
+        mCredentialDescriptions = Arrays.asList(requireNonNull(credentialDescription));
+    }
+
+    public UnregisterCredentialDescriptionRequest(
+            @NonNull List<CredentialDescription> credentialDescriptions) {
+        mCredentialDescriptions = new ArrayList<>(requireNonNull(credentialDescriptions));
     }
 
     private UnregisterCredentialDescriptionRequest(@NonNull Parcel in) {
-        CredentialDescription credentialDescription =
-                CredentialDescription.CREATOR.createFromParcel(in);
+        List<CredentialDescription> credentialDescriptions = new ArrayList<>();
+        in.readTypedList(credentialDescriptions, CredentialDescription.CREATOR);
 
-        mCredentialDescription = credentialDescription;
+        mCredentialDescriptions = new ArrayList<>();
         AnnotationValidations.validate(android.annotation.NonNull.class, null,
-                credentialDescription);
+                credentialDescriptions);
+        mCredentialDescriptions.addAll(credentialDescriptions);
     }
 
     public static final @NonNull Parcelable.Creator<UnregisterCredentialDescriptionRequest>
@@ -73,11 +80,11 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        mCredentialDescription.writeToParcel(dest, flags);
+        dest.writeTypedList(mCredentialDescriptions, flags);
     }
 
     @NonNull
-    public CredentialDescription getCredentialDescription() {
-        return mCredentialDescription;
+    public List<CredentialDescription> getCredentialDescriptions() {
+        return mCredentialDescriptions;
     }
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b333f5a..08238ca 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1301,6 +1301,54 @@
     }
 
     /**
+     * Sets the HDR conversion mode for the device.
+     *
+     * @param hdrConversionMode The {@link HdrConversionMode} to set.
+     * Note, {@code HdrConversionMode.preferredHdrOutputType} is only applicable when
+     * {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+     *
+     * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is not set
+     * when hdrConversionMode.conversionMode is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+     * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is set but
+     * hdrConversionMode.conversionMode is not {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+     *
+     * @see #getHdrConversionMode
+     * @see #getSupportedHdrOutputTypes
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_HDR_CONVERSION_MODE)
+    public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+        mGlobal.setHdrConversionMode(hdrConversionMode);
+    }
+
+    /**
+     * Returns the {@link HdrConversionMode} of the device, which is set by the user.
+     *
+     * @see #setHdrConversionMode
+     * @see #getSupportedHdrOutputTypes
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public HdrConversionMode getHdrConversionMode() {
+        return mGlobal.getHdrConversionMode();
+    }
+
+    /**
+     * Returns the HDR output types supported by the device.
+     *
+     * @see #getHdrConversionMode
+     * @see #setHdrConversionMode
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public @HdrType int[] getSupportedHdrOutputTypes() {
+        return mGlobal.getSupportedHdrOutputTypes();
+    }
+
+    /**
      * When enabled the app requested mode is always selected regardless of user settings and
      * policies for low brightness, low battery, etc.
      *
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f038c66..d9db177 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -979,6 +979,39 @@
     }
 
     /**
+     * Sets the {@link HdrConversionMode} for the device.
+     */
+    public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+        try {
+            mDm.setHdrConversionMode(hdrConversionMode);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the {@link HdrConversionMode} of the device.
+     */
+    public HdrConversionMode getHdrConversionMode() {
+        try {
+            return mDm.getHdrConversionMode();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the HDR output types supported by the device.
+     */
+    public @HdrType int[] getSupportedHdrOutputTypes() {
+        try {
+            return mDm.getSupportedHdrOutputTypes();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * When enabled the app requested display resolution and refresh rate is always selected
      * in DisplayModeDirector regardless of user settings and policies for low brightness, low
      * battery etc.
diff --git a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl b/core/java/android/hardware/display/HdrConversionMode.aidl
similarity index 65%
rename from core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
rename to core/java/android/hardware/display/HdrConversionMode.aidl
index 124a319..ac89dd6 100644
--- a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
+++ b/core/java/android/hardware/display/HdrConversionMode.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,6 @@
  * limitations under the License.
  */
 
-package android.credentials;
+package android.hardware.display;
 
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IRegisterCredentialDescriptionCallback {
-    oneway void onResponse();
-    oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+parcelable HdrConversionMode;
\ No newline at end of file
diff --git a/core/java/android/hardware/display/HdrConversionMode.java b/core/java/android/hardware/display/HdrConversionMode.java
new file mode 100644
index 0000000..1accd17
--- /dev/null
+++ b/core/java/android/hardware/display/HdrConversionMode.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Display;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes the HDR conversion mode for a device.
+ *
+ * This class is used when user changes the HDR conversion mode of the device via
+ * {@link DisplayManager#setHdrConversionMode(HdrConversionMode)}.
+ * <p>
+ * HDR conversion mode has a conversionMode and preferredHdrOutputType. </p><p>
+ * The conversionMode can be one of:
+ * HDR_CONVERSION_PASSSTHROUGH : HDR conversion is disabled. The output HDR type will change
+ * dynamically to match the content. In this mode, preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_AUTO: The output HDR type is selected by the implementation. In this mode,
+ * preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_FORCE : The implementation converts all content to this HDR type, when possible.
+ * In this mode, preferredHdrOutputType should be set.
+ * </p>
+ * @hide
+ */
+@TestApi
+public final class HdrConversionMode implements Parcelable {
+    /** HDR output conversion is disabled */
+    public static final int HDR_CONVERSION_PASSTHROUGH = 1;
+    /** HDR output conversion is managed by the device manufacturer's implementation. */
+    public static final int HDR_CONVERSION_SYSTEM = 2;
+    /**
+     * HDR output conversion is set by the user. The preferred output type must be
+     * set in this case.
+     */
+    public static final int HDR_CONVERSION_FORCE = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"HDR_CONVERSION"}, value = {
+            HDR_CONVERSION_PASSTHROUGH,
+            HDR_CONVERSION_SYSTEM,
+            HDR_CONVERSION_FORCE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConversionMode {}
+
+    public static final @NonNull
+            Parcelable.Creator<HdrConversionMode> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public HdrConversionMode createFromParcel(Parcel source) {
+                    return new HdrConversionMode(source);
+                }
+
+                @Override
+                public HdrConversionMode[] newArray(int size) {
+                    return new HdrConversionMode[size];
+                }
+            };
+
+    private final @ConversionMode int mConversionMode;
+    private @Display.HdrCapabilities.HdrType int mPreferredHdrOutputType;
+
+    public HdrConversionMode(int conversionMode, int preferredHdrOutputType) {
+        if (conversionMode != HdrConversionMode.HDR_CONVERSION_FORCE
+                && preferredHdrOutputType != -1) {
+            throw new IllegalArgumentException("preferredHdrOutputType must not be set if"
+                    + " the conversion mode is not HDR_CONVERSION_FORCE");
+        }
+
+        mConversionMode = conversionMode;
+        mPreferredHdrOutputType = preferredHdrOutputType;
+    }
+
+    public HdrConversionMode(int conversionMode) {
+        mConversionMode = conversionMode;
+        mPreferredHdrOutputType = Display.HdrCapabilities.HDR_TYPE_INVALID;
+    }
+
+    private HdrConversionMode(Parcel source) {
+        this(source.readInt(), source.readInt());
+    }
+
+    public int getConversionMode() {
+        return mConversionMode;
+    }
+
+    public int getPreferredHdrOutputType() {
+        return mPreferredHdrOutputType;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mConversionMode);
+        dest.writeInt(mPreferredHdrOutputType);
+    }
+}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 28bb35f..0a44f85 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -23,6 +23,7 @@
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.Curve;
 import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.hardware.display.HdrConversionMode;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
@@ -174,6 +175,14 @@
     Mode getUserPreferredDisplayMode(int displayId);
     Mode getSystemPreferredDisplayMode(int displayId);
 
+    // Sets the HDR conversion mode for a device.
+    // Requires MODIFY_HDR_CONVERSION_MODE permission.
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+                + ".permission.MODIFY_HDR_CONVERSION_MODE)")
+    void setHdrConversionMode(in HdrConversionMode hdrConversionMode);
+    HdrConversionMode getHdrConversionMode();
+    int[] getSupportedHdrOutputTypes();
+
     // When enabled the app requested display resolution and refresh rate is always selected
     // in DisplayModeDirector regardless of user settings and policies for low brightness, low
     // battery etc.
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 101a071..25ee6af 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -283,7 +283,13 @@
      * A scaling factor for fonts displayed on the display.  This is the same
      * as {@link #density}, except that it may be adjusted in smaller
      * increments at runtime based on a user preference for the font size.
+     *
+     * @deprecated this scalar factor is no longer accurate due to adaptive non-linear font scaling.
+     *  Please use {@link TypedValue#applyDimension(int, float, DisplayMetrics)} or
+     *  {@link TypedValue#deriveDimension(int, float, DisplayMetrics)} to convert between SP font
+     *  sizes and pixels.
      */
+    @Deprecated
     public float scaledDensity;
 
     /**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 689dce8..25863a6 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2323,6 +2323,10 @@
          */
         public static final float INVALID_LUMINANCE = -1;
         /**
+         * Invalid HDR type value.
+         */
+        public static final int HDR_TYPE_INVALID = -1;
+        /**
          * Dolby Vision high dynamic range (HDR) display.
          */
         public static final int HDR_TYPE_DOLBY_VISION = 1;
@@ -2350,6 +2354,7 @@
 
         /** @hide */
         @IntDef(prefix = { "HDR_TYPE_" }, value = {
+                HDR_TYPE_INVALID,
                 HDR_TYPE_DOLBY_VISION,
                 HDR_TYPE_HDR10,
                 HDR_TYPE_HLG,
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f6348d7..490091b 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -292,7 +292,7 @@
     public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
             @NonNull WindowlessWindowManager wwm) {
         mWm = wwm;
-        mViewRoot = new ViewRootImpl(c, d, mWm);
+        mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
         addConfigCallback(c, d);
 
         WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
@@ -322,7 +322,7 @@
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
 
-        mViewRoot = new ViewRootImpl(context, display, mWm);
+        mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout());
         addConfigCallback(context, display);
 
         WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5165478..6eb932e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -930,13 +930,14 @@
     private String mTag = TAG;
 
     public ViewRootImpl(Context context, Display display) {
-        this(context, display, WindowManagerGlobal.getWindowSession());
+        this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
     }
 
-    public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) {
+    public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
+            WindowLayout windowLayout) {
         mContext = context;
         mWindowSession = session;
-        mWindowLayout = new WindowLayout();
+        mWindowLayout = windowLayout;
         mDisplay = display;
         mBasePackageName = context.getBasePackageName();
         mThread = Thread.currentThread();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5f6f55a..5144f3a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -629,6 +629,114 @@
     @interface DisplayImePolicy {}
 
     /**
+     * The root state of all the state. This is an abstract state, and the keyguard should be in
+     * one of this sub state.
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_ROOT = 0x0;
+
+    /**
+     * Keyguard is off, so activity can be shown on the screen.
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_OFF = 0x1;
+
+    /**
+     * The keyguard is off, but lock screen is still rendered on the screen. Waiting for
+     * starting unlock animation.
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_GOING_AWAY = 0x11;
+
+    /**
+     * They keyguard is on, so normal activities cannot be shown on the screen. This is an abstract
+     * state, and the keyguard should be in one of ths sub state.
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_ON = 0x2;
+
+    /**
+     * The keyguard is on and not occluded.
+     * @hide
+     */
+    int KEYGUARD_STATE_KEYGUARD_TOP = 0x21;
+
+    /**
+     * The keyguard is on, and the lock screen is shown.
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_LOCKSCREEN_SHOWN = 0x211;
+
+    /**
+     * The keyguard is on, and the AOD is shown.
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_AOD_SHOWN = 0x212;
+
+    /**
+     * The keyguard is on but it's occluded by a normal SHOW_WHEN_LOCKED activity (i.e. non
+     * occluded by Dream activity).
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_OCCLUDED = 0x22;
+
+    /**
+     * The keyguard is on but it's occluded by a Dream activity.
+     *
+     * @hide
+     */
+    int KEYGUARD_STATE_DREAMING = 0x221;
+
+    /** @hide */
+    @IntDef(prefix = { "KEYGUARD_STATE_" }, value = {
+            KEYGUARD_STATE_ROOT,
+            KEYGUARD_STATE_OFF,
+            KEYGUARD_STATE_GOING_AWAY,
+            KEYGUARD_STATE_ON,
+            KEYGUARD_STATE_KEYGUARD_TOP,
+            KEYGUARD_STATE_LOCKSCREEN_SHOWN,
+            KEYGUARD_STATE_AOD_SHOWN,
+            KEYGUARD_STATE_OCCLUDED,
+            KEYGUARD_STATE_DREAMING,
+    })
+    @interface KeyguardState {}
+
+    /**
+     * @hide
+     */
+    static String keyguardStateToString(@KeyguardState int type) {
+        switch (type) {
+            case KEYGUARD_STATE_ROOT:
+                return "ROOT";
+            case KEYGUARD_STATE_OFF:
+                return "KEYGUARD_OFF";
+            case KEYGUARD_STATE_GOING_AWAY:
+                return "KEYGUARD_GOING_AWAY";
+            case KEYGUARD_STATE_ON:
+                return "KEYGUARD_ON";
+            case KEYGUARD_STATE_KEYGUARD_TOP:
+                return "KEYGUARD_TOP";
+            case KEYGUARD_STATE_LOCKSCREEN_SHOWN:
+                return "KEYGUARD_LOCKSCREEN_SHOWN";
+            case KEYGUARD_STATE_AOD_SHOWN:
+                return "KEYGUARD_AOD_SHOWN";
+            case KEYGUARD_STATE_OCCLUDED:
+                return "KEYGUARD_OCCLUDED";
+            case KEYGUARD_STATE_DREAMING:
+                return "KEYGUARD_DREAMING";
+            default:
+                return "KEYGUARD_STATE_UNKNOWN(" + Integer.toHexString(type) + ")";
+        }
+    }
+
+    /**
      * Exception that is thrown when trying to add view whose
      * {@link LayoutParams} {@link LayoutParams#token}
      * is invalid.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index acc0c0b..4a9dc5b 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -388,7 +388,8 @@
             if (windowlessSession == null) {
                 root = new ViewRootImpl(view.getContext(), display);
             } else {
-                root = new ViewRootImpl(view.getContext(), display, windowlessSession);
+                root = new ViewRootImpl(view.getContext(), display,
+                        windowlessSession, new WindowlessWindowLayout());
             }
 
             view.setLayoutParams(wparams);
diff --git a/core/java/android/view/WindowlessWindowLayout.java b/core/java/android/view/WindowlessWindowLayout.java
new file mode 100644
index 0000000..e2afaa5
--- /dev/null
+++ b/core/java/android/view/WindowlessWindowLayout.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.app.WindowConfiguration.WindowingMode;
+import android.graphics.Rect;
+import android.view.WindowInsets.Type.InsetsType;
+import android.window.ClientWindowFrames;
+
+/**
+ * Computes window frames for the windowless window.
+ *
+ * This can't be replaced with the regular WindowLayout because WindowLayout computes bounds
+ * with insets and cutout values. Since windowless windows aren't affected by insets and
+ * instead are bound by their parent, it will compute incorrect bounds for them if insets are used.
+ *
+ * @hide
+ */
+public class WindowlessWindowLayout extends WindowLayout {
+
+    @Override
+    public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
+            Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
+            int requestedWidth, int requestedHeight, @InsetsType int requestedVisibleTypes,
+            float compatScale, ClientWindowFrames frames) {
+        if (frames.attachedFrame == null) {
+            frames.frame.set(0, 0, attrs.width, attrs.height);
+            frames.parentFrame.set(frames.frame);
+            frames.displayFrame.set(frames.frame);
+            return;
+        }
+
+        final int height = calculateLength(attrs.height, requestedHeight,
+                frames.attachedFrame.height());
+        final int width = calculateLength(attrs.width, requestedWidth,
+                frames.attachedFrame.width());
+        Gravity.apply(attrs.gravity, width, height, frames.attachedFrame,
+                (int) (attrs.x + attrs.horizontalMargin),
+                (int) (attrs.y + attrs.verticalMargin),
+                frames.frame);
+        frames.displayFrame.set(frames.frame);
+        frames.parentFrame.set(frames.attachedFrame);
+    }
+
+    private static int calculateLength(int attrLength, int requestedLength, int parentLength) {
+        if (attrLength == MATCH_PARENT) {
+            return parentLength;
+        }
+        if (attrLength == WRAP_CONTENT) {
+            return requestedLength;
+        }
+        return attrLength;
+    }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7d37c50..21d22f2 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -94,10 +94,7 @@
     private InsetsState mInsetsState;
     private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
     private final MergedConfiguration mTmpConfig = new MergedConfiguration();
-    private final InsetsState mTmpInsetsState = new InsetsState();
-    private final Rect mTmpDisplayCutoutSafe = new Rect();
-    private final Rect mTmpWindowBounds = new Rect();
-    private final WindowLayout mLayout = new WindowLayout();
+    private final WindowlessWindowLayout mLayout = new WindowlessWindowLayout();
 
     public WindowlessWindowManager(Configuration c, SurfaceControl rootSurface,
             IBinder hostInputToken) {
@@ -349,27 +346,22 @@
         }
         WindowManager.LayoutParams attrs = state.mParams;
 
-        mTmpFrames.attachedFrame = state.mAttachedFrame;
+        ClientWindowFrames frames = new ClientWindowFrames();
+        frames.attachedFrame = state.mAttachedFrame;
 
-        if (state.mAttachedFrame == null) {
-            mTmpWindowBounds.set(0, 0, requestedWidth, requestedHeight);
-        } else {
-            mTmpWindowBounds.set(state.mAttachedFrame);
-        }
+        mLayout.computeFrames(attrs, null, null, null, WindowConfiguration.WINDOWING_MODE_UNDEFINED,
+                requestedWidth, requestedHeight, 0, 0,
+                frames);
 
-        mLayout.computeFrames(attrs, mTmpInsetsState, mTmpDisplayCutoutSafe, mTmpWindowBounds,
-                WindowConfiguration.WINDOWING_MODE_UNDEFINED, requestedWidth, requestedHeight, 0,
-                1f, mTmpFrames);
-
-        state.mFrame.set(mTmpFrames.frame);
+        state.mFrame.set(frames.frame);
         if (outFrames != null) {
-            outFrames.frame.set(mTmpFrames.frame);
-            outFrames.parentFrame.set(mTmpFrames.parentFrame);
-            outFrames.displayFrame.set(mTmpFrames.displayFrame);
+            outFrames.frame.set(frames.frame);
+            outFrames.parentFrame.set(frames.parentFrame);
+            outFrames.displayFrame.set(frames.displayFrame);
         }
 
-        t.setPosition(leash, mTmpFrames.frame.left, mTmpFrames.frame.top);
-        t.setWindowCrop(leash, mTmpFrames.frame.width(), mTmpFrames.frame.height());
+        t.setPosition(leash, frames.frame.left, frames.frame.top);
+        t.setWindowCrop(leash, frames.frame.width(), frames.frame.height());
 
         if (viewFlags == View.VISIBLE) {
             // TODO(b/262892794) ViewRootImpl modifies the app's rendering SurfaceControl
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 82e1777..b112e7a 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -117,7 +117,7 @@
     repeated KeyguardOccludedProto keyguard_occluded_states = 2 [deprecated=true];
     optional bool aod_showing = 3;
     repeated KeyguardPerDisplayProto keyguard_per_display = 4;
-    optional bool keyguard_going_away = 5;
+    optional bool keyguard_going_away = 5 [deprecated=true];
 }
 
 message KeyguardOccludedProto {
@@ -134,7 +134,7 @@
     optional bool keyguard_showing = 2;
     optional bool aod_showing = 3;
     optional bool keyguard_occluded = 4;
-    optional bool keyguard_going_away = 5;
+    optional bool keyguard_going_away = 5 [deprecated=true];
 }
 
 /* represents PhoneWindowManager */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 077b0c5..07aefbd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1733,6 +1733,34 @@
         android:protectionLevel="dangerous"
         android:permissionFlags="hardRestricted" />
 
+    <!-- Allows an application to access wrist temperature data from the watch sensors.
+        <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
+        <p>Protection level: dangerous -->
+    <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:label="@string/permlab_bodySensorsWristTemperature"
+                android:description="@string/permdesc_bodySensorsWristTemperature"
+                android:backgroundPermission="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
+                android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to access wrist temperature data from the watch sensors.
+         If you're requesting this permission, you must also request
+         {@link #BODY_SENSORS_WRIST_TEMPERATURE}. Requesting this permission by itself doesn't
+         give you heart rate body sensors access.
+         <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
+         <p>Protection level: dangerous
+
+         <p> This is a hard restricted permission which cannot be held by an app until
+         the installer on record allowlists the permission. For more details see
+         {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+    -->
+    <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:label="@string/permlab_bodySensors_wristTemperature_background"
+                android:description="@string/permdesc_bodySensors_wristTemperature_background"
+                android:protectionLevel="dangerous"
+                android:permissionFlags="hardRestricted" />
+
     <!-- Allows an app to use fingerprint hardware.
          <p>Protection level: normal
          @deprecated Applications should request {@link
@@ -5244,6 +5272,12 @@
     <permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE"
                 android:protectionLevel="signature" />
 
+    <!-- Allows an application to modify the HDR conversion mode.
+         @hide
+         @TestApi -->
+    <permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to control VPN.
          <p>Not for use by third-party applications.</p>
          @hide -->
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 4b27bf2..fe296c7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -18,7 +18,6 @@
     <bool name="kg_enable_camera_default_widget">true</bool>
     <bool name="kg_center_small_widgets_vertically">false</bool>
     <bool name="kg_top_align_page_shrink_on_bouncer_visible">true</bool>
-    <bool name="kg_wake_on_acquire_start">false</bool>
     <bool name="action_bar_embed_tabs">true</bool>
     <bool name="split_action_bar_is_narrow">true</bool>
     <bool name="preferences_prefer_dual_pane">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7c6f81d..fa8ba9d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1329,6 +1329,16 @@
     <!-- Description of the background body sensors permission, listed so the user can decide whether to allow the application to access data from body sensors in the background. [CHAR LIMIT=NONE] -->
     <string name="permdesc_bodySensors_background" product="default">Allows the app to access body sensor data, such as heart rate, temperature, and blood oxygen percentage, while the app is in the background.</string>
 
+    <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
+    <string name="permlab_bodySensorsWristTemperature">Access body sensor wrist temperature data while the app is in use.</string>
+    <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_bodySensorsWristTemperature" product="default">Allows the app to access body sensor wrist temperature data, while the app is in use.</string>
+
+    <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
+    <string name="permlab_bodySensors_wristTemperature_background">Access body sensor wrist temperature data while the app is in the background.</string>
+    <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_bodySensors_wristTemperature_background" product="default">Allows the app to access body sensor wrist temperature data, while the app is in the background.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readCalendar">Read calendar events and details</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 38336e4..9dc7835 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2774,7 +2774,6 @@
   <java-symbol type="dimen" name="fast_scroller_minimum_touch_target" />
   <java-symbol type="array" name="config_cdma_international_roaming_indicators" />
   <java-symbol type="string" name="kg_text_message_separator" />
-  <java-symbol type="bool" name="kg_wake_on_acquire_start" />
 
   <java-symbol type="bool" name="config_use_sim_language_file" />
   <java-symbol type="bool" name="config_LTE_eri_for_network_name" />
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index 01907fb..dea1b0e 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -16,8 +16,11 @@
 
 package android.content.pm;
 
+import static org.junit.Assert.assertThrows;
+
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ServiceSpecificException;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.LargeTest;
@@ -114,6 +117,34 @@
         }
     }
 
+    /**
+     * Test that exceptions created when parcelling data in the service are really
+     * sent to the client and re-thrown.
+     */
+    public void testThrownException() throws Exception {
+        final List<ThrowingObject> throwers = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            throwers.add(new ThrowingObject(/* throws= */ false));
+        }
+        throwers.add(new ThrowingObject(/* throws= */ true));
+
+        final ParceledListSlice<ThrowingObject> src = new ParceledListSlice<>(throwers);
+        src.setInlineCountLimit(1);
+
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.writeParcelable(src, 0);
+            parcel.setDataPosition(0);
+
+            assertThrows(ServiceSpecificException.class, () -> {
+                final ParceledListSlice<ThrowingObject> dst =
+                        parcel.readParcelable(getClass().getClassLoader());
+            });
+        } finally {
+            parcel.recycle();
+        }
+    }
+
     private void sendParcelStringList(List<String> list) {
         StringParceledListSlice slice;
         Parcel parcel = Parcel.obtain();
@@ -236,6 +267,40 @@
         };
     }
 
+    public static class ThrowingObject implements Parcelable {
+
+        private final boolean mShouldThrow;
+
+        public ThrowingObject(boolean shouldThrow) {
+            mShouldThrow = shouldThrow;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            if (mShouldThrow) {
+                throw new ServiceSpecificException(1234);
+            }
+            dest.writeBoolean(mShouldThrow);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<ThrowingObject> CREATOR = new Creator<ThrowingObject>() {
+            @Override
+            public ThrowingObject createFromParcel(Parcel source) {
+                return new ThrowingObject(source.readBoolean());
+            }
+
+            @Override
+            public ThrowingObject[] newArray(int size) {
+                return new ThrowingObject[size];
+            }
+        };
+    }
+
     public static class SmallObject extends BaseObject {
         public int mFieldA;
         public int mFieldB;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 129924a..f77ac81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -143,8 +143,8 @@
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
         return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
                 || (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
-                    && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
-                        == WINDOWING_MODE_FREEFORM);
+                && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
+                == WINDOWING_MODE_FREEFORM);
     }
 
     private void createWindowDecoration(
@@ -175,16 +175,18 @@
                 new CaptionTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
         windowDecoration.setDragResizeCallback(taskPositioner);
+        windowDecoration.setDragDetector(touchEventListener.mDragDetector);
         windowDecoration.relayout(taskInfo, startT, finishT);
         setupCaptionColor(taskInfo, windowDecoration);
     }
 
     private class CaptionTouchEventListener implements
-            View.OnClickListener, View.OnTouchListener {
+            View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
 
         private final int mTaskId;
         private final WindowContainerToken mTaskToken;
         private final DragResizeCallback mDragResizeCallback;
+        private final DragDetector mDragDetector;
 
         private int mDragPointerId = -1;
 
@@ -194,6 +196,7 @@
             mTaskId = taskInfo.taskId;
             mTaskToken = taskInfo.token;
             mDragResizeCallback = dragResizeCallback;
+            mDragDetector = new DragDetector(this);
         }
 
         @Override
@@ -216,7 +219,7 @@
             if (v.getId() != R.id.caption) {
                 return false;
             }
-            handleEventForMove(e);
+            mDragDetector.onMotionEvent(e);
 
             if (e.getAction() != MotionEvent.ACTION_DOWN) {
                 return false;
@@ -235,10 +238,11 @@
          * @param e {@link MotionEvent} to process
          * @return {@code true} if a drag is happening; or {@code false} if it is not
          */
-        private void handleEventForMove(MotionEvent e) {
+        @Override
+        public boolean handleMotionEvent(MotionEvent e) {
             final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
             if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-                return;
+                return false;
             }
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
@@ -261,6 +265,7 @@
                     break;
                 }
             }
+            return true;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index d26f1fc..f94fbfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -49,7 +49,7 @@
     private View.OnTouchListener mOnCaptionTouchListener;
     private DragResizeCallback mDragResizeCallback;
     private DragResizeInputListener mDragResizeListener;
-    private final DragDetector mDragDetector;
+    private DragDetector mDragDetector;
 
     private RelayoutParams mRelayoutParams = new RelayoutParams();
     private final RelayoutResult<WindowDecorLinearLayout> mResult =
@@ -69,7 +69,6 @@
         mHandler = handler;
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
-        mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
     }
 
     void setCaptionListeners(
@@ -83,6 +82,11 @@
         mDragResizeCallback = dragResizeCallback;
     }
 
+    void setDragDetector(DragDetector dragDetector) {
+        mDragDetector = dragDetector;
+        mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
+    }
+
     @Override
     void relayout(RunningTaskInfo taskInfo) {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2863adc..bd3afa9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -205,7 +205,7 @@
     }
 
     private class DesktopModeTouchEventListener implements
-            View.OnClickListener, View.OnTouchListener {
+            View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
 
         private final int mTaskId;
         private final WindowContainerToken mTaskToken;
@@ -216,12 +216,11 @@
 
         private DesktopModeTouchEventListener(
                 RunningTaskInfo taskInfo,
-                DragResizeCallback dragResizeCallback,
-                DragDetector dragDetector) {
+                DragResizeCallback dragResizeCallback) {
             mTaskId = taskInfo.taskId;
             mTaskToken = taskInfo.token;
             mDragResizeCallback = dragResizeCallback;
-            mDragDetector = dragDetector;
+            mDragDetector = new DragDetector(this);
         }
 
         @Override
@@ -254,8 +253,7 @@
                 return false;
             }
             if (id == R.id.caption_handle) {
-                isDrag = mDragDetector.detectDragEvent(e);
-                handleEventForMove(e);
+                isDrag = mDragDetector.onMotionEvent(e);
             }
             if (e.getAction() != MotionEvent.ACTION_DOWN) {
                 return isDrag;
@@ -272,19 +270,19 @@
 
         /**
          * @param e {@link MotionEvent} to process
-         * @return {@code true} if a drag is happening; or {@code false} if it is not
+         * @return {@code true} if the motion event is handled.
          */
-        private void handleEventForMove(MotionEvent e) {
+        @Override
+        public boolean handleMotionEvent(MotionEvent e) {
             final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
             if (DesktopModeStatus.isProto2Enabled()
                     && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-                return;
+                return false;
             }
             if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
-                    && mDesktopModeController.get().getDisplayAreaWindowingMode(
-                    taskInfo.displayId)
+                    && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
                     == WINDOWING_MODE_FULLSCREEN) {
-                return;
+                return false;
             }
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
@@ -324,6 +322,7 @@
                     break;
                 }
             }
+            return true;
         }
     }
 
@@ -560,10 +559,10 @@
         final TaskPositioner taskPositioner =
                 new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
         final DesktopModeTouchEventListener touchEventListener =
-                new DesktopModeTouchEventListener(
-                        taskInfo, taskPositioner, windowDecoration.getDragDetector());
+                new DesktopModeTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
         windowDecoration.setDragResizeCallback(taskPositioner);
+        windowDecoration.setDragDetector(touchEventListener.mDragDetector);
         windowDecoration.relayout(taskInfo, startT, finishT);
         incrementEventReceiverTasks(taskInfo.displayId);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 1a38d24..9550937 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -57,7 +57,7 @@
     private View.OnTouchListener mOnCaptionTouchListener;
     private DragResizeCallback mDragResizeCallback;
     private DragResizeInputListener mDragResizeListener;
-    private final DragDetector mDragDetector;
+    private DragDetector mDragDetector;
 
     private RelayoutParams mRelayoutParams = new RelayoutParams();
     private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
@@ -81,7 +81,6 @@
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
         mDesktopActive = DesktopModeStatus.isActive(mContext);
-        mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
     }
 
     void setCaptionListeners(
@@ -95,8 +94,9 @@
         mDragResizeCallback = dragResizeCallback;
     }
 
-    DragDetector getDragDetector() {
-        return mDragDetector;
+    void setDragDetector(DragDetector dragDetector) {
+        mDragDetector = dragDetector;
+        mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 0abe8ab..4fac843 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
@@ -25,63 +26,82 @@
 import android.view.MotionEvent;
 
 /**
- * A detector for touch inputs that differentiates between drag and click inputs.
+ * A detector for touch inputs that differentiates between drag and click inputs. It receives a flow
+ * of {@link MotionEvent} and generates a new flow of motion events with slop in consideration to
+ * the event handler. In particular, it always passes down, up and cancel events. It'll pass move
+ * events only when there is at least one move event that's beyond the slop threshold. For the
+ * purpose of convenience it also passes all events of other actions.
+ *
  * All touch events must be passed through this class to track a drag event.
  */
-public class DragDetector {
+class DragDetector {
+    private final MotionEventHandler mEventHandler;
+
+    private final PointF mInputDownPoint = new PointF();
     private int mTouchSlop;
-    private PointF mInputDownPoint;
     private boolean mIsDragEvent;
     private int mDragPointerId;
-    public DragDetector(int touchSlop) {
-        mTouchSlop = touchSlop;
-        mInputDownPoint = new PointF();
-        mIsDragEvent = false;
-        mDragPointerId = -1;
+
+    private boolean mResultOfDownAction;
+
+    DragDetector(MotionEventHandler eventHandler) {
+        resetState();
+        mEventHandler = eventHandler;
     }
 
     /**
-     * Determine if {@link MotionEvent} is part of a drag event.
-     * @return {@code true} if this is a drag event, {@code false} if not
-     */
-    public boolean detectDragEvent(MotionEvent ev) {
-        switch (ev.getAction()) {
+     * The receiver of the {@link MotionEvent} flow.
+     *
+     * @return the result returned by {@link #mEventHandler}, or the result when
+     * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
+    */
+    boolean onMotionEvent(MotionEvent ev) {
+        switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
+                // Only touch screens generate noisy moves.
+                mIsDragEvent = (ev.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
                 mDragPointerId = ev.getPointerId(0);
                 float rawX = ev.getRawX(0);
                 float rawY = ev.getRawY(0);
                 mInputDownPoint.set(rawX, rawY);
-                return false;
+                mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
+                return mResultOfDownAction;
             }
             case ACTION_MOVE: {
                 if (!mIsDragEvent) {
                     int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
                     float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
                     float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
-                    if (Math.hypot(dx, dy) > mTouchSlop) {
-                        mIsDragEvent = true;
-                    }
+                    mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
                 }
-                return mIsDragEvent;
+                if (mIsDragEvent) {
+                    return mEventHandler.handleMotionEvent(ev);
+                } else {
+                    return mResultOfDownAction;
+                }
             }
-            case ACTION_UP: {
-                boolean result = mIsDragEvent;
-                mIsDragEvent = false;
-                mInputDownPoint.set(0, 0);
-                mDragPointerId = -1;
-                return result;
-            }
+            case ACTION_UP:
             case ACTION_CANCEL: {
-                mIsDragEvent = false;
-                mInputDownPoint.set(0, 0);
-                mDragPointerId = -1;
-                return false;
+                resetState();
+                return mEventHandler.handleMotionEvent(ev);
             }
+            default:
+                return mEventHandler.handleMotionEvent(ev);
         }
-        return mIsDragEvent;
     }
 
-    public void setTouchSlop(int touchSlop) {
+    void setTouchSlop(int touchSlop) {
         mTouchSlop = touchSlop;
     }
+
+    private void resetState() {
+        mIsDragEvent = false;
+        mInputDownPoint.set(0, 0);
+        mDragPointerId = -1;
+        mResultOfDownAction = false;
+    }
+
+    interface MotionEventHandler {
+        boolean handleMotionEvent(MotionEvent ev);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index bb67145..ea417b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -48,7 +48,6 @@
  * Task edges are for resizing with a mouse.
  * Task corners are for resizing with touch input.
  */
-// TODO(b/251270585): investigate how to pass taps in corners to the tasks
 class DragResizeInputListener implements AutoCloseable {
     private static final String TAG = "DragResizeInputListener";
 
@@ -116,7 +115,8 @@
         mInputEventReceiver = new TaskResizeInputEventReceiver(
                 mInputChannel, mHandler, mChoreographer);
         mCallback = callback;
-        mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
+        mDragDetector = new DragDetector(mInputEventReceiver);
+        mDragDetector.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
     }
 
     /**
@@ -224,12 +224,12 @@
         }
     }
 
-    private class TaskResizeInputEventReceiver extends InputEventReceiver {
+    private class TaskResizeInputEventReceiver extends InputEventReceiver
+            implements DragDetector.MotionEventHandler {
         private final Choreographer mChoreographer;
         private final Runnable mConsumeBatchEventRunnable;
         private boolean mConsumeBatchEventScheduled;
         private boolean mShouldHandleEvents;
-        private boolean mDragging;
 
         private TaskResizeInputEventReceiver(
                 InputChannel inputChannel, Handler handler, Choreographer choreographer) {
@@ -271,15 +271,15 @@
             if (!(inputEvent instanceof MotionEvent)) {
                 return false;
             }
+            return mDragDetector.onMotionEvent((MotionEvent) inputEvent);
+        }
 
-            MotionEvent e = (MotionEvent) inputEvent;
+        @Override
+        public boolean handleMotionEvent(MotionEvent e) {
             boolean result = false;
             // Check if this is a touch event vs mouse event.
             // Touch events are tracked in four corners. Other events are tracked in resize edges.
             boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
-            if (isTouch) {
-                mDragging = mDragDetector.detectDragEvent(e);
-            }
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
                     float x = e.getX(0);
@@ -306,24 +306,17 @@
                     int dragPointerIndex = e.findPointerIndex(mDragPointerId);
                     float rawX = e.getRawX(dragPointerIndex);
                     float rawY = e.getRawY(dragPointerIndex);
-                    if (!isTouch) {
-                        // For all other types allow immediate dragging.
-                        mDragging = true;
-                    }
-                    if (mDragging) {
-                        mCallback.onDragResizeMove(rawX, rawY);
-                        result = true;
-                    }
+                    mCallback.onDragResizeMove(rawX, rawY);
+                    result = true;
                     break;
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
-                    if (mShouldHandleEvents && mDragging) {
+                    if (mShouldHandleEvents) {
                         int dragPointerIndex = e.findPointerIndex(mDragPointerId);
                         mCallback.onDragResizeEnd(
                                 e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
                     }
-                    mDragging = false;
                     mShouldHandleEvents = false;
                     mDragPointerId = -1;
                     result = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index 20631f8..8cd2a59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -40,9 +40,7 @@
     private final Rect mTaskBoundsAtDragStart = new Rect();
     private final PointF mResizeStartPoint = new PointF();
     private final Rect mResizeTaskBounds = new Rect();
-    // Whether the |dragResizing| hint should be sent with the next bounds change WCT.
-    // Used to optimized fluid resizing of freeform tasks.
-    private boolean mPendingDragResizeHint = false;
+    private boolean mHasMoved = false;
 
     private int mCtrlType;
     private DragStartListener mDragStartListener;
@@ -60,11 +58,7 @@
 
     @Override
     public void onDragResizeStart(int ctrlType, float x, float y) {
-        if (ctrlType != CTRL_TYPE_UNDEFINED) {
-            // The task is being resized, send the |dragResizing| hint to core with the first
-            // bounds-change wct.
-            mPendingDragResizeHint = true;
-        }
+        mHasMoved = false;
 
         mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
         mCtrlType = ctrlType;
@@ -78,30 +72,44 @@
     public void onDragResizeMove(float x, float y) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (changeBounds(wct, x, y)) {
-            if (mPendingDragResizeHint) {
+            // The task is being resized, send the |dragResizing| hint to core with the first
+            // bounds-change wct.
+            if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) {
                 // This is the first bounds change since drag resize operation started.
                 wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
-                mPendingDragResizeHint = false;
             }
             mTaskOrganizer.applyTransaction(wct);
+            mHasMoved = true;
         }
     }
 
     @Override
     public void onDragResizeEnd(float x, float y) {
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
-        changeBounds(wct, x, y);
-        mTaskOrganizer.applyTransaction(wct);
+        // |mHasMoved| being false means there is no real change to the task bounds in WM core, so
+        // we don't need a WCT to finish it.
+        if (mHasMoved) {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
+            changeBounds(wct, x, y);
+            mTaskOrganizer.applyTransaction(wct);
+        }
 
-        mCtrlType = 0;
+        mCtrlType = CTRL_TYPE_UNDEFINED;
         mTaskBoundsAtDragStart.setEmpty();
         mResizeStartPoint.set(0, 0);
-        mPendingDragResizeHint = false;
+        mHasMoved = false;
     }
 
     private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
-        float deltaX = x - mResizeStartPoint.x;
+        // |mResizeTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not true,
+        // we can compare it against |mTaskBoundsAtDragStart|.
+        final int oldLeft = mHasMoved ? mResizeTaskBounds.left : mTaskBoundsAtDragStart.left;
+        final int oldTop = mHasMoved ? mResizeTaskBounds.top : mTaskBoundsAtDragStart.top;
+        final int oldRight = mHasMoved ? mResizeTaskBounds.right : mTaskBoundsAtDragStart.right;
+        final int oldBottom = mHasMoved ? mResizeTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
+
+        final float deltaX = x - mResizeStartPoint.x;
+        final float deltaY = y - mResizeStartPoint.y;
         mResizeTaskBounds.set(mTaskBoundsAtDragStart);
         if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
             mResizeTaskBounds.left += deltaX;
@@ -109,22 +117,22 @@
         if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
             mResizeTaskBounds.right += deltaX;
         }
-        float deltaY = y - mResizeStartPoint.y;
         if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
             mResizeTaskBounds.top += deltaY;
         }
         if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
             mResizeTaskBounds.bottom += deltaY;
         }
-        if (mCtrlType == 0) {
+        if (mCtrlType == CTRL_TYPE_UNDEFINED) {
             mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
         }
 
-        if (!mResizeTaskBounds.isEmpty()) {
-            wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
-            return true;
+        if (oldLeft == mResizeTaskBounds.left && oldTop == mResizeTaskBounds.top
+                && oldRight == mResizeTaskBounds.right && oldBottom == mResizeTaskBounds.bottom) {
+            return false;
         }
-        return false;
+        wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
+        return true;
     }
 
     interface DragStartListener {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 85812c4..8c3bea8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -23,6 +23,7 @@
 import com.android.server.wm.flicker.FlickerBuilder
 import com.android.server.wm.flicker.FlickerTest
 import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
 import com.android.server.wm.traces.common.service.PlatformConsts
 import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -30,6 +31,7 @@
 import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
 import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -71,7 +73,19 @@
     @Test
     fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
 
-    @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+    @FlakyTest
+    @Test
+    fun primaryAppLayerBecomesVisible() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+        flicker.layerBecomesVisible(primaryApp)
+    }
+
+    @Presubmit
+    @Test
+    fun primaryAppLayerBecomesVisibleShellTransit() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+        flicker.layerBecomesVisible(primaryApp)
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 7c62433..06a1449 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -23,6 +23,7 @@
 import com.android.server.wm.flicker.FlickerBuilder
 import com.android.server.wm.flicker.FlickerTest
 import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
 import com.android.server.wm.traces.common.service.PlatformConsts
 import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -30,6 +31,7 @@
 import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
 import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -71,7 +73,19 @@
     @Test
     fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
 
-    @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+    @FlakyTest
+    @Test
+    fun primaryAppLayerBecomesVisible() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+        flicker.layerBecomesVisible(primaryApp)
+    }
+
+    @Presubmit
+    @Test
+    fun primaryAppLayerBecomesVisibleShellTransit() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+        flicker.layerBecomesVisible(primaryApp)
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
new file mode 100644
index 0000000..8f84008
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.os.SystemClock
+import android.testing.AndroidTestingRunner
+import android.view.MotionEvent
+import android.view.InputDevice
+import androidx.test.filters.SmallTest
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/**
+ * Tests for [DragDetector].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DragDetectorTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DragDetectorTest {
+    private val motionEvents = mutableListOf<MotionEvent>()
+
+    @Mock
+    private lateinit var eventHandler: DragDetector.MotionEventHandler
+
+    private lateinit var dragDetector: DragDetector
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
+
+        dragDetector = DragDetector(eventHandler)
+        dragDetector.setTouchSlop(SLOP)
+    }
+
+    @After
+    fun tearDown() {
+        motionEvents.forEach {
+            it.recycle()
+        }
+        motionEvents.clear()
+    }
+
+    @Test
+    fun testNoMove_passesDownAndUp() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testMoveInSlop_touch_passesDownAndUp() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        val newX = X + SLOP - 1
+        assertFalse(
+                dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+        verify(eventHandler, never()).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testMoveInSlop_mouse_passesDownMoveAndUp() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            it.action == MotionEvent.ACTION_DOWN
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(
+                createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        val newX = X + SLOP - 1
+        assertTrue(dragDetector.onMotionEvent(
+                createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(
+                createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+    }
+
+    @Test
+    fun testMoveBeyondSlop_passesDownMoveAndUp() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            it.action == MotionEvent.ACTION_DOWN
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        val newX = X + SLOP + 1
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testPassesHoverEnter() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            it.action == MotionEvent.ACTION_HOVER_ENTER
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
+        })
+    }
+
+    @Test
+    fun testPassesHoverMove() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
+        })
+    }
+
+    @Test
+    fun testPassesHoverExit() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
+        })
+    }
+
+    private fun createMotionEvent(action: Int, x: Float = X, y: Float = Y, isTouch: Boolean = true):
+            MotionEvent {
+        val time = SystemClock.uptimeMillis()
+        val ev = MotionEvent.obtain(time, time, action, x, y, 0)
+        ev.source = if (isTouch) InputDevice.SOURCE_TOUCHSCREEN else InputDevice.SOURCE_MOUSE
+        motionEvents.add(ev)
+        return ev
+    }
+
+    companion object {
+        private const val SLOP = 10
+        private const val X = 123f
+        private const val Y = 234f
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index ac10ddb..804c416 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -1,6 +1,7 @@
 package com.android.wm.shell.windowdecor
 
 import android.app.ActivityManager
+import android.app.WindowConfiguration
 import android.graphics.Rect
 import android.os.IBinder
 import android.testing.AndroidTestingRunner
@@ -10,6 +11,7 @@
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
 import org.junit.Before
 import org.junit.Test
@@ -63,6 +65,90 @@
     }
 
     @Test
+    fun testDragResize_notMove_skipsTransactionOnEnd() {
+        taskPositioner.onDragResizeStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragResizeEnd(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat() + 10
+        )
+
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() {
+        taskPositioner.onDragResizeStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragResizeMove(
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragResizeEnd(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat() + 10
+        )
+
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() {
+        taskPositioner.onDragResizeStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragResizeMove(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat()
+        )
+        val rectAfterMove = Rect(STARTING_BOUNDS)
+        rectAfterMove.right += 10
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds == rectAfterMove
+            }
+        })
+
+        taskPositioner.onDragResizeEnd(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat() + 10
+        )
+        val rectAfterEnd = Rect(rectAfterMove)
+        rectAfterEnd.top += 10
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds == rectAfterEnd
+            }
+        })
+    }
+
+    @Test
     fun testDragResize_move_skipsDragResizingFlag() {
         taskPositioner.onDragResizeStart(
                 CTRL_TYPE_UNDEFINED, // Move
diff --git a/libs/securebox/Android.bp b/libs/securebox/Android.bp
new file mode 100644
index 0000000..a29c03c
--- /dev/null
+++ b/libs/securebox/Android.bp
@@ -0,0 +1,8 @@
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+    name: "securebox",
+    srcs: ["src/**/*.java"],
+}
diff --git a/libs/securebox/OWNERS b/libs/securebox/OWNERS
new file mode 100644
index 0000000..e160799
--- /dev/null
+++ b/libs/securebox/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java b/libs/securebox/src/com/android/security/SecureBox.java
similarity index 98%
rename from services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
rename to libs/securebox/src/com/android/security/SecureBox.java
index 51a37b3..0ebaff4 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
+++ b/libs/securebox/src/com/android/security/SecureBox.java
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.locksettings.recoverablekeystore;
+package com.android.security;
 
 import android.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+
 import java.math.BigInteger;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
@@ -41,6 +43,7 @@
 import java.security.spec.EllipticCurve;
 import java.security.spec.InvalidKeySpecException;
 import java.util.Arrays;
+
 import javax.crypto.AEADBadTagException;
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -380,7 +383,7 @@
      * @param publicKey The public key.
      * @return The key packed into a 65-byte array.
      */
-    static byte[] encodePublicKey(PublicKey publicKey) {
+    public static byte[] encodePublicKey(PublicKey publicKey) {
         ECPoint point = ((ECPublicKey) publicKey).getW();
         byte[] x = point.getAffineX().toByteArray();
         byte[] y = point.getAffineY().toByteArray();
@@ -394,8 +397,13 @@
         return output;
     }
 
-    @VisibleForTesting
-    static PublicKey decodePublicKey(byte[] keyBytes)
+    /**
+     * Decodes byte[] encoded public key.
+     *
+     * @param keyBytes encoded public key
+     * @return the public key
+     */
+    public static PublicKey decodePublicKey(byte[] keyBytes)
             throws NoSuchAlgorithmException, InvalidKeyException {
         BigInteger x =
                 new BigInteger(
diff --git a/libs/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp
new file mode 100644
index 0000000..7df546a
--- /dev/null
+++ b/libs/securebox/tests/Android.bp
@@ -0,0 +1,46 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "SecureBoxTests",
+    srcs: [
+        "**/*.java",
+    ],
+    static_libs: [
+        "securebox",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "frameworks-base-testutils",
+        "junit",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "testables",
+        "testng",
+        "truth-prebuilt",
+    ],
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+}
diff --git a/libs/securebox/tests/AndroidManifest.xml b/libs/securebox/tests/AndroidManifest.xml
new file mode 100644
index 0000000..3dc9563
--- /dev/null
+++ b/libs/securebox/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.security.tests">
+
+    <application android:debuggable="true" android:largeHeap="true">
+        <uses-library android:name="android.test.mock" />
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Tests for SecureBox"
+        android:targetPackage="com.android.security.tests">
+    </instrumentation>
+
+</manifest>
diff --git a/libs/securebox/tests/AndroidTest.xml b/libs/securebox/tests/AndroidTest.xml
new file mode 100644
index 0000000..54abd135
--- /dev/null
+++ b/libs/securebox/tests/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<configuration description="Runs Tests for SecureBox">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="SecureBoxTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="SecureBoxTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.security.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java b/libs/securebox/tests/src/com/android/security/SecureBoxTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
rename to libs/securebox/tests/src/com/android/security/SecureBoxTest.java
index 34235bd..b6e2365 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
+++ b/libs/securebox/tests/src/com/android/security/SecureBoxTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.locksettings.recoverablekeystore;
+package com.android.security;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -24,11 +24,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.ArrayUtils;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import com.android.internal.util.ArrayUtils;
-
 import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.security.InvalidKeyException;
diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java
index bb97c78..90d9929 100644
--- a/location/java/android/location/Address.java
+++ b/location/java/android/location/Address.java
@@ -26,7 +26,7 @@
 import android.os.Parcelable;
 
 /**
- * A class representing an Address, i.e, a set of Strings describing a location.
+ * A class representing an Address, that is, a set of Strings describing a location.
  *
  * The address format is a simplified version of xAL (eXtensible Address Language)
  * http://www.oasis-open.org/committees/ciq/ciq.html#6
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index f9f9fa4..11b5833 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -240,7 +240,11 @@
     }
 
     /**
-     * Returns {@code true} if GNSS chipset supports on demand time, {@code false} otherwise.
+     * Returns {@code true} if GNSS chipset requests periodic time signal injection from the
+     * platform in addition to on-demand and occasional time updates, {@code false} otherwise.
+     *
+     * <p><em>Note: The naming of this capability and the behavior it controls differ substantially.
+     * This is the result of a historic implementation bug, b/73893222.</em>
      */
     public boolean hasOnDemandTime() {
         return (mTopFlags & TOP_HAL_CAPABILITY_ON_DEMAND_TIME) != 0;
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 44bb56e..7ac8446 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -85,6 +85,7 @@
     public static int getNumPlanesForHardwareBufferFormat(int hardwareBufferFormat) {
         switch(hardwareBufferFormat) {
             case HardwareBuffer.YCBCR_420_888:
+            case HardwareBuffer.YCBCR_P010:
                 return 3;
             case HardwareBuffer.RGBA_8888:
             case HardwareBuffer.RGBX_8888:
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0c97989..2779fa2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -57,6 +57,7 @@
     <uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
     <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
     <uses-permission android:name="android.permission.BODY_SENSORS" />
+    <uses-permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE" />
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
     <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
     <uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
@@ -511,6 +512,9 @@
     <!-- Permission needed for CTS test - DefaultDisplayModeTest -->
     <uses-permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE" />
 
+    <!-- Permission needed for CTS test - HdrConversionTest -->
+    <uses-permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE" />
+
     <!-- Permissions needed for manual testing telephony time zone detector behavior -->
     <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
 
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
new file mode 100644
index 0000000..a64a0d1
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,9c1.65,0 3,1.35 3,3s-1.35,3 -3,3 -3,-1.35 -3,-3 1.35,-3 3,-3m0,-2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
new file mode 100644
index 0000000..40423c7
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
new file mode 100644
index 0000000..a0f7b5d
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
new file mode 100644
index 0000000..8757f22
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v6L4,17v2h16v-2h-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6zM12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
new file mode 100644
index 0000000..049013a
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
new file mode 100644
index 0000000..4f25e7d
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46 0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19c-0.59,-0.45 -0.74,-1.26 -0.37,-1.88l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91c-0.1,0.68 -0.72,1.22 -1.46,1.22zM10.62,20.25h2.76l0.37,-2.55 0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34 2.38,0.96 1.38,-2.4 -2.03,-1.58 0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78s-0.03,-0.53 -0.06,-0.78l-0.07,-0.56 2.03,-1.58 -1.39,-2.4 -2.39,0.96 -0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77l-0.52,-0.22 -0.37,-2.55h-2.76l-0.37,2.55 -0.53,0.21c-0.44,0.19 -0.88,0.44 -1.34,0.79l-0.45,0.33 -2.38,-0.95 -1.39,2.39 2.03,1.58 -0.07,0.56c-0.03,0.26 -0.06,0.53 -0.06,0.79s0.02,0.53 0.06,0.78l0.07,0.56 -2.03,1.58 1.38,2.4 2.39,-0.96 0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22 0.38,2.55z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
new file mode 100644
index 0000000..38234c0
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M2,7h4v10H2V7zM7,19h10V5H7V19zM9,7h6v10H9V7zM18,7h4v10h-4V7z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
new file mode 100644
index 0000000..6d7f49c
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,21L7,21v-1h10v1zM17,18L7,18L7,6h10v12zM17,4L7,4L7,3h10v1zM9.5,8.5L12,8.5L12,7L8,7v4h1.5zM12,17h4v-4h-1.5v2.5L12,15.5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
new file mode 100644
index 0000000..5ed6f19
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.5,4c-2.61,0.7 -5.67,1 -8.5,1s-5.89,-0.3 -8.5,-1L3,6c1.86,0.5 4,0.83 6,1v13h2v-6h2v6h2L15,7c2,-0.17 4.14,-0.5 6,-1l-0.5,-2zM12,4c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM7,24h2v-2L7,22v2zM11,24h2v-2h-2v2zM15,24h2v-2h-2v2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
new file mode 100644
index 0000000..16653e8
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M16,7.97v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02 0,-1.77 -1.02,-3.29 -2.5,-4.03zM5,9v6h4l5,5L14,4L9,9L5,9zM12,8.83v6.34L9.83,13L7,13v-2h2.83L12,8.83z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
new file mode 100644
index 0000000..e572c6a
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="@color/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM10,8.83v6.34L7.83,13L5,13v-2h2.83L10,8.83zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77 0,-4.28 -2.99,-7.86 -7,-8.77z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 658c03b..91cb4ba 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -50,6 +50,17 @@
 
   </LinearLayout>
 
+  <TextView
+      android:id="@+id/snackbar"
+      android:visibility="gone"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:paddingStart="16dp"
+      android:gravity="center_vertical"
+      android:textColor="@color/colorControlNormal"
+      android:textSize="@dimen/label_text_size"
+      android:background="@color/snackbar_bg_color"/>
+
   <View
       android:id="@+id/bottom_listDivider"
       android:layout_width="match_parent"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
index 33c0cca..a600ec6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
@@ -19,5 +19,6 @@
   <color name="footer_icon_enabled_color">#E8EAED</color>
   <color name="footer_icon_disabled_color">#5F6368</color>
   <color name="colorControlNormal">#202124</color>
+  <color name="snackbar_bg_color">@android:color/white</color>
 
 </resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
index 36d1fc1..c1494f9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
@@ -20,6 +20,7 @@
   <color name="footer_icon_enabled_color">@android:color/black</color>
   <color name="footer_icon_disabled_color">#ddd</color>
   <color name="colorControlNormal">@android:color/white</color>
+  <color name="snackbar_bg_color">#313235</color>
 
   <color name="colorAccessibilityMenuIcon">#3AA757</color>
 </resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
index fa42e61..b6328f0 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
@@ -15,9 +15,16 @@
  */
 package com.android.systemui.accessibility.accessibilitymenu.model;
 
+import android.util.Log;
+
 import com.android.systemui.accessibility.accessibilitymenu.R;
 
-/** Provides a data structure for a11y menu shortcuts. */
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides a data structure for a11y menu shortcuts.
+ */
 public class A11yMenuShortcut {
 
     public enum ShortcutId {
@@ -38,6 +45,88 @@
 
     private static final String TAG = "A11yMenuShortcut";
 
+    // Index of resource ID in the array, shortcutResource.
+    private static final int IMG_SRC_INDEX = 0;
+    private static final int IMG_COLOR_INDEX = 1;
+    private static final int CONTENT_DESCRIPTION_INDEX = 2;
+    private static final int LABEL_TEXT_INDEX = 3;
+
+    /** Map stores all shortcut resource IDs that is in matching order of defined shortcut. */
+    private static final Map<ShortcutId, int[]> sShortcutResource = new HashMap<>() {{
+            put(ShortcutId.ID_ASSISTANT_VALUE, new int[] {
+                    R.drawable.ic_logo_assistant_32dp,
+                    R.color.assistant_color,
+                    R.string.assistant_utterance,
+                    R.string.assistant_label,
+            });
+            put(ShortcutId.ID_A11YSETTING_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_settings_24dp,
+                    R.color.a11y_settings_color,
+                    R.string.a11y_settings_label,
+                    R.string.a11y_settings_label,
+            });
+            put(ShortcutId.ID_POWER_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_power_24dp,
+                    R.color.power_color,
+                    R.string.power_utterance,
+                    R.string.power_label,
+            });
+            put(ShortcutId.ID_RECENT_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_recent_apps_24dp,
+                    R.color.recent_apps_color,
+                    R.string.recent_apps_label,
+                    R.string.recent_apps_label,
+            });
+            put(ShortcutId.ID_LOCKSCREEN_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_lock_24dp,
+                    R.color.lockscreen_color,
+                    R.string.lockscreen_label,
+                    R.string.lockscreen_label,
+            });
+            put(ShortcutId.ID_QUICKSETTING_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_quick_settings_24dp,
+                    R.color.quick_settings_color,
+                    R.string.quick_settings_label,
+                    R.string.quick_settings_label,
+            });
+            put(ShortcutId.ID_NOTIFICATION_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_notifications_24dp,
+                    R.color.notifications_color,
+                    R.string.notifications_label,
+                    R.string.notifications_label,
+            });
+            put(ShortcutId.ID_SCREENSHOT_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_screenshot_24dp,
+                    R.color.screenshot_color,
+                    R.string.screenshot_utterance,
+                    R.string.screenshot_label,
+            });
+            put(ShortcutId.ID_BRIGHTNESS_UP_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_brightness_up_24dp,
+                    R.color.brightness_color,
+                    R.string.brightness_up_label,
+                    R.string.brightness_up_label,
+            });
+            put(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_brightness_down_24dp,
+                    R.color.brightness_color,
+                    R.string.brightness_down_label,
+                    R.string.brightness_down_label,
+            });
+            put(ShortcutId.ID_VOLUME_UP_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_volume_up_24dp,
+                    R.color.volume_color,
+                    R.string.volume_up_label,
+                    R.string.volume_up_label,
+            });
+            put(ShortcutId.ID_VOLUME_DOWN_VALUE, new int[] {
+                    R.drawable.ic_logo_a11y_volume_down_24dp,
+                    R.color.volume_color,
+                    R.string.volume_down_label,
+                    R.string.volume_down_label,
+            });
+        }};
+
     /** Shortcut id used to identify. */
     private int mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal();
 
@@ -53,17 +142,33 @@
 
     /**
      * Sets Id to shortcut, checks the value first and updates shortcut resources. It will set id to
+     * default value {@link ShortcutId.UNSPECIFIED_ID_VALUE} if invalid.
      *
      * @param id id set to shortcut
      */
     public void setId(int id) {
         mShortcutId = id;
 
-        // TODO(jonesriley) load the proper resources based on id
-        imageSrc = R.drawable.ic_logo_assistant_32dp;
-        imageColor = android.R.color.darker_gray;
-        imgContentDescription = R.string.empty_content;
-        labelText = R.string.empty_content;
+        if (id < ShortcutId.UNSPECIFIED_ID_VALUE.ordinal()
+                || id > ShortcutId.values().length) {
+            mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal();
+            Log.w(
+                    TAG, String.format(
+                            "setId to default UNSPECIFIED_ID as id is invalid. "
+                                    + "Max value is %d while id is %d",
+                            ShortcutId.values().length, id
+                    ));
+        }
+        int[] resources = sShortcutResource.getOrDefault(ShortcutId.values()[id], new int[] {
+                R.drawable.ic_add_32dp,
+                android.R.color.darker_gray,
+                R.string.empty_content,
+                R.string.empty_content,
+        });
+        imageSrc = resources[IMG_SRC_INDEX];
+        imageColor = resources[IMG_COLOR_INDEX];
+        imgContentDescription = resources[CONTENT_DESCRIPTION_INDEX];
+        labelText = resources[LABEL_TEXT_INDEX];
     }
 
     public int getId() {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 740bc8a..7bdb8f9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -18,10 +18,14 @@
 
 import static java.lang.Math.max;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -31,8 +35,11 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
-import android.widget.Toast;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
 
 import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
 import com.android.systemui.accessibility.accessibilitymenu.R;
@@ -69,11 +76,15 @@
     private ViewGroup mLayout;
     private WindowManager.LayoutParams mLayoutParameter;
     private A11yMenuViewPager mA11yMenuViewPager;
+    private Handler mHandler;
+    private AccessibilityManager mAccessibilityManager;
 
     public A11yMenuOverlayLayout(AccessibilityMenuService service) {
         mService = service;
         mWindowManager = mService.getSystemService(WindowManager.class);
         configureLayout();
+        mHandler = new Handler(Looper.getMainLooper());
+        mAccessibilityManager = mService.getSystemService(AccessibilityManager.class);
     }
 
     /** Creates Accessibility menu layout and configure layout parameters. */
@@ -261,9 +272,30 @@
         mLayout.setVisibility((mLayout.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE);
     }
 
-    /** Shows hint text on Toast. */
-    public void showToast(String text) {
-        final View viewPos = mLayout.findViewById(R.id.coordinatorLayout);
-        Toast.makeText(viewPos.getContext(), text, Toast.LENGTH_SHORT).show();
+    /** Shows hint text on a minimal Snackbar-like text view. */
+    public void showSnackbar(String text) {
+        final int animationDurationMs = 300;
+        final int timeoutDurationMs = mAccessibilityManager.getRecommendedTimeoutMillis(2000,
+                AccessibilityManager.FLAG_CONTENT_TEXT);
+
+        final TextView snackbar = mLayout.findViewById(R.id.snackbar);
+        snackbar.setText(text);
+
+        // Remove any existing fade-out animation before starting any new animations.
+        mHandler.removeCallbacksAndMessages(null);
+
+        if (snackbar.getVisibility() != View.VISIBLE) {
+            snackbar.setAlpha(0f);
+            snackbar.setVisibility(View.VISIBLE);
+            snackbar.animate().alpha(1f).setDuration(animationDurationMs).setListener(null);
+        }
+        mHandler.postDelayed(() -> snackbar.animate().alpha(0f).setDuration(
+                animationDurationMs).setListener(
+                new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(@NonNull Animator animation) {
+                            snackbar.setVisibility(View.GONE);
+                        }
+                    }), timeoutDurationMs);
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
index 18e8a96..bf922bc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
@@ -21,4 +21,5 @@
     const val MESSAGE_ID_SLOT_SELECTED = 1337
     const val KEY_SLOT_ID = "slot_id"
     const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
+    const val KEY_HIGHLIGHT_QUICK_AFFORDANCES = "highlight_quick_affordances"
 }
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 3d3ccf4..a38afdf 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -197,13 +197,10 @@
 -packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
 -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
 -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -611,7 +608,6 @@
 -packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 314c736..db88b59 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -171,7 +171,7 @@
             a1 = TonalSpec(HueSource(), ChromaConstant(36.0)),
             a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
             a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
-            n1 = TonalSpec(HueSource(), ChromaConstant(4.0)),
+            n1 = TonalSpec(HueSource(), ChromaConstant(6.0)),
             n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
     )),
     VIBRANT(CoreSpec(
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
index acd2462..7d03b0d 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
@@ -21,7 +21,7 @@
   <item android:state_selected="true">
     <shape android:shape="oval">
       <stroke
-          android:color="@color/control_primary_text"
+          android:color="?android:attr/textColorPrimary"
           android:width="2dp"/>
       <size
           android:width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index d97031f..52a98984 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -41,9 +41,6 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/privacy_container"
             app:layout_constraintBottom_toBottomOf="@id/carrier_group"
-            app:layout_constraintEnd_toStartOf="@id/carrier_group"
-            app:layout_constraintHorizontal_bias="0"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
         />
         <Transform
             android:scaleX="2.57"
@@ -62,18 +59,18 @@
         />
     </Constraint>
 
+    <!-- LargeScreenShadeHeaderController helps with managing clock width to layout this view -->
     <Constraint
         android:id="@+id/carrier_group">
         <Layout
-            app:layout_constraintWidth_min="48dp"
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="@dimen/large_screen_shade_header_min_height"
-            app:layout_constraintStart_toEndOf="@id/clock"
+            app:layout_constraintWidth_min="48dp"
+            app:layout_constraintWidth_default="wrap"
+            app:layout_constraintStart_toStartOf="@id/clock"
             app:layout_constraintTop_toBottomOf="@id/privacy_container"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintHorizontal_bias="1"
             app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
             />
         <PropertySet
             android:alpha="1"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index e4f85db..5fd2fab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -235,14 +235,6 @@
                     }
                     updateChildren(0 /* translationY */, 1f /* alpha */);
                 }
-
-                private void updateChildren(int translationY, float alpha) {
-                    for (int i = 0; i < KeyguardSecurityContainer.this.getChildCount(); ++i) {
-                        View child = KeyguardSecurityContainer.this.getChildAt(i);
-                        child.setTranslationY(translationY);
-                        child.setAlpha(alpha);
-                    }
-                }
             };
 
     private final OnBackAnimationCallback mBackCallback = new OnBackAnimationCallback() {
@@ -594,6 +586,7 @@
      * This will run when the bouncer shows in all cases except when the user drags the bouncer up.
      */
     public void startAppearAnimation(SecurityMode securityMode) {
+        updateChildren(0 /* translationY */, 1f /* alpha */);
         mViewMode.startAppearAnimation(securityMode);
     }
 
@@ -777,6 +770,14 @@
         setScaleY(scale);
     }
 
+    private void updateChildren(int translationY, float alpha) {
+        for (int i = 0; i < getChildCount(); ++i) {
+            View child = getChildAt(i);
+            child.setTranslationY(translationY);
+            child.setAlpha(alpha);
+        }
+    }
+
     /**
      * Enscapsulates the differences between bouncer modes for the container.
      */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9d6bb08..9eb7e2cd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -26,7 +26,6 @@
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
 import static android.hardware.biometrics.BiometricConstants.LockoutMode;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
 import static android.hardware.biometrics.BiometricSourceType.FACE;
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -353,7 +352,6 @@
     private final Executor mBackgroundExecutor;
     private final SensorPrivacyManager mSensorPrivacyManager;
     private final ActiveUnlockConfig mActiveUnlockConfig;
-    private final PowerManager mPowerManager;
     private final IDreamManager mDreamManager;
     private final TelephonyManager mTelephonyManager;
     @Nullable
@@ -361,7 +359,6 @@
     @Nullable
     private final FaceManager mFaceManager;
     private final LockPatternUtils mLockPatternUtils;
-    private final boolean mWakeOnFingerprintAcquiredStart;
     @VisibleForTesting
     @DevicePostureController.DevicePostureInt
     protected int mConfigFaceAuthSupportedPosture;
@@ -885,11 +882,6 @@
     private void handleFingerprintAcquired(
             @BiometricFingerprintConstants.FingerprintAcquired int acquireInfo) {
         Assert.isMainThread();
-        if (mWakeOnFingerprintAcquiredStart && acquireInfo == FINGERPRINT_ACQUIRED_START) {
-            mPowerManager.wakeUp(
-                    SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_BIOMETRIC,
-                    "com.android.systemui.keyguard:FINGERPRINT_ACQUIRED_START");
-        }
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1538,6 +1530,7 @@
     @VisibleForTesting
     void setAssistantVisible(boolean assistantVisible) {
         mAssistantVisible = assistantVisible;
+        mLogger.logAssistantVisible(mAssistantVisible);
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
                 FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED);
         if (mAssistantVisible) {
@@ -2050,7 +2043,6 @@
             UiEventLogger uiEventLogger,
             // This has to be a provider because SessionTracker depends on KeyguardUpdateMonitor :(
             Provider<SessionTracker> sessionTrackerProvider,
-            PowerManager powerManager,
             TrustManager trustManager,
             SubscriptionManager subscriptionManager,
             UserManager userManager,
@@ -2087,7 +2079,6 @@
         mLogger = logger;
         mUiEventLogger = uiEventLogger;
         mSessionTrackerProvider = sessionTrackerProvider;
-        mPowerManager = powerManager;
         mTrustManager = trustManager;
         mUserManager = userManager;
         mDreamManager = dreamManager;
@@ -2098,8 +2089,6 @@
         mFpm = fingerprintManager;
         mFaceManager = faceManager;
         mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
-        mWakeOnFingerprintAcquiredStart = context.getResources()
-                        .getBoolean(com.android.internal.R.bool.kg_wake_on_acquire_start);
         mFaceAcquiredInfoIgnoreList = Arrays.stream(
                 mContext.getResources().getIntArray(
                         R.array.config_face_acquire_device_entry_ignorelist))
@@ -2680,7 +2669,10 @@
 
     private boolean shouldListenForFaceAssistant() {
         BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
-        return mAssistantVisible && mKeyguardOccluded
+        return mAssistantVisible
+                // There can be intermediate states where mKeyguardShowing is false but
+                // mKeyguardOccluded is true, we don't want to run face auth in such a scenario.
+                && (mKeyguardShowing && mKeyguardOccluded)
                 && !(face != null && face.mAuthenticated)
                 && !mUserHasTrust.get(getCurrentUser(), false);
     }
@@ -3681,6 +3673,7 @@
                 if (info == null) {
                     return;
                 }
+                mLogger.logTaskStackChangedForAssistant(info.visible);
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_ASSISTANT_STACK_CHANGED,
                         info.visible));
             } catch (RemoteException e) {
@@ -3879,7 +3872,6 @@
         pw.println("  getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
         pw.println("  getUserUnlockedWithBiometric()="
                 + getUserUnlockedWithBiometric(getCurrentUser()));
-        pw.println("  mWakeOnFingerprintAcquiredStart=" + mWakeOnFingerprintAcquiredStart);
         pw.println("  SIM States:");
         for (SimData data : mSimDatas.values()) {
             pw.println("    " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 5b42455..201a1d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -431,4 +431,20 @@
             str1 = PowerManager.wakeReasonToString(pmWakeReason)
         }, { "Skip updating face listening state on wakeup from $str1"})
     }
+
+    fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
+        logBuffer.log(TAG, VERBOSE, {
+            bool1 = assistantVisible
+        }, {
+            "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
+        })
+    }
+
+    fun logAssistantVisible(assistantVisible: Boolean) {
+        logBuffer.log(TAG, VERBOSE, {
+            bool1 = assistantVisible
+        }, {
+            "Updating mAssistantVisible to new value: $bool1"
+        })
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 108a0e8..6012e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -265,10 +265,11 @@
     // 600- status bar
 
     // TODO(b/256614753): Tracking Bug
-    val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
+    val NEW_STATUS_BAR_MOBILE_ICONS =
+        unreleasedFlag(606, "new_status_bar_mobile_icons", teamfood = true)
 
     // TODO(b/256614210): Tracking Bug
-    val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon")
+    val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon", teamfood = true)
 
     // TODO(b/256614751): Tracking Bug
     val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index d020529..e9d7a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -66,6 +66,8 @@
 object KeyguardBottomAreaViewBinder {
 
     private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
+    private const val SCALE_SELECTED_BUTTON = 1.23f
+    private const val DIM_ALPHA = 0.3f
 
     /**
      * Defines interface for an object that acts as the binding between the view and its view-model.
@@ -315,6 +317,12 @@
             } else {
                 null
             }
+        view
+            .animate()
+            .scaleX(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+            .scaleY(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+            .alpha(if (viewModel.isDimmed) DIM_ALPHA else 1f)
+            .start()
 
         view.isClickable = viewModel.isClickable
         if (viewModel.isClickable) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a5ae8ba5..8808574 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -24,7 +24,6 @@
 import android.hardware.display.DisplayManager
 import android.os.Bundle
 import android.os.IBinder
-import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.SurfaceControlViewHost
 import android.view.View
@@ -65,6 +64,11 @@
     val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
     private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
     private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT)
+    private val shouldHighlightSelectedAffordance: Boolean =
+        bundle.getBoolean(
+            KeyguardQuickAffordancePreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
+            false,
+        )
 
     private var host: SurfaceControlViewHost
 
@@ -82,6 +86,7 @@
                 bundle.getString(
                     KeyguardQuickAffordancePreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
                 ),
+            shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
         )
         runBlocking(mainDispatcher) {
             host =
@@ -154,8 +159,7 @@
             bottomAreaView,
             FrameLayout.LayoutParams(
                 FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                Gravity.BOTTOM,
+                FrameLayout.LayoutParams.MATCH_PARENT,
             ),
         )
     }
@@ -195,7 +199,13 @@
             ?.events
             ?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
         clockView?.let { parentView.removeView(it) }
-        clockView = clockController.clock?.largeClock?.view?.apply { parentView.addView(this) }
+        clockView =
+            clockController.clock?.largeClock?.view?.apply {
+                if (shouldHighlightSelectedAffordance) {
+                    alpha = DIM_ALPHA
+                }
+                parentView.addView(this)
+            }
     }
 
     companion object {
@@ -203,5 +213,7 @@
         private const val KEY_VIEW_WIDTH = "width"
         private const val KEY_VIEW_HEIGHT = "height"
         private const val KEY_DISPLAY_ID = "display_id"
+
+        private const val DIM_ALPHA = 0.3f
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 5d85680..1e3b60c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -45,12 +45,17 @@
     private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
     private val burnInHelperWrapper: BurnInHelperWrapper,
 ) {
+    data class PreviewMode(
+        val isInPreviewMode: Boolean = false,
+        val shouldHighlightSelectedAffordance: Boolean = false,
+    )
+
     /**
      * Whether this view-model instance is powering the preview experience that renders exclusively
      * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
      * experience.
      */
-    private val isInPreviewMode = MutableStateFlow(false)
+    private val previewMode = MutableStateFlow(PreviewMode())
 
     /**
      * ID of the slot that's currently selected in the preview that renders exclusively in the
@@ -87,8 +92,8 @@
         keyguardInteractor.isDozing.map { !it }.distinctUntilChanged()
     /** An observable for the alpha level for the entire bottom area. */
     val alpha: Flow<Float> =
-        isInPreviewMode.flatMapLatest { isInPreviewMode ->
-            if (isInPreviewMode) {
+        previewMode.flatMapLatest {
+            if (it.isInPreviewMode) {
                 flowOf(1f)
             } else {
                 bottomAreaInteractor.alpha.distinctUntilChanged()
@@ -129,9 +134,18 @@
      * lock screen.
      *
      * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one.
+     * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be
+     * highlighted (while all others are dimmed to make the selected one stand out).
      */
-    fun enablePreviewMode(initiallySelectedSlotId: String?) {
-        isInPreviewMode.value = true
+    fun enablePreviewMode(
+        initiallySelectedSlotId: String?,
+        shouldHighlightSelectedAffordance: Boolean,
+    ) {
+        previewMode.value =
+            PreviewMode(
+                isInPreviewMode = true,
+                shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
+            )
         onPreviewSlotSelected(
             initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
         )
@@ -150,9 +164,9 @@
     private fun button(
         position: KeyguardQuickAffordancePosition
     ): Flow<KeyguardQuickAffordanceViewModel> {
-        return isInPreviewMode.flatMapLatest { isInPreviewMode ->
+        return previewMode.flatMapLatest { previewMode ->
             combine(
-                    if (isInPreviewMode) {
+                    if (previewMode.isInPreviewMode) {
                         quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
                     } else {
                         quickAffordanceInteractor.quickAffordance(position = position)
@@ -161,11 +175,18 @@
                     areQuickAffordancesFullyOpaque,
                     selectedPreviewSlotId,
                 ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId ->
+                    val isSelected = selectedPreviewSlotId == position.toSlotId()
                     model.toViewModel(
-                        animateReveal = !isInPreviewMode && animateReveal,
-                        isClickable = isFullyOpaque && !isInPreviewMode,
+                        animateReveal = !previewMode.isInPreviewMode && animateReveal,
+                        isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
                         isSelected =
-                            (isInPreviewMode && selectedPreviewSlotId == position.toSlotId()),
+                            previewMode.isInPreviewMode &&
+                                previewMode.shouldHighlightSelectedAffordance &&
+                                isSelected,
+                        isDimmed =
+                            previewMode.isInPreviewMode &&
+                                previewMode.shouldHighlightSelectedAffordance &&
+                                !isSelected,
                     )
                 }
                 .distinctUntilChanged()
@@ -176,6 +197,7 @@
         animateReveal: Boolean,
         isClickable: Boolean,
         isSelected: Boolean,
+        isDimmed: Boolean,
     ): KeyguardQuickAffordanceViewModel {
         return when (this) {
             is KeyguardQuickAffordanceModel.Visible ->
@@ -194,6 +216,7 @@
                     isActivated = activationState is ActivationState.Active,
                     isSelected = isSelected,
                     useLongPress = quickAffordanceInteractor.useLongPress,
+                    isDimmed = isDimmed,
                 )
             is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index cf3a6da..cb68a82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -31,6 +31,7 @@
     val isActivated: Boolean = false,
     val isSelected: Boolean = false,
     val useLongPress: Boolean = false,
+    val isDimmed: Boolean = false,
 ) {
     data class OnClickedParameters(
         val configKey: String,
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e3c44d1..f61d135 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -200,28 +200,6 @@
     }
 
     /**
-     * Provides a logging buffer for logs related to the media tap-to-transfer chip on the sender
-     * device. See {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
-     */
-    @Provides
-    @SysUISingleton
-    @MediaTttSenderLogBuffer
-    public static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
-        return factory.create("MediaTttSender", 20);
-    }
-
-    /**
-     * Provides a logging buffer for logs related to the media tap-to-transfer chip on the receiver
-     * device. See {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
-     */
-    @Provides
-    @SysUISingleton
-    @MediaTttReceiverLogBuffer
-    public static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
-        return factory.create("MediaTttReceiver", 20);
-    }
-
-    /**
      * Provides a logging buffer for logs related to the media mute-await connections. See
      * {@link com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager}.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index bb833df..9ae4577 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -17,8 +17,7 @@
 package com.android.systemui.media.dagger;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.log.dagger.MediaTttReceiverLogBuffer;
-import com.android.systemui.log.dagger.MediaTttSenderLogBuffer;
+import com.android.systemui.log.LogBufferFactory;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
 import com.android.systemui.media.controls.ui.MediaHost;
@@ -29,12 +28,9 @@
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger;
-import com.android.systemui.media.taptotransfer.receiver.ChipReceiverInfo;
-import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger;
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger;
+import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer;
 import com.android.systemui.plugins.log.LogBuffer;
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo;
 
 import java.util.Optional;
 
@@ -94,22 +90,22 @@
         return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
     }
 
+    /** Provides a logging buffer related to the media tap-to-transfer chip on the sender device. */
     @Provides
     @SysUISingleton
-    @MediaTttSenderLogger
-    static MediaTttLogger<ChipbarInfo> providesMediaTttSenderLogger(
-            @MediaTttSenderLogBuffer LogBuffer buffer
-    ) {
-        return new MediaTttLogger<>("Sender", buffer);
+    @MediaTttSenderLogBuffer
+    static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
+        return factory.create("MediaTttSender", 30);
     }
 
+    /**
+     * Provides a logging buffer related to the media tap-to-transfer chip on the receiver device.
+     */
     @Provides
     @SysUISingleton
-    @MediaTttReceiverLogger
-    static MediaTttLogger<ChipReceiverInfo> providesMediaTttReceiverLogger(
-            @MediaTttReceiverLogBuffer LogBuffer buffer
-    ) {
-        return new MediaTttLogger<>("Receiver", buffer);
+    @MediaTttReceiverLogBuffer
+    static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
+        return factory.create("MediaTttReceiver", 20);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
deleted file mode 100644
index 8aef938..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.media.taptotransfer.common
-
-import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
-import com.android.systemui.temporarydisplay.TemporaryViewLogger
-
-/**
- * A logger for media tap-to-transfer events.
- *
- * @param deviceTypeTag the type of device triggering the logs -- "Sender" or "Receiver".
- *
- * TODO(b/245610654): We should de-couple the sender and receiver loggers, since they're vastly
- * different experiences.
- */
-class MediaTttLogger<T : TemporaryViewInfo>(
-    deviceTypeTag: String,
-    buffer: LogBuffer
-) : TemporaryViewLogger<T>(buffer, BASE_TAG + deviceTypeTag) {
-    /** Logs a change in the chip state for the given [mediaRouteId]. */
-    fun logStateChange(stateName: String, mediaRouteId: String, packageName: String?) {
-        buffer.log(
-            tag,
-            LogLevel.DEBUG,
-            {
-                str1 = stateName
-                str2 = mediaRouteId
-                str3 = packageName
-            },
-            { "State changed to $str1 for ID=$str2 package=$str3" }
-        )
-    }
-
-    /**
-     * Logs an error in trying to update to [displayState].
-     *
-     * [displayState] is either a [android.app.StatusBarManager.MediaTransferSenderState] or
-     * a [android.app.StatusBarManager.MediaTransferReceiverState].
-     */
-    fun logStateChangeError(displayState: Int) {
-        buffer.log(
-            tag,
-            LogLevel.ERROR,
-            { int1 = displayState },
-            { "Cannot display state=$int1; aborting" }
-        )
-    }
-
-    /**
-     * Logs an invalid sender state transition error in trying to update to [desiredState].
-     *
-     * @param currentState the previous state of the chip.
-     * @param desiredState the new state of the chip.
-     */
-    fun logInvalidStateTransitionError(
-        currentState: String,
-        desiredState: String
-    ) {
-        buffer.log(
-                tag,
-                LogLevel.ERROR,
-                {
-                    str1 = currentState
-                    str2 = desiredState
-                },
-                { "Cannot display state=$str2 after state=$str1; invalid transition" }
-        )
-    }
-
-    /** Logs that we couldn't find information for [packageName]. */
-    fun logPackageNotFound(packageName: String) {
-        buffer.log(
-            tag,
-            LogLevel.DEBUG,
-            { str1 = packageName },
-            { "Package $str1 could not be found" }
-        )
-    }
-
-    /**
-     * Logs that a removal request has been bypassed (ignored).
-     *
-     * @param removalReason the reason that the chip removal was requested.
-     * @param bypassReason the reason that the request was bypassed.
-     */
-    fun logRemovalBypass(removalReason: String, bypassReason: String) {
-        buffer.log(
-            tag,
-            LogLevel.DEBUG,
-            {
-                str1 = removalReason
-                str2 = bypassReason
-            },
-            { "Chip removal requested due to $str1; however, removal was ignored because $str2" })
-    }
-}
-
-private const val BASE_TAG = "MediaTtt"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
new file mode 100644
index 0000000..0e839c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.media.taptotransfer.common
+
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+
+/** A helper for logging media tap-to-transfer events. */
+object MediaTttLoggerUtils {
+    fun logStateChange(
+        buffer: LogBuffer,
+        tag: String,
+        stateName: String,
+        mediaRouteId: String,
+        packageName: String?,
+    ) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            {
+                str1 = stateName
+                str2 = mediaRouteId
+                str3 = packageName
+            },
+            { "State changed to $str1 for ID=$str2 package=$str3" }
+        )
+    }
+
+    fun logStateChangeError(buffer: LogBuffer, tag: String, displayState: Int) {
+        buffer.log(
+            tag,
+            LogLevel.ERROR,
+            { int1 = displayState },
+            { "Cannot display state=$int1; aborting" }
+        )
+    }
+
+    fun logPackageNotFound(buffer: LogBuffer, tag: String, packageName: String) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            { str1 = packageName },
+            { "Package $str1 could not be found" }
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index 066c185..a3ae943 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.TintedIcon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 
 /** Utility methods for media tap-to-transfer. */
 class MediaTttUtils {
@@ -43,12 +42,13 @@
          * default name and icon if we can't find the app name/icon.
          *
          * @param appPackageName the package name of the app playing the media.
-         * @param logger the logger to use for any errors.
+         * @param onPackageNotFoundException a function run if a
+         * [PackageManager.NameNotFoundException] occurs.
          */
         fun getIconInfoFromPackageName(
             context: Context,
             appPackageName: String?,
-            logger: MediaTttLogger<out TemporaryViewInfo>
+            onPackageNotFoundException: () -> Unit,
         ): IconInfo {
             if (appPackageName != null) {
                 val packageManager = context.packageManager
@@ -70,7 +70,7 @@
                         isAppIcon = true
                     )
                 } catch (e: PackageManager.NameNotFoundException) {
-                    logger.logPackageNotFound(appPackageName)
+                    onPackageNotFoundException.invoke()
                 }
             }
             return IconInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 60dd5da..34bf74fa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -40,7 +40,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttIcon
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.media.taptotransfer.common.MediaTttUtils
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -65,7 +64,7 @@
 open class MediaTttChipControllerReceiver @Inject constructor(
         private val commandQueue: CommandQueue,
         context: Context,
-        @MediaTttReceiverLogger logger: MediaTttLogger<ChipReceiverInfo>,
+        logger: MediaTttReceiverLogger,
         windowManager: WindowManager,
         mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
@@ -79,7 +78,7 @@
         wakeLockBuilder: WakeLock.Builder,
         systemClock: SystemClock,
         private val rippleController: MediaTttReceiverRippleController,
-) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger<ChipReceiverInfo>>(
+) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
         context,
         logger,
         windowManager,
@@ -173,9 +172,10 @@
     }
 
     override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
-        var iconInfo = MediaTttUtils.getIconInfoFromPackageName(
-            context, newInfo.routeInfo.clientPackageName, logger
-        )
+        val packageName = newInfo.routeInfo.clientPackageName
+        var iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, packageName) {
+            logger.logPackageNotFound(packageName)
+        }
 
         if (newInfo.appNameOverride != null) {
             iconInfo = iconInfo.copy(
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
index 1570d43..67e464c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.receiver;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -26,8 +26,7 @@
 import javax.inject.Qualifier;
 
 /**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
+ * A {@link LogBuffer} for receiver logs.
  */
 @Qualifier
 @Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
index 54fc48d..b0c6257 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
@@ -13,14 +13,44 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.systemui.media.taptotransfer.receiver
 
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.temporarydisplay.TemporaryViewLogger
+import javax.inject.Inject
 
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttReceiverLogger
+/** A logger for all events related to the media tap-to-transfer receiver experience. */
+@SysUISingleton
+class MediaTttReceiverLogger
+@Inject
+constructor(
+    @MediaTttReceiverLogBuffer buffer: LogBuffer,
+) : TemporaryViewLogger<ChipReceiverInfo>(buffer, TAG) {
+
+    /** Logs a change in the chip state for the given [mediaRouteId]. */
+    fun logStateChange(
+        stateName: String,
+        mediaRouteId: String,
+        packageName: String?,
+    ) {
+        MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+    }
+
+    /** Logs an error in trying to update to [displayState]. */
+    fun logStateChangeError(@StatusBarManager.MediaTransferReceiverState displayState: Int) {
+        MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+    }
+
+    /** Logs that we couldn't find information for [packageName]. */
+    fun logPackageNotFound(packageName: String) {
+        MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+    }
+
+    companion object {
+        private const val TAG = "MediaTttReceiver"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 902a10a0..89ca5d3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -23,11 +23,12 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.CoreStartable
+import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.media.taptotransfer.common.MediaTttUtils
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -35,6 +36,7 @@
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
 import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
+import java.io.PrintWriter
 import javax.inject.Inject
 
 /**
@@ -48,14 +50,13 @@
     private val chipbarCoordinator: ChipbarCoordinator,
     private val commandQueue: CommandQueue,
     private val context: Context,
-    @MediaTttSenderLogger private val logger: MediaTttLogger<ChipbarInfo>,
+    private val dumpManager: DumpManager,
+    private val logger: MediaTttSenderLogger,
     private val mediaTttFlags: MediaTttFlags,
     private val uiEventLogger: MediaTttSenderUiEventLogger,
-) : CoreStartable {
+) : CoreStartable, Dumpable {
 
-    private var displayedState: ChipStateSender? = null
     // A map to store current chip state per id.
-    // TODO(b/265455911): Log whenever we add or remove from the store.
     private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
 
     private val commandQueueCallbacks =
@@ -76,6 +77,7 @@
     override fun start() {
         if (mediaTttFlags.isMediaTttEnabled()) {
             commandQueue.addCallback(commandQueueCallbacks)
+            dumpManager.registerNormalDumpable(this)
         }
     }
 
@@ -93,11 +95,11 @@
             return
         }
 
-        val currentState = stateMap[routeInfo.id]
-        if (!ChipStateSender.isValidStateTransition(currentState, chipState)) {
+        val currentStateForId: ChipStateSender? = stateMap[routeInfo.id]
+        if (!ChipStateSender.isValidStateTransition(currentStateForId, chipState)) {
             // ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
             logger.logInvalidStateTransitionError(
-                currentState = currentState?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
+                currentState = currentStateForId?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
                 chipState.name
             )
             return
@@ -105,30 +107,29 @@
         uiEventLogger.logSenderStateChange(chipState)
 
         if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
-            // No need to store the state since it is the default state
-            removeIdFromStore(routeInfo.id)
-            // Return early if we're not displaying a chip anyway
-            val currentDisplayedState = displayedState ?: return
+            // Return early if we're not displaying a chip for this ID anyway
+            if (currentStateForId == null) return
 
             val removalReason = ChipStateSender.FAR_FROM_RECEIVER.name
             if (
-                currentDisplayedState.transferStatus == TransferStatus.IN_PROGRESS ||
-                    currentDisplayedState.transferStatus == TransferStatus.SUCCEEDED
+                currentStateForId.transferStatus == TransferStatus.IN_PROGRESS ||
+                    currentStateForId.transferStatus == TransferStatus.SUCCEEDED
             ) {
                 // Don't remove the chip if we're in progress or succeeded, since the user should
                 // still be able to see the status of the transfer.
                 logger.logRemovalBypass(
                     removalReason,
-                    bypassReason = "transferStatus=${currentDisplayedState.transferStatus.name}"
+                    bypassReason = "transferStatus=${currentStateForId.transferStatus.name}"
                 )
                 return
             }
 
-            displayedState = null
+            // No need to store the state since it is the default state
+            removeIdFromStore(routeInfo.id, reason = removalReason)
             chipbarCoordinator.removeView(routeInfo.id, removalReason)
         } else {
             stateMap[routeInfo.id] = chipState
-            displayedState = chipState
+            logger.logStateMap(stateMap)
             chipbarCoordinator.registerListener(displayListener)
             chipbarCoordinator.displayView(
                 createChipbarInfo(
@@ -150,7 +151,7 @@
         routeInfo: MediaRoute2Info,
         undoCallback: IUndoMediaTransferCallback?,
         context: Context,
-        logger: MediaTttLogger<ChipbarInfo>,
+        logger: MediaTttSenderLogger,
     ): ChipbarInfo {
         val packageName = routeInfo.clientPackageName
         val otherDeviceName =
@@ -159,12 +160,14 @@
             } else {
                 routeInfo.name.toString()
             }
+        val icon =
+            MediaTttUtils.getIconInfoFromPackageName(context, packageName) {
+                logger.logPackageNotFound(packageName)
+            }
 
         return ChipbarInfo(
             // Display the app's icon as the start icon
-            startIcon =
-                MediaTttUtils.getIconInfoFromPackageName(context, packageName, logger)
-                    .toTintedIcon(),
+            startIcon = icon.toTintedIcon(),
             text = chipStateSender.getChipTextString(context, otherDeviceName),
             endItem =
                 when (chipStateSender.endItem) {
@@ -231,12 +234,19 @@
     }
 
     private val displayListener =
-        TemporaryViewDisplayController.Listener { id -> removeIdFromStore(id) }
+        TemporaryViewDisplayController.Listener { id, reason -> removeIdFromStore(id, reason) }
 
-    private fun removeIdFromStore(id: String) {
+    private fun removeIdFromStore(id: String, reason: String) {
+        logger.logStateMapRemoval(id, reason)
         stateMap.remove(id)
+        logger.logStateMap(stateMap)
         if (stateMap.isEmpty()) {
             chipbarCoordinator.unregisterListener(displayListener)
         }
     }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("Current sender states:")
+        pw.println(stateMap.toString())
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
index bf216c6..a262e97 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.sender;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -26,8 +26,7 @@
 import javax.inject.Qualifier;
 
 /**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
+ * A {@link LogBuffer} for sender logs.
  */
 @Qualifier
 @Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
index 4393af9..964a95b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
@@ -13,14 +13,102 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.systemui.media.taptotransfer.sender
 
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import javax.inject.Inject
 
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttSenderLogger
+/** A logger for all events related to the media tap-to-transfer sender experience. */
+@SysUISingleton
+class MediaTttSenderLogger
+@Inject
+constructor(
+    @MediaTttSenderLogBuffer private val buffer: LogBuffer,
+) {
+    /** Logs a change in the chip state for the given [mediaRouteId]. */
+    fun logStateChange(
+        stateName: String,
+        mediaRouteId: String,
+        packageName: String?,
+    ) {
+        MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+    }
+
+    /** Logs an error in trying to update to [displayState]. */
+    fun logStateChangeError(@StatusBarManager.MediaTransferSenderState displayState: Int) {
+        MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+    }
+
+    /** Logs that we couldn't find information for [packageName]. */
+    fun logPackageNotFound(packageName: String) {
+        MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+    }
+
+    /**
+     * Logs an invalid sender state transition error in trying to update to [desiredState].
+     *
+     * @param currentState the previous state of the chip.
+     * @param desiredState the new state of the chip.
+     */
+    fun logInvalidStateTransitionError(currentState: String, desiredState: String) {
+        buffer.log(
+            TAG,
+            LogLevel.ERROR,
+            {
+                str1 = currentState
+                str2 = desiredState
+            },
+            { "Cannot display state=$str2 after state=$str1; invalid transition" }
+        )
+    }
+
+    /**
+     * Logs that a removal request has been bypassed (ignored).
+     *
+     * @param removalReason the reason that the chip removal was requested.
+     * @param bypassReason the reason that the request was bypassed.
+     */
+    fun logRemovalBypass(removalReason: String, bypassReason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = removalReason
+                str2 = bypassReason
+            },
+            { "Chip removal requested due to $str1; however, removal was ignored because $str2" }
+        )
+    }
+
+    /** Logs the current contents of the state map. */
+    fun logStateMap(map: Map<String, ChipStateSender>) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { str1 = map.toString() },
+            { "Current sender states: $str1" }
+        )
+    }
+
+    /** Logs that [id] has been removed from the state map due to [reason]. */
+    fun logStateMapRemoval(id: String, reason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = id
+                str2 = reason
+            },
+            { "State removal: id=$str1 reason=$str2" }
+        )
+    }
+
+    companion object {
+        private const val TAG = "MediaTttSender"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index b48ea23..be93550 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -43,6 +43,7 @@
 import javax.inject.Inject
 
 private const val TAG = "AutoAddTracker"
+private const val DELIMITER = ","
 
 /**
  * Class to track tiles that have been auto-added
@@ -67,7 +68,7 @@
 
     @GuardedBy("autoAdded")
     private val autoAdded = ArraySet<String>()
-    private var restoredTiles: Set<String>? = null
+    private var restoredTiles: Map<String, AutoTile>? = null
 
     override val currentUserId: Int
         get() = userId
@@ -98,25 +99,26 @@
         when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
             Settings.Secure.QS_TILES -> {
                 restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
-                        ?.split(",")
-                        ?.toSet()
+                        ?.split(DELIMITER)
+                        ?.mapIndexed(::AutoTile)
+                        ?.associateBy(AutoTile::tileType)
                         ?: run {
                             Log.w(TAG, "Null restored tiles for user $userId")
-                            emptySet()
+                            emptyMap()
                         }
             }
             Settings.Secure.QS_AUTO_ADDED_TILES -> {
-                restoredTiles?.let { tiles ->
+                restoredTiles?.let { restoredTiles ->
                     val restoredAutoAdded = intent
                             .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
-                            ?.split(",")
+                            ?.split(DELIMITER)
                             ?: emptyList()
                     val autoAddedBeforeRestore = intent
                             .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
-                            ?.split(",")
+                            ?.split(DELIMITER)
                             ?: emptyList()
 
-                    val tilesToRemove = restoredAutoAdded.filter { it !in tiles }
+                    val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
                     if (tilesToRemove.isNotEmpty()) {
                         qsHost.removeTiles(tilesToRemove)
                     }
@@ -180,6 +182,9 @@
         registerBroadcastReceiver()
     }
 
+    fun getRestoredTilePosition(tile: String): Int =
+        restoredTiles?.get(tile)?.index ?: QSTileHost.POSITION_AT_END
+
     /**
      * Returns `true` if the tile has been auto-added before
      */
@@ -196,12 +201,12 @@
      */
     fun setTileAdded(tile: String) {
         val tiles = synchronized(autoAdded) {
-                if (autoAdded.add(tile)) {
-                    getTilesFromListLocked()
-                } else {
-                    null
-                }
+            if (autoAdded.add(tile)) {
+                getTilesFromListLocked()
+            } else {
+                null
             }
+        }
         tiles?.let { saveTiles(it) }
     }
 
@@ -222,7 +227,7 @@
     }
 
     private fun getTilesFromListLocked(): String {
-        return TextUtils.join(",", autoAdded)
+        return TextUtils.join(DELIMITER, autoAdded)
     }
 
     private fun saveTiles(tiles: String) {
@@ -245,7 +250,7 @@
 
     private fun getAdded(): Collection<String> {
         val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
-        return current?.split(",") ?: emptySet()
+        return current?.split(DELIMITER) ?: emptySet()
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -281,4 +286,6 @@
             )
         }
     }
+
+    private data class AutoTile(val index: Int, val tileType: String)
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 8867637..197232e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade
 
+import android.animation.Animator
 import android.annotation.IdRes
 import android.app.StatusBarManager
 import android.content.res.Configuration
@@ -45,7 +46,6 @@
 import com.android.systemui.qs.carrier.QSCarrierGroup
 import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
-import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
 import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
 import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -176,9 +176,13 @@
     var shadeExpandedFraction = -1f
         set(value) {
             if (field != value) {
+                val oldAlpha = header.alpha
                 header.alpha = ShadeInterpolation.getContentAlpha(value)
                 field = value
-                updateVisibility()
+                if ((oldAlpha == 0f && header.alpha > 0f) ||
+                        (oldAlpha > 0f && header.alpha == 0f)) {
+                    updateVisibility()
+                }
             }
         }
 
@@ -305,6 +309,8 @@
                 val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
                 v.pivotX = newPivot
                 v.pivotY = v.height.toFloat() / 2
+
+                qsCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
             }
         }
 
@@ -335,9 +341,28 @@
                 .setUpdateListener {
                     updateVisibility()
                 }
+                .setListener(endAnimationListener)
                 .start()
     }
 
+    private val endAnimationListener = object : Animator.AnimatorListener {
+        override fun onAnimationCancel(animation: Animator?) {
+            clearListeners()
+        }
+
+        override fun onAnimationEnd(animation: Animator?) {
+            clearListeners()
+        }
+
+        override fun onAnimationRepeat(animation: Animator?) {}
+
+        override fun onAnimationStart(animation: Animator?) {}
+
+        private fun clearListeners() {
+            header.animate().setListener(null).setUpdateListener(null)
+        }
+    }
+
     private fun loadConstraints() {
         if (header is MotionLayout) {
             // Use resources.getXml instead of passing the resource id due to bug b/205018300
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d11f9da..457d7ba 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2157,6 +2157,7 @@
         }
         ValueAnimator animator = createHeightAnimator(target, overshootAmount);
         if (expand) {
+            maybeVibrateOnOpening(true /* openingWithTouch */);
             if (expandBecauseOfFalsing && vel < 0) {
                 vel = 0;
             }
@@ -2167,6 +2168,7 @@
                 animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
             }
         } else {
+            mHasVibratedOnOpen = false;
             if (shouldUseDismissingAnimation()) {
                 if (vel == 0) {
                     animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
@@ -6375,7 +6377,7 @@
                         mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
                     }
                     addMovement(event);
-                    if (!isFullyCollapsed()) {
+                    if (!isFullyCollapsed() && !isOnKeyguard()) {
                         maybeVibrateOnOpening(true /* openingWithTouch */);
                     }
                     float h = y - mInitialExpandY;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 808638a..8436ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -16,27 +16,15 @@
 
 package com.android.systemui.statusbar.notification.dagger;
 
-import android.app.INotificationManager;
 import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutManager;
-import android.os.Handler;
-import android.view.accessibility.AccessibilityManager;
 
-import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeEventsModule;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -50,7 +38,6 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
 import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
@@ -72,22 +59,17 @@
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
-import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.wmshell.BubblesManager;
 
-import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import javax.inject.Provider;
 
 import dagger.Binds;
-import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -109,48 +91,6 @@
     @Binds
     StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl);
 
-    /** Provides an instance of {@link NotificationGutsManager} */
-    @SysUISingleton
-    @Provides
-    static NotificationGutsManager provideNotificationGutsManager(
-            Context context,
-            Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
-            @Main Handler mainHandler,
-            @Background Handler bgHandler,
-            AccessibilityManager accessibilityManager,
-            HighPriorityProvider highPriorityProvider,
-            INotificationManager notificationManager,
-            PeopleSpaceWidgetManager peopleSpaceWidgetManager,
-            LauncherApps launcherApps,
-            ShortcutManager shortcutManager,
-            ChannelEditorDialogController channelEditorDialogController,
-            UserContextProvider contextTracker,
-            AssistantFeedbackController assistantFeedbackController,
-            Optional<BubblesManager> bubblesManagerOptional,
-            UiEventLogger uiEventLogger,
-            OnUserInteractionCallback onUserInteractionCallback,
-            ShadeController shadeController) {
-        return new NotificationGutsManager(
-                context,
-                centralSurfacesOptionalLazy,
-                mainHandler,
-                bgHandler,
-                accessibilityManager,
-                highPriorityProvider,
-                notificationManager,
-                peopleSpaceWidgetManager,
-                launcherApps,
-                shortcutManager,
-                channelEditorDialogController,
-                contextTracker,
-                assistantFeedbackController,
-                bubblesManagerOptional,
-                uiEventLogger,
-                onUserInteractionCallback,
-                shadeController
-        );
-    }
-
     /** Provides an instance of {@link NotifGutsViewManager} */
     @Binds
     NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index ea12b82..37ff11d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -44,12 +44,10 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settingslib.notification.ConversationIconFactory;
-import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -65,28 +63,29 @@
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.wmshell.BubblesManager;
 
-import java.io.PrintWriter;
 import java.util.Optional;
 
+import javax.inject.Inject;
+
 import dagger.Lazy;
 
 /**
  * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
  * closing guts, and keeping track of the currently exposed notification guts.
  */
+@SysUISingleton
 public class NotificationGutsManager implements NotifGutsViewManager {
     private static final String TAG = "NotificationGutsManager";
 
     // Must match constant in Settings. Used to highlight preferences when linking to Settings.
     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
 
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final MetricsLogger mMetricsLogger;
     private final Context mContext;
     private final AccessibilityManager mAccessibilityManager;
     private final HighPriorityProvider mHighPriorityProvider;
@@ -94,12 +93,9 @@
     private final OnUserInteractionCallback mOnUserInteractionCallback;
 
     // Dependencies:
-    private final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    private final StatusBarStateController mStatusBarStateController =
-            Dependency.get(StatusBarStateController.class);
-    private final DeviceProvisionedController mDeviceProvisionedController =
-            Dependency.get(DeviceProvisionedController.class);
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+    private final StatusBarStateController mStatusBarStateController;
+    private final DeviceProvisionedController mDeviceProvisionedController;
     private final AssistantFeedbackController mAssistantFeedbackController;
 
     // which notification is currently being longpress-examined by the user
@@ -124,9 +120,7 @@
     private final ShadeController mShadeController;
     private NotifGutsViewListener mGutsListener;
 
-    /**
-     * Injected constructor. See {@link NotificationsModule}.
-     */
+    @Inject
     public NotificationGutsManager(Context context,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             @Main Handler mainHandler,
@@ -143,7 +137,11 @@
             Optional<BubblesManager> bubblesManagerOptional,
             UiEventLogger uiEventLogger,
             OnUserInteractionCallback onUserInteractionCallback,
-            ShadeController shadeController) {
+            ShadeController shadeController,
+            NotificationLockscreenUserManager notificationLockscreenUserManager,
+            StatusBarStateController statusBarStateController,
+            DeviceProvisionedController deviceProvisionedController,
+            MetricsLogger metricsLogger) {
         mContext = context;
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
         mMainHandler = mainHandler;
@@ -161,6 +159,10 @@
         mUiEventLogger = uiEventLogger;
         mOnUserInteractionCallback = onUserInteractionCallback;
         mShadeController = shadeController;
+        mLockscreenUserManager = notificationLockscreenUserManager;
+        mStatusBarStateController = statusBarStateController;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mMetricsLogger = metricsLogger;
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -372,7 +374,8 @@
                 mDeviceProvisionedController.isDeviceProvisioned(),
                 row.getIsNonblockable(),
                 mHighPriorityProvider.isHighPriority(row.getEntry()),
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index ea0060a..8a50f2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -204,10 +204,11 @@
             boolean isDeviceProvisioned,
             boolean isNonblockable,
             boolean wasShownHighPriority,
-            AssistantFeedbackController assistantFeedbackController)
+            AssistantFeedbackController assistantFeedbackController,
+            MetricsLogger metricsLogger)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
-        mMetricsLogger = Dependency.get(MetricsLogger.class);
+        mMetricsLogger = metricsLogger;
         mOnUserInteractionCallback = onUserInteractionCallback;
         mChannelEditorDialogController = channelEditorDialogController;
         mAssistantFeedbackController = assistantFeedbackController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 149ec54..8b1a02b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -279,7 +279,8 @@
                 public void onManagedProfileChanged() {
                     if (mManagedProfileController.hasActiveProfile()) {
                         if (mAutoTracker.isAdded(WORK)) return;
-                        mHost.addTile(WORK);
+                        final int position = mAutoTracker.getRestoredTilePosition(WORK);
+                        mHost.addTile(WORK, position);
                         mAutoTracker.setTileAdded(WORK);
                     } else {
                         if (!mAutoTracker.isAdded(WORK)) return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 510482d..39ad31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -53,7 +53,7 @@
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
-import com.android.systemui.util.kotlin.pairwiseBy
+import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.settings.GlobalSettings
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -66,10 +66,10 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
@@ -287,22 +287,13 @@
      */
     @SuppressLint("MissingPermission")
     override val activeSubChangedInGroupEvent =
-        flow {
-                activeMobileDataSubscriptionId.pairwiseBy { prevVal: Int, newVal: Int ->
-                    if (!defaultMobileNetworkConnectivity.value.isValidated) {
-                        return@pairwiseBy
-                    }
-                    val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)
-                    val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)
+        activeMobileDataSubscriptionId
+            .pairwise()
+            .mapNotNull { (prevVal: Int, newVal: Int) ->
+                val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)?.groupUuid
+                val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)?.groupUuid
 
-                    if (prevSub == null || nextSub == null) {
-                        return@pairwiseBy
-                    }
-
-                    if (prevSub.groupUuid != null && prevSub.groupUuid == nextSub.groupUuid) {
-                        emit(Unit)
-                    }
-                }
+                if (prevSub != null && prevSub == nextSub) Unit else null
             }
             .flowOn(bgDispatcher)
 
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index df8d161..1065d33 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -327,7 +327,7 @@
         // appropriately.
         activeViews.remove(displayInfo)
         listeners.forEach {
-            it.onInfoPermanentlyRemoved(id)
+            it.onInfoPermanentlyRemoved(id, removalReason)
         }
 
         // No need to time the view out since it's already gone
@@ -393,7 +393,7 @@
             activeViews.remove(it)
             logger.logViewExpiration(it.info)
             listeners.forEach { listener ->
-                listener.onInfoPermanentlyRemoved(it.info.id)
+                listener.onInfoPermanentlyRemoved(it.info.id, REMOVAL_REASON_TIME_EXPIRED)
             }
         }
     }
@@ -457,7 +457,7 @@
          * Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
          * displayed again (unless another call to [updateView] is made).
          */
-        fun onInfoPermanentlyRemoved(id: String)
+        fun onInfoPermanentlyRemoved(id: String, reason: String)
     }
 
     /** A container for all the display-related state objects. */
@@ -494,6 +494,7 @@
 }
 
 private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
+private const val REMOVAL_REASON_TIME_EXPIRED = "TIMEOUT_EXPIRED_BEFORE_REDISPLAY"
 private const val MIN_REQUIRED_TIME_FOR_REDISPLAY = 1000
 
 private data class IconInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 9e0bbb7..46f13cc 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -206,31 +206,44 @@
     }
 
     override fun animateViewIn(view: ViewGroup) {
-        ViewHierarchyAnimator.animateAddition(
-            view.getInnerView(),
-            ViewHierarchyAnimator.Hotspot.TOP,
-            Interpolators.EMPHASIZED_DECELERATE,
-            duration = ANIMATION_IN_DURATION,
-            includeMargins = true,
-            includeFadeIn = true,
-            // We can only request focus once the animation finishes.
-            onAnimationEnd = {
-                maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
-            },
-        )
+        val onAnimationEnd = Runnable {
+            maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
+        }
+        val added =
+            ViewHierarchyAnimator.animateAddition(
+                view.getInnerView(),
+                ViewHierarchyAnimator.Hotspot.TOP,
+                Interpolators.EMPHASIZED_DECELERATE,
+                duration = ANIMATION_IN_DURATION,
+                includeMargins = true,
+                includeFadeIn = true,
+                // We can only request focus once the animation finishes.
+                onAnimationEnd = onAnimationEnd,
+            )
+        // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
+        // run it immediately.
+        if (!added) {
+            onAnimationEnd.run()
+        }
     }
 
     override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
         val innerView = view.getInnerView()
         innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
-        ViewHierarchyAnimator.animateRemoval(
-            innerView,
-            ViewHierarchyAnimator.Hotspot.TOP,
-            Interpolators.EMPHASIZED_ACCELERATE,
-            ANIMATION_OUT_DURATION,
-            includeMargins = true,
-            onAnimationEnd,
-        )
+        val removed =
+            ViewHierarchyAnimator.animateRemoval(
+                innerView,
+                ViewHierarchyAnimator.Hotspot.TOP,
+                Interpolators.EMPHASIZED_ACCELERATE,
+                ANIMATION_OUT_DURATION,
+                includeMargins = true,
+                onAnimationEnd,
+            )
+        // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
+        // run it immediately.
+        if (!removed) {
+            onAnimationEnd.run()
+        }
 
         updateGestureListening()
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 87dd6a4..eaef959 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -18,7 +18,6 @@
 
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
@@ -42,7 +41,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -96,7 +94,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.service.trust.TrustAgentService;
 import android.telephony.ServiceState;
@@ -239,8 +236,6 @@
     @Mock
     private UiEventLogger mUiEventLogger;
     @Mock
-    private PowerManager mPowerManager;
-    @Mock
     private GlobalSettings mGlobalSettings;
     private FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
     @Mock
@@ -834,6 +829,19 @@
     }
 
     @Test
+    public void doesNotTryToAuthenticateWhenKeyguardIsNotShowingButOccluded_whenAssistant() {
+        mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
+        mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+        verify(mFaceManager, never()).authenticate(any(),
+                any(),
+                any(),
+                any(),
+                anyInt(),
+                anyBoolean());
+    }
+
+    @Test
     public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
@@ -1872,28 +1880,6 @@
     }
 
     @Test
-    public void testFingerAcquired_wakesUpPowerManager() {
-        cleanupKeyguardUpdateMonitor();
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.kg_wake_on_acquire_start, true);
-        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
-        fingerprintAcquireStart();
-
-        verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
-    }
-
-    @Test
-    public void testFingerAcquired_doesNotWakeUpPowerManager() {
-        cleanupKeyguardUpdateMonitor();
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.kg_wake_on_acquire_start, false);
-        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
-        fingerprintAcquireStart();
-
-        verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
-    }
-
-    @Test
     public void testDreamingStopped_faceDoesNotRun() {
         mKeyguardUpdateMonitor.dispatchDreamingStopped();
         mTestableLooper.processAllMessages();
@@ -2374,11 +2360,6 @@
                 .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
     }
 
-    private void fingerprintAcquireStart() {
-        mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
-                .onAuthenticationAcquired(FINGERPRINT_ACQUIRED_START);
-    }
-
     private void deviceInPostureStateOpened() {
         mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED);
     }
@@ -2525,7 +2506,7 @@
                     mAuthController, mTelephonyListenerManager,
                     mInteractionJankMonitor, mLatencyTracker, mActiveUnlockConfig,
                     mKeyguardUpdateMonitorLogger, mUiEventLogger, () -> mSessionTracker,
-                    mPowerManager, mTrustManager, mSubscriptionManager, mUserManager,
+                    mTrustManager, mSubscriptionManager, mUserManager,
                     mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
                     mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
                     mFaceWakeUpTriggersConfig, mDevicePostureController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 022afdd..4b04b7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -234,7 +234,10 @@
     @Test
     fun `startButton - in preview mode - visible even when keyguard not showing`() =
         testScope.runTest {
-            underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
+            underTest.enablePreviewMode(
+                initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                shouldHighlightSelectedAffordance = true,
+            )
             repository.setKeyguardShowing(false)
             val latest = collectLastValue(underTest.startButton)
 
@@ -263,6 +266,7 @@
                         icon = icon,
                         canShowWhileLocked = false,
                         intent = Intent("action"),
+                        isSelected = true,
                     ),
                 configKey = configKey,
             )
@@ -270,6 +274,60 @@
         }
 
     @Test
+    fun `endButton - in higlighted preview mode - dimmed when other is selected`() =
+        testScope.runTest {
+            underTest.enablePreviewMode(
+                initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                shouldHighlightSelectedAffordance = true,
+            )
+            repository.setKeyguardShowing(false)
+            val startButton = collectLastValue(underTest.startButton)
+            val endButton = collectLastValue(underTest.endButton)
+
+            val icon: Icon = mock()
+            setUpQuickAffordanceModel(
+                position = KeyguardQuickAffordancePosition.BOTTOM_START,
+                testConfig =
+                    TestConfig(
+                        isVisible = true,
+                        isClickable = true,
+                        isActivated = true,
+                        icon = icon,
+                        canShowWhileLocked = false,
+                        intent = Intent("action"),
+                    ),
+            )
+            val configKey =
+                setUpQuickAffordanceModel(
+                    position = KeyguardQuickAffordancePosition.BOTTOM_END,
+                    testConfig =
+                        TestConfig(
+                            isVisible = true,
+                            isClickable = true,
+                            isActivated = true,
+                            icon = icon,
+                            canShowWhileLocked = false,
+                            intent = Intent("action"),
+                        ),
+                )
+
+            assertQuickAffordanceViewModel(
+                viewModel = endButton(),
+                testConfig =
+                    TestConfig(
+                        isVisible = true,
+                        isClickable = false,
+                        isActivated = true,
+                        icon = icon,
+                        canShowWhileLocked = false,
+                        intent = Intent("action"),
+                        isDimmed = true,
+                    ),
+                configKey = configKey,
+            )
+        }
+
+    @Test
     fun `endButton - present - visible model - do nothing on click`() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
@@ -377,7 +435,10 @@
     @Test
     fun `alpha - in preview mode - does not change`() =
         testScope.runTest {
-            underTest.enablePreviewMode(null)
+            underTest.enablePreviewMode(
+                initiallySelectedSlotId = null,
+                shouldHighlightSelectedAffordance = false,
+            )
             val value = collectLastValue(underTest.alpha)
 
             assertThat(value()).isEqualTo(1f)
@@ -639,6 +700,8 @@
         assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
         assertThat(viewModel.isClickable).isEqualTo(testConfig.isClickable)
         assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
+        assertThat(viewModel.isSelected).isEqualTo(testConfig.isSelected)
+        assertThat(viewModel.isDimmed).isEqualTo(testConfig.isDimmed)
         if (testConfig.isVisible) {
             assertThat(viewModel.icon).isEqualTo(testConfig.icon)
             viewModel.onClicked.invoke(
@@ -664,6 +727,8 @@
         val icon: Icon? = null,
         val canShowWhileLocked: Boolean = false,
         val intent: Intent? = null,
+        val isSelected: Boolean = false,
+        val isDimmed: Boolean = false,
     ) {
         init {
             check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
similarity index 70%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
index 0e7bf8d..8da1c64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -31,16 +30,15 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttLoggerUtilsTest : SysuiTestCase() {
 
     private lateinit var buffer: LogBuffer
-    private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
 
     @Before
-    fun setUp () {
-        buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-            .create("buffer", 10)
-        logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+    fun setUp() {
+        buffer =
+            LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+                .create("buffer", 10)
     }
 
     @Test
@@ -49,35 +47,33 @@
         val id = "test id"
         val packageName = "this.is.a.package"
 
-        logger.logStateChange(stateName, id, packageName)
+        MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, id, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(DEVICE_TYPE_TAG)
+        assertThat(actualString).contains(TAG)
         assertThat(actualString).contains(stateName)
         assertThat(actualString).contains(id)
         assertThat(actualString).contains(packageName)
     }
 
     @Test
-    fun logPackageNotFound_bufferHasPackageName() {
-        val packageName = "this.is.a.package"
-
-        logger.logPackageNotFound(packageName)
+    fun logStateChangeError_hasState() {
+        MediaTttLoggerUtils.logStateChangeError(buffer, TAG, 3456)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(packageName)
+        assertThat(actualString).contains(TAG)
+        assertThat(actualString).contains("3456")
     }
 
     @Test
-    fun logRemovalBypass_bufferHasReasons() {
-        val removalReason = "fakeRemovalReason"
-        val bypassReason = "fakeBypassReason"
+    fun logPackageNotFound_bufferHasPackageName() {
+        val packageName = "this.is.a.package"
 
-        logger.logRemovalBypass(removalReason, bypassReason)
+        MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(removalReason)
-        assertThat(actualString).contains(bypassReason)
+        assertThat(actualString).contains(TAG)
+        assertThat(actualString).contains(packageName)
     }
 
     private fun getStringFromBuffer(): String {
@@ -87,4 +83,4 @@
     }
 }
 
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
+private const val TAG = "TEST TAG"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 561867f..8055b98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -41,7 +40,6 @@
     private lateinit var appIconFromPackageName: Drawable
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var applicationInfo: ApplicationInfo
-    @Mock private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
 
     @Before
     fun setUp() {
@@ -67,8 +65,7 @@
 
     @Test
     fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
-        val iconInfo =
-            MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null, logger)
+        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null) {}
 
         assertThat(iconInfo.isAppIcon).isFalse()
         assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -77,8 +74,19 @@
     }
 
     @Test
+    fun getIconInfoFromPackageName_nullPackageName_exceptionFnNotTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null) {
+            exceptionTriggered = true
+        }
+
+        assertThat(exceptionTriggered).isFalse()
+    }
+
+    @Test
     fun getIconInfoFromPackageName_invalidPackageName_returnsDefault() {
-        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName", logger)
+        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName") {}
 
         assertThat(iconInfo.isAppIcon).isFalse()
         assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -87,8 +95,19 @@
     }
 
     @Test
+    fun getIconInfoFromPackageName_invalidPackageName_exceptionFnTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = "fakePackageName") {
+            exceptionTriggered = true
+        }
+
+        assertThat(exceptionTriggered).isTrue()
+    }
+
+    @Test
     fun getIconInfoFromPackageName_validPackageName_returnsAppInfo() {
-        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, logger)
+        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME) {}
 
         assertThat(iconInfo.isAppIcon).isTrue()
         assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName))
@@ -96,6 +115,17 @@
     }
 
     @Test
+    fun getIconInfoFromPackageName_validPackageName_exceptionFnNotTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME) {
+            exceptionTriggered = true
+        }
+
+        assertThat(exceptionTriggered).isFalse()
+    }
+
+    @Test
     fun iconInfo_toTintedIcon_loaded() {
         val contentDescription = ContentDescription.Loaded("test")
         val drawable = context.getDrawable(R.drawable.ic_cake)!!
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index b3e621e..bd042c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -24,7 +24,6 @@
 import android.view.accessibility.AccessibilityManager
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -35,7 +34,7 @@
 class FakeMediaTttChipControllerReceiver(
     commandQueue: CommandQueue,
     context: Context,
-    logger: MediaTttLogger<ChipReceiverInfo>,
+    logger: MediaTttReceiverLogger,
     windowManager: WindowManager,
     mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 5e40898..dba2da7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -68,7 +67,7 @@
     @Mock
     private lateinit var applicationInfo: ApplicationInfo
     @Mock
-    private lateinit var logger: MediaTttLogger<ChipReceiverInfo>
+    private lateinit var logger: MediaTttReceiverLogger
     @Mock
     private lateinit var accessibilityManager: AccessibilityManager
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
index 0e7bf8d..95df484 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.receiver
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -31,16 +30,17 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttReceiverLoggerTest : SysuiTestCase() {
 
     private lateinit var buffer: LogBuffer
-    private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+    private lateinit var logger: MediaTttReceiverLogger
 
     @Before
-    fun setUp () {
-        buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-            .create("buffer", 10)
-        logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+    fun setUp() {
+        buffer =
+            LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+                .create("buffer", 10)
+        logger = MediaTttReceiverLogger(buffer)
     }
 
     @Test
@@ -52,13 +52,20 @@
         logger.logStateChange(stateName, id, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(DEVICE_TYPE_TAG)
         assertThat(actualString).contains(stateName)
         assertThat(actualString).contains(id)
         assertThat(actualString).contains(packageName)
     }
 
     @Test
+    fun logStateChangeError_hasState() {
+        logger.logStateChangeError(3456)
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("3456")
+    }
+
+    @Test
     fun logPackageNotFound_bufferHasPackageName() {
         val packageName = "this.is.a.package"
 
@@ -68,23 +75,9 @@
         assertThat(actualString).contains(packageName)
     }
 
-    @Test
-    fun logRemovalBypass_bufferHasReasons() {
-        val removalReason = "fakeRemovalReason"
-        val bypassReason = "fakeBypassReason"
-
-        logger.logRemovalBypass(removalReason, bypassReason)
-
-        val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(removalReason)
-        assertThat(actualString).contains(bypassReason)
-    }
-
     private fun getStringFromBuffer(): String {
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
         return stringWriter.toString()
     }
 }
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 54d4460..c63ca3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -40,16 +40,13 @@
 import com.android.systemui.common.shared.model.Text.Companion.loadText
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
 import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
-import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
 import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -92,7 +89,7 @@
     @Mock private lateinit var falsingManager: FalsingManager
     @Mock private lateinit var falsingCollector: FalsingCollector
     @Mock private lateinit var chipbarLogger: ChipbarLogger
-    @Mock private lateinit var logger: MediaTttLogger<ChipbarInfo>
+    @Mock private lateinit var logger: MediaTttSenderLogger
     @Mock private lateinit var mediaTttFlags: MediaTttFlags
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var powerManager: PowerManager
@@ -139,7 +136,7 @@
         uiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
 
         chipbarCoordinator =
-            FakeChipbarCoordinator(
+            ChipbarCoordinator(
                 context,
                 chipbarLogger,
                 windowManager,
@@ -163,6 +160,7 @@
                 chipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -181,6 +179,7 @@
                 chipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -542,6 +541,7 @@
         val viewCaptor = ArgumentCaptor.forClass(View::class.java)
         verify(windowManager).addView(viewCaptor.capture(), any())
         verify(windowManager).removeView(viewCaptor.value)
+        verify(logger).logStateMapRemoval(eq(DEFAULT_ID), any())
     }
 
     @Test
@@ -741,6 +741,99 @@
         verify(windowManager, never()).addView(any(), any())
     }
 
+    /** Regression test for b/266217596. */
+    @Test
+    fun toReceiver_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+            routeInfo,
+            null,
+        )
+
+        // WHEN a FAR command comes in
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+            routeInfo,
+            null,
+        )
+
+        // THEN it is ignored and the chipbar is stilled displayed
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+        verify(windowManager, never()).removeView(any())
+
+        // WHEN a SUCCEEDED command comes in
+        val succeededRouteInfo =
+            MediaRoute2Info.Builder(DEFAULT_ID, "Tablet Succeeded")
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+            succeededRouteInfo,
+            /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+                override fun onUndoTriggered() {}
+            },
+        )
+
+        // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+        // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(
+                ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.getExpectedStateText(
+                    "Tablet Succeeded"
+                )
+            )
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+        assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+    }
+
+    /** Regression test for b/266217596. */
+    @Test
+    fun toThisDevice_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            routeInfo,
+            null,
+        )
+
+        // WHEN a FAR command comes in
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+            routeInfo,
+            null,
+        )
+
+        // THEN it is ignored and the chipbar is stilled displayed
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+        verify(windowManager, never()).removeView(any())
+
+        // WHEN a SUCCEEDED command comes in
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            routeInfo,
+            /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+                override fun onUndoTriggered() {}
+            },
+        )
+
+        // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+        // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(
+                ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.getExpectedStateText(
+                    "Tablet Succeeded"
+                )
+            )
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+        assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+    }
+
     @Test
     fun receivesNewStateFromCommandQueue_isLogged() {
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
@@ -934,6 +1027,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -960,6 +1054,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -977,9 +1072,10 @@
         verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
 
         // WHEN the listener is notified that the view has been removed
-        listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID)
+        listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID, "reason")
 
         // THEN the media coordinator unregisters the listener
+        verify(logger).logStateMapRemoval(DEFAULT_ID, "reason")
         verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
     }
 
@@ -991,6 +1087,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -1008,7 +1105,7 @@
         verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
 
         // WHEN the listener is notified that a different view has been removed
-        listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId")
+        listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId", "reason")
 
         // THEN the media coordinator doesn't unregister the listener
         verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
@@ -1022,6 +1119,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -1057,6 +1155,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -1086,10 +1185,173 @@
         verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
 
         // THEN one of them is removed
-        listenerCaptor.value.onInfoPermanentlyRemoved("route1")
+        listenerCaptor.value.onInfoPermanentlyRemoved("route1", "reason")
 
         // THEN the media coordinator doesn't unregister the listener (since route2 is still active)
         verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
+        verify(logger).logStateMapRemoval("route1", "reason")
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun twoIdsDisplayed_oldIdIsFar_viewStillDisplayed() {
+        // WHEN there are two different media transfers with different IDs
+        val route1 =
+            MediaRoute2Info.Builder("route1", OTHER_DEVICE_NAME)
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+            route1,
+            null,
+        )
+        verify(windowManager).addView(any(), any())
+        reset(windowManager)
+
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+            MediaRoute2Info.Builder("route2", "Route 2 name")
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build(),
+            null,
+        )
+        val newView = getChipbarView()
+
+        // WHEN there's a FAR event for the earlier one
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+            route1,
+            null,
+        )
+
+        // THEN it's ignored and the more recent one is still displayed
+        assertThat(newView.getChipText())
+            .isEqualTo(
+                ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText("Route 2 name")
+            )
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToEnd() {
+        displayReceiverTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show ALMOST_CLOSE_TO_END
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+            routeInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.getExpectedStateText())
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayReceiverTriggered() {
+        displayReceiverTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show RECEIVER_TRIGGERED
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+            routeInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToStart() {
+        displayThisDeviceTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show ALMOST_CLOSE_TO_START
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+            routeInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText())
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayThisDeviceTriggered() {
+        displayThisDeviceTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show THIS_DEVICE_TRIGGERED
+        val newRouteInfo =
+            MediaRoute2Info.Builder(DEFAULT_ID, "New Name")
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            newRouteInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(
+                ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText("New Name")
+            )
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
     }
 
     private fun getChipbarView(): ViewGroup {
@@ -1109,8 +1371,10 @@
 
     private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
 
-    private fun ChipStateSender.getExpectedStateText(): String? {
-        return this.getChipTextString(context, OTHER_DEVICE_NAME).loadText(context)
+    private fun ChipStateSender.getExpectedStateText(
+        otherDeviceName: String = OTHER_DEVICE_NAME,
+    ): String? {
+        return this.getChipTextString(context, otherDeviceName).loadText(context)
     }
 
     // display receiver triggered state helper method to make sure we start from a valid state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
similarity index 62%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
index 0e7bf8d..0033757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.sender
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -31,16 +30,17 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttSenderLoggerTest : SysuiTestCase() {
 
     private lateinit var buffer: LogBuffer
-    private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+    private lateinit var logger: MediaTttSenderLogger
 
     @Before
-    fun setUp () {
-        buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-            .create("buffer", 10)
-        logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+    fun setUp() {
+        buffer =
+            LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+                .create("buffer", 10)
+        logger = MediaTttSenderLogger(buffer)
     }
 
     @Test
@@ -52,13 +52,20 @@
         logger.logStateChange(stateName, id, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(DEVICE_TYPE_TAG)
         assertThat(actualString).contains(stateName)
         assertThat(actualString).contains(id)
         assertThat(actualString).contains(packageName)
     }
 
     @Test
+    fun logStateChangeError_hasState() {
+        logger.logStateChangeError(3456)
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("3456")
+    }
+
+    @Test
     fun logPackageNotFound_bufferHasPackageName() {
         val packageName = "this.is.a.package"
 
@@ -80,11 +87,35 @@
         assertThat(actualString).contains(bypassReason)
     }
 
+    @Test
+    fun logStateMap_bufferHasInfo() {
+        val map =
+            mapOf(
+                "123" to ChipStateSender.ALMOST_CLOSE_TO_START_CAST,
+                "456" to ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            )
+
+        logger.logStateMap(map)
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("123")
+        assertThat(actualString).contains(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
+        assertThat(actualString).contains("456")
+        assertThat(actualString).contains(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
+    }
+
+    @Test
+    fun logStateMapRemoval_bufferHasInfo() {
+        logger.logStateMapRemoval("456", "testReason")
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("456")
+        assertThat(actualString).contains("testReason")
+    }
+
     private fun getStringFromBuffer(): String {
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
         return stringWriter.toString()
     }
 }
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 43fb1bd..dee1cc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -59,6 +59,7 @@
 @SmallTest
 public class AutoAddTrackerTest extends SysuiTestCase {
 
+    private static final int END_POSITION = -1;
     private static final int USER = 0;
 
     @Mock
@@ -142,6 +143,29 @@
     }
 
     @Test
+    public void testRestoredTilePositionPreserved() {
+        verify(mBroadcastDispatcher).registerReceiver(
+                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+        String restoredTiles = "saver,internet,work,cast";
+        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        assertEquals(2, mAutoTracker.getRestoredTilePosition("work"));
+    }
+
+    @Test
+    public void testNoRestoredTileReturnsEndPosition() {
+        verify(mBroadcastDispatcher).registerReceiver(
+                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, null);
+
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        assertEquals(END_POSITION, mAutoTracker.getRestoredTilePosition("work"));
+    }
+
+    @Test
     public void testBroadcastReceiverRegistered() {
         verify(mBroadcastDispatcher).registerReceiver(
                 any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index ed9baf5b1..3706859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -90,7 +90,7 @@
     fun testEdgeElementsAlignedWithEdgeOrGuide_qs() {
         with(qsConstraint) {
             assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID)
-            assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+            assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f)
 
             assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
             assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f)
@@ -342,7 +342,6 @@
                 R.id.clock to "clock",
                 R.id.date to "date",
                 R.id.privacy_container to "privacy",
-                R.id.carrier_group to "carriers",
         )
         views.forEach { (id, name) ->
             assertWithMessage("$name has 0 height in qqs")
@@ -361,7 +360,6 @@
         val views = mapOf(
                 R.id.clock to "clock",
                 R.id.privacy_container to "privacy",
-                R.id.carrier_group to "carriers",
         )
         views.forEach { (id, name) ->
             expect.withMessage("$name changes height")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index f580f5e..91fef1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -692,6 +692,19 @@
         assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
     }
 
+    @Test
+    fun `carrier left padding is set when clock layout changes`() {
+        val width = 200
+        whenever(clock.width).thenReturn(width)
+        whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml
+        val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java)
+
+        verify(clock).addOnLayoutChangeListener(capture(captor))
+        captor.value.onLayoutChange(clock, 0, 0, width, 0, 0, 0, 0, 0)
+
+        verify(carrierGroup).setPaddingRelative(514, 0, 0, 0)
+    }
+
     private fun View.executeLayoutChange(
             left: Int,
             top: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index b568122..2bf2a81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.shade
 
+import android.animation.Animator
 import android.animation.ValueAnimator
 import android.app.StatusBarManager
 import android.content.Context
@@ -45,8 +46,8 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -293,6 +294,34 @@
     }
 
     @Test
+    fun animatorListenersClearedAtEnd() {
+        val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+        whenever(view.animate()).thenReturn(animator)
+
+        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
+        val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+        verify(animator).setListener(capture(listenerCaptor))
+
+        listenerCaptor.value.onAnimationEnd(mock())
+        verify(animator).setListener(null)
+        verify(animator).setUpdateListener(null)
+    }
+
+    @Test
+    fun animatorListenersClearedOnCancel() {
+        val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+        whenever(view.animate()).thenReturn(animator)
+
+        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
+        val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+        verify(animator).setListener(capture(listenerCaptor))
+
+        listenerCaptor.value.onAnimationCancel(mock())
+        verify(animator).setListener(null)
+        verify(animator).setUpdateListener(null)
+    }
+
+    @Test
     fun demoMode_attachDemoMode() {
         val cb = argumentCaptor<DemoMode>()
         verify(demoModeController).addCallback(capture(cb))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index b6a1bb3..d7ac6b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -66,9 +66,9 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -133,18 +133,13 @@
     @Mock private ShadeController mShadeController;
     @Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
     @Mock private AssistantFeedbackController mAssistantFeedbackController;
+    @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+    @Mock private StatusBarStateController mStatusBarStateController;
 
     @Before
     public void setUp() {
         mTestableLooper = TestableLooper.get(this);
         allowTestableLooperAsMainThread();
-        mDependency.injectTestDependency(DeviceProvisionedController.class,
-                mDeviceProvisionedController);
-        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mDependency.injectTestDependency(
-                OnUserInteractionCallback.class,
-                mOnUserInteractionCallback);
-        mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
         mHandler = Handler.createAsync(mTestableLooper.getLooper());
         mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
@@ -155,7 +150,11 @@
                 mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
                 mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
                 Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
-                mShadeController);
+                mShadeController,
+                mNotificationLockscreenUserManager,
+                mStatusBarStateController,
+                mDeviceProvisionedController,
+                mMetricsLogger);
         mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
                 mOnSettingsClickListener);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -372,7 +371,8 @@
                 eq(false),
                 eq(false),
                 eq(true), /* wasShownHighPriority */
-                eq(mAssistantFeedbackController));
+                eq(mAssistantFeedbackController),
+                any(MetricsLogger.class));
     }
 
     @Test
@@ -406,7 +406,8 @@
                 eq(true),
                 eq(false),
                 eq(false), /* wasShownHighPriority */
-                eq(mAssistantFeedbackController));
+                eq(mAssistantFeedbackController),
+                any(MetricsLogger.class));
     }
 
     @Test
@@ -438,7 +439,8 @@
                 eq(false),
                 eq(false),
                 eq(false), /* wasShownHighPriority */
-                eq(mAssistantFeedbackController));
+                eq(mAssistantFeedbackController),
+                any(MetricsLogger.class));
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 80a81a5..8dd0488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -130,7 +130,6 @@
         mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
 
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
-        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -194,7 +193,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -220,7 +220,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkg_icon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -242,7 +243,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
     }
@@ -273,7 +275,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
         assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -296,7 +299,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
     }
@@ -324,7 +328,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -347,7 +352,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -369,7 +375,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
@@ -395,7 +402,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -417,7 +425,8 @@
                 true,
                 true,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -443,7 +452,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -468,7 +478,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -493,7 +504,8 @@
                 false,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -515,7 +527,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         mNotificationInfo.bindNotification(
                 mMockPackageManager,
                 mMockINotificationManager,
@@ -531,7 +544,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -556,7 +570,8 @@
                 true,
                 true,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.info).performClick();
         // Verify that listener was triggered.
@@ -582,7 +597,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView channelNameView =
                 mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, channelNameView.getVisibility());
@@ -606,7 +622,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(GONE, mNotificationInfo.findViewById(
                 R.id.interruptiveness_settings).getVisibility());
         assertEquals(VISIBLE, mNotificationInfo.findViewById(
@@ -630,7 +647,8 @@
                 true,
                 true,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -673,7 +691,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_call_desc),
@@ -716,7 +735,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(GONE,
                 mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility());
         assertEquals(VISIBLE,
@@ -743,7 +763,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
     }
@@ -765,7 +786,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
         assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
     }
@@ -789,7 +811,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertTrue(mNotificationInfo.findViewById(R.id.automatic).isSelected());
     }
 
@@ -810,7 +833,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected());
     }
 
@@ -831,7 +855,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected());
     }
 
@@ -852,7 +877,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -875,7 +901,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(1, mUiEventLogger.numLogs());
         assertEquals(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN.getId(),
                 mUiEventLogger.eventId(0));
@@ -899,7 +926,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mTestableLooper.processAllMessages();
@@ -926,7 +954,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mTestableLooper.processAllMessages();
@@ -953,7 +982,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.automatic).performClick();
         mTestableLooper.processAllMessages();
@@ -981,7 +1011,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -1008,7 +1039,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -1043,7 +1075,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -1071,7 +1104,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1112,7 +1146,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1149,7 +1184,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.automatic).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1181,7 +1217,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1217,7 +1254,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1255,7 +1293,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.handleCloseControls(false, false);
@@ -1286,7 +1325,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1324,7 +1364,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1353,7 +1394,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1384,7 +1426,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1419,7 +1462,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1452,7 +1496,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1485,7 +1530,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
 
@@ -1511,7 +1557,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertFalse(mNotificationInfo.willBeRemoved());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 091bb54..8cfcc07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -510,6 +510,7 @@
     @Test
     public void managedProfileAdded_tileAdded() {
         when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
+        when(mAutoAddTracker.getRestoredTilePosition(eq("work"))).thenReturn(2);
         mAutoTileManager = createAutoTileManager(mContext);
         Mockito.doAnswer((Answer<Object>) invocation -> {
             mManagedProfileCallback = invocation.getArgument(0);
@@ -520,7 +521,7 @@
 
         mManagedProfileCallback.onManagedProfileChanged();
 
-        verify(mQsTileHost, times(1)).addTile(eq("work"));
+        verify(mQsTileHost, times(1)).addTile(eq("work"), eq(2));
         verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index ae390a0..db8172a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -22,6 +22,7 @@
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.os.ParcelUuid
 import android.provider.Settings
 import android.telephony.CarrierConfigManager
 import android.telephony.SubscriptionInfo
@@ -50,6 +51,7 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -104,6 +106,17 @@
             mock<TableLogBuffer>()
         }
 
+        // For convenience, set up the subscription info callbacks
+        whenever(subscriptionManager.getActiveSubscriptionInfo(anyInt())).thenAnswer { invocation ->
+            when (invocation.getArgument(0) as Int) {
+                1 -> SUB_1
+                2 -> SUB_2
+                3 -> SUB_3
+                4 -> SUB_4
+                else -> null
+            }
+        }
+
         wifiRepository = FakeWifiRepository()
 
         connectionFactory =
@@ -686,6 +699,38 @@
             job.cancel()
         }
 
+    @Test
+    fun `active data change - in same group - emits unit`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Unit? = null
+            val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_4_ID_GROUPED)
+
+            assertThat(latest).isEqualTo(Unit)
+
+            job.cancel()
+        }
+
+    @Test
+    fun `active data change - not in same group - does not emit`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Unit? = null
+            val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_1_ID)
+
+            assertThat(latest).isEqualTo(null)
+
+            job.cancel()
+        }
+
     private fun createCapabilities(connected: Boolean, validated: Boolean): NetworkCapabilities =
         mock<NetworkCapabilities>().also {
             whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(connected)
@@ -719,19 +764,50 @@
 
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
+
+        // Subscription 1
         private const val SUB_1_ID = 1
         private val SUB_1 =
-            mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_1_ID)
+                whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+            }
         private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
 
+        // Subscription 2
         private const val SUB_2_ID = 2
         private val SUB_2 =
-            mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_2_ID)
+                whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+            }
         private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
 
+        // Subs 3 and 4 are considered to be in the same group ------------------------------------
+        private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID())
+
+        // Subscription 3
+        private const val SUB_3_ID_GROUPED = 3
+        private val SUB_3 =
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED)
+                whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+            }
+
+        // Subscription 4
+        private const val SUB_4_ID_GROUPED = 4
+        private val SUB_4 =
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED)
+                whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+            }
+
+        // Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
         private const val NET_ID = 123
         private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
 
+        // Carrier merged subscription
         private const val SUB_CM_ID = 5
         private val SUB_CM =
             mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 45f7df3..c7c6b94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -1172,7 +1172,7 @@
 
     inner class Listener : TemporaryViewDisplayController.Listener {
         val permanentlyRemovedIds = mutableListOf<String>()
-        override fun onInfoPermanentlyRemoved(id: String) {
+        override fun onInfoPermanentlyRemoved(id: String, reason: String) {
             permanentlyRemovedIds.add(id)
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 45eb1f9..dd04ac4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -66,7 +66,7 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class ChipbarCoordinatorTest : SysuiTestCase() {
-    private lateinit var underTest: FakeChipbarCoordinator
+    private lateinit var underTest: ChipbarCoordinator
 
     @Mock private lateinit var logger: ChipbarLogger
     @Mock private lateinit var accessibilityManager: AccessibilityManager
@@ -100,7 +100,7 @@
         uiEventLoggerFake = UiEventLoggerFake()
 
         underTest =
-            FakeChipbarCoordinator(
+            ChipbarCoordinator(
                 context,
                 logger,
                 windowManager,
@@ -436,6 +436,23 @@
         verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
     }
 
+    /** Regression test for b/266209420. */
+    @Test
+    fun displayViewThenImmediateRemoval_viewStillRemoved() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+                Text.Loaded("title text"),
+                endItem = ChipbarEndItem.Error,
+            ),
+        )
+        val chipbarView = getChipbarView()
+
+        underTest.removeView(DEVICE_ID, "test reason")
+
+        verify(windowManager).removeView(chipbarView)
+    }
+
     @Test
     fun swipeToDismiss_false_neverListensForGesture() {
         underTest.displayView(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
deleted file mode 100644
index ffac8f6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.temporarydisplay.chipbar
-
-import android.content.Context
-import android.os.PowerManager
-import android.view.ViewGroup
-import android.view.WindowManager
-import android.view.accessibility.AccessibilityManager
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
-import com.android.systemui.util.view.ViewUtil
-import com.android.systemui.util.wakelock.WakeLock
-
-/** A fake implementation of [ChipbarCoordinator] for testing. */
-class FakeChipbarCoordinator(
-    context: Context,
-    logger: ChipbarLogger,
-    windowManager: WindowManager,
-    mainExecutor: DelayableExecutor,
-    accessibilityManager: AccessibilityManager,
-    configurationController: ConfigurationController,
-    dumpManager: DumpManager,
-    powerManager: PowerManager,
-    falsingManager: FalsingManager,
-    falsingCollector: FalsingCollector,
-    swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
-    viewUtil: ViewUtil,
-    vibratorHelper: VibratorHelper,
-    wakeLockBuilder: WakeLock.Builder,
-    systemClock: SystemClock,
-) :
-    ChipbarCoordinator(
-        context,
-        logger,
-        windowManager,
-        mainExecutor,
-        accessibilityManager,
-        configurationController,
-        dumpManager,
-        powerManager,
-        falsingManager,
-        falsingCollector,
-        swipeChipbarAwayGestureHandler,
-        viewUtil,
-        vibratorHelper,
-        wakeLockBuilder,
-        systemClock,
-    ) {
-    override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
-        // Just bypass the animation in tests
-        onAnimationEnd.run()
-    }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
index 7ea1e6c..97e70a4 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
@@ -16,60 +16,148 @@
 
 package com.android.server.companion.datatransfer.contextsync;
 
+import android.annotation.Nullable;
 import android.telecom.Call;
 import android.telecom.InCallService;
+import android.telecom.TelecomManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.companion.CompanionDeviceConfig;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
 
-/** In-call service to sync call metadata across a user's devices. */
+/**
+ * In-call service to sync call metadata across a user's devices. Note that mute and silence are
+ * global states and apply to all current calls.
+ */
 public class CallMetadataSyncInCallService extends InCallService {
 
+    private static final long NOT_VALID = -1L;
+
     @VisibleForTesting
-    final Set<CrossDeviceCall> mCurrentCalls = new HashSet<>();
+    final Map<Call, CrossDeviceCall> mCurrentCalls = new HashMap<>();
+    final Call.Callback mTelecomCallback = new Call.Callback() {
+        @Override
+        public void onDetailsChanged(Call call, Call.Details details) {
+            mCurrentCalls.get(call).updateCallDetails(details);
+        }
+    };
+    final CallMetadataSyncCallback mCallMetadataSyncCallback = new CallMetadataSyncCallback() {
+        @Override
+        void processCallControlAction(int crossDeviceCallId, int callControlAction) {
+            final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
+                    mCurrentCalls.values());
+            switch (callControlAction) {
+                case android.companion.Telecom.Call.ACCEPT:
+                    if (crossDeviceCall != null) {
+                        crossDeviceCall.doAccept();
+                    }
+                    break;
+                case android.companion.Telecom.Call.REJECT:
+                    if (crossDeviceCall != null) {
+                        crossDeviceCall.doReject();
+                    }
+                    break;
+                case android.companion.Telecom.Call.SILENCE:
+                    doSilence();
+                    break;
+                case android.companion.Telecom.Call.MUTE:
+                    doMute();
+                    break;
+                case android.companion.Telecom.Call.UNMUTE:
+                    doUnmute();
+                    break;
+                case android.companion.Telecom.Call.END:
+                    if (crossDeviceCall != null) {
+                        crossDeviceCall.doEnd();
+                    }
+                    break;
+                case android.companion.Telecom.Call.PUT_ON_HOLD:
+                    if (crossDeviceCall != null) {
+                        crossDeviceCall.doPutOnHold();
+                    }
+                    break;
+                case android.companion.Telecom.Call.TAKE_OFF_HOLD:
+                    if (crossDeviceCall != null) {
+                        crossDeviceCall.doTakeOffHold();
+                    }
+                    break;
+                default:
+            }
+        }
+
+        @Override
+        void requestCrossDeviceSync(int userId) {
+        }
+    };
 
     @Override
     public void onCreate() {
         super.onCreate();
-        mCurrentCalls.addAll(getCalls().stream().map(CrossDeviceCall::new).toList());
+        if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+            mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call,
+                    call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState()))));
+        }
+    }
+
+    @Nullable
+    @VisibleForTesting
+    CrossDeviceCall getCallForId(long crossDeviceCallId, Collection<CrossDeviceCall> calls) {
+        if (crossDeviceCallId == NOT_VALID) {
+            return null;
+        }
+        for (CrossDeviceCall crossDeviceCall : calls) {
+            if (crossDeviceCall.getId() == crossDeviceCallId) {
+                return crossDeviceCall;
+            }
+        }
+        return null;
     }
 
     @Override
     public void onCallAdded(Call call) {
-        onCallAdded(new CrossDeviceCall(call));
-    }
-
-    @VisibleForTesting
-    void onCallAdded(CrossDeviceCall call) {
-        mCurrentCalls.add(call);
+        if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+            mCurrentCalls.put(call,
+                    new CrossDeviceCall(getPackageManager(), call, getCallAudioState()));
+        }
     }
 
     @Override
     public void onCallRemoved(Call call) {
-        mCurrentCalls.removeIf(crossDeviceCall -> crossDeviceCall.getCall().equals(call));
-    }
-
-    /** Data holder for a telecom call and additional metadata. */
-    public static final class CrossDeviceCall {
-        private static final AtomicLong sNextId = new AtomicLong(1);
-
-        private final Call mCall;
-        private final long mId;
-
-        public CrossDeviceCall(Call call) {
-            mCall = call;
-            mId = sNextId.getAndIncrement();
-        }
-
-        public Call getCall() {
-            return mCall;
-        }
-
-        public long getId() {
-            return mId;
+        if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+            mCurrentCalls.remove(call);
         }
     }
-}
+
+    @Override
+    public void onMuteStateChanged(boolean isMuted) {
+        if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+            mCurrentCalls.values().forEach(call -> call.updateMuted(isMuted));
+        }
+    }
+
+    @Override
+    public void onSilenceRinger() {
+        if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+            mCurrentCalls.values().forEach(call -> call.updateSilencedIfRinging());
+        }
+    }
+
+    private void doMute() {
+        setMuted(/* shouldMute= */ true);
+    }
+
+    private void doUnmute() {
+        setMuted(/* shouldMute= */ false);
+    }
+
+    private void doSilence() {
+        final TelecomManager telecomManager = getSystemService(TelecomManager.class);
+        if (telecomManager != null) {
+            telecomManager.silenceRinger();
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/Android.bp b/services/core/Android.bp
index c0bb586..f65ed33 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -182,6 +182,7 @@
         "SurfaceFlingerProperties",
         "com.android.sysprop.watchdog",
         "ImmutabilityAnnotation",
+        "securebox",
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5f27841..10d50c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14256,6 +14256,16 @@
             }
         }
 
+        if (Process.isSdkSandboxUid(realCallingUid)) {
+            SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
+                    SdkSandboxManagerLocal.class);
+            if (sdkSandboxManagerLocal == null) {
+                throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
+                    + " a broadcast from an SDK sandbox uid.");
+            }
+            sdkSandboxManagerLocal.enforceAllowedToSendBroadcast(intent);
+        }
+
         boolean timeoutExempt = false;
 
         if (action != null) {
@@ -14266,16 +14276,6 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
             }
 
-            if (Process.isSdkSandboxUid(realCallingUid)) {
-                SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
-                        SdkSandboxManagerLocal.class);
-                if (sdkSandboxManagerLocal == null) {
-                    throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
-                            + " a broadcast from an SDK sandbox uid.");
-                }
-                sdkSandboxManagerLocal.enforceAllowedToSendBroadcast(intent);
-            }
-
             switch (action) {
                 case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
                     UserManagerInternal umInternal = LocalServices.getService(
@@ -18498,6 +18498,12 @@
     }
 
     @Override
+    public boolean isProcessFrozen(int pid) {
+        enforceCallingPermission(permission.DUMP, "isProcessFrozen()");
+        return mOomAdjuster.mCachedAppOptimizer.isProcessFrozen(pid);
+    }
+
+    @Override
     @ReasonCode
     public int getBackgroundRestrictionExemptionReason(int uid) {
         enforceCallingPermission(android.Manifest.permission.DEVICE_POWER,
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index d7cca60..a440f18 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -1113,6 +1113,7 @@
         pw.print(" because ");
         pw.print(reasonToString(mRunnableAtReason));
         pw.println();
+        pw.print("mProcessCached="); pw.println(mProcessCached);
         pw.increaseIndent();
         if (mActive != null) {
             dumpRecord("ACTIVE", now, pw, mActive, mActiveIndex);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 410bc41..2689193 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1509,6 +1509,12 @@
         return profile;
     }
 
+    boolean isProcessFrozen(int pid) {
+        synchronized (mProcLock) {
+            return mFrozenProcesses.contains(pid);
+        }
+    }
+
     @VisibleForTesting
     static final class SingleCompactionStats {
         private static final float STATSD_SAMPLE_RATE = 0.1f;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 8d3c21e..3357708 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -1121,11 +1121,15 @@
             final String permissionCheck =
                     checkContentProviderPermission(cpi, callingPid, callingUid,
                             userId, checkUser, null);
-            if (permissionCheck != null) {
+            final boolean grantCheck = mService.checkUriPermission(uri, callingPid, callingUid,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION , userId, null)
+                    == PackageManager.PERMISSION_GRANTED;
+
+            if (!grantCheck && permissionCheck != null) {
                 FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
                         GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_FRAMEWORK_PERMISSION,
                         callingUid, authority, type);
-            } else if (cpi.forceUriPermissions
+            } else if (!grantCheck && cpi.forceUriPermissions
                     && holder.provider.checkUriPermission(attributionSource,
                             uri, callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
                             != PermissionChecker.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index 08dcccf..35df3ee 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -86,7 +86,7 @@
 
     private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
             PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER,
-            ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER);
+            ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER, PREFERRED_HELPER);
 
     private int mUserId = UserHandle.USER_SYSTEM;
 
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 10abfdd..05464c8 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -196,6 +196,11 @@
     // available.
     private float mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
+    // The screen brightness level before clamping and throttling. This value needs to be stored
+    // for concurrent displays mode and passed to the additional displays which will do their own
+    // clamping and throttling.
+    private float mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
     // The current display policy. This is useful, for example,  for knowing when we're dozing,
     // where the light sensor may not be available.
     private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF;
@@ -380,6 +385,10 @@
         return mScreenAutoBrightness;
     }
 
+    float getRawAutomaticScreenBrightness() {
+        return mRawScreenAutoBrightness;
+    }
+
     public boolean hasValidAmbientLux() {
         return mAmbientLuxValid;
     }
@@ -653,6 +662,7 @@
                 mPreThresholdLux = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             }
             mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+            mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             mRecentLightSamples = 0;
             mAmbientLightRingBuffer.clear();
@@ -915,6 +925,7 @@
 
         float value = mCurrentBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
                 mForegroundAppCategory);
+        mRawScreenAutoBrightness = value;
         float newScreenAutoBrightness = clampScreenBrightness(value);
 
         // The min/max range can change for brightness due to HBM. See if the current brightness
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index 864510e..fa9a100 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.os.IBinder;
+import android.view.Display;
 
 import java.util.Objects;
 
@@ -32,6 +33,9 @@
     private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
     private static native long[] nativeGetPhysicalDisplayIds();
     private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
+    private static native void nativeSetHdrConversionMode(int conversionMode,
+            int preferredHdrOutputType, int[] autoHdrTypes, int autoHdrTypesLength);
+    private static native int[] nativeGetSupportedHdrOutputTypes();
 
     /**
      * Create a display in SurfaceFlinger.
@@ -79,4 +83,20 @@
     public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
         return nativeGetPhysicalDisplayToken(physicalDisplayId);
     }
+
+    /**
+     * @hide
+     */
+    public static void setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
+            int[] autoHdrTypes) {
+        int length = autoHdrTypes != null ? autoHdrTypes.length : 0;
+        nativeSetHdrConversionMode(conversionMode, preferredHdrOutputType, autoHdrTypes, length);
+    }
+
+    /**
+     * @hide
+     */
+    public static @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypes() {
+        return nativeGetSupportedHdrOutputTypes();
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index e557b50..535129c 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -132,6 +132,16 @@
  *            <brightness>0.01</brightness>
  *          </brightnessThrottlingPoint>
  *        </brightnessThrottlingMap>
+ *        <concurrentDisplaysBrightnessThrottlingMap>
+ *          <brightnessThrottlingPoint>
+ *            <thermalStatus>severe</thermalStatus>
+ *            <brightness>0.07</brightness>
+ *          </brightnessThrottlingPoint>
+ *          <brightnessThrottlingPoint>
+ *            <thermalStatus>critical</thermalStatus>
+ *            <brightness>0.005</brightness>
+ *          </brightnessThrottlingPoint>
+ *        </concurrentDisplaysBrightnessThrottlingMap>
  *      </thermalThrottling>
  *
  *      <refreshRate>
@@ -613,6 +623,8 @@
     // overwritten value.
     private BrightnessThrottlingData mBrightnessThrottlingData;
     private BrightnessThrottlingData mOriginalBrightnessThrottlingData;
+    // The concurrent displays mode might need a stricter throttling policy
+    private BrightnessThrottlingData mConcurrentDisplaysBrightnessThrottlingData;
 
     @VisibleForTesting
     DisplayDeviceConfig(Context context) {
@@ -1258,13 +1270,21 @@
     }
 
     /**
-     * @return brightness throttling data configuration data for the display.
+     * @return brightness throttling configuration data for the display.
      */
     public BrightnessThrottlingData getBrightnessThrottlingData() {
         return BrightnessThrottlingData.create(mBrightnessThrottlingData);
     }
 
     /**
+     * @return brightness throttling configuration data for the display for the concurrent
+     * displays mode.
+     */
+    public BrightnessThrottlingData getConcurrentDisplaysBrightnessThrottlingData() {
+        return BrightnessThrottlingData.create(mConcurrentDisplaysBrightnessThrottlingData);
+    }
+
+    /**
      * @return Auto brightness darkening light debounce
      */
     public long getAutoBrightnessDarkeningLightDebounce() {
@@ -1503,6 +1523,7 @@
                 loadBrightnessConstraintsFromConfigXml();
                 loadBrightnessMap(config);
                 loadBrightnessThrottlingMap(config);
+                loadConcurrentDisplaysBrightnessThrottlingMap(config);
                 loadHighBrightnessModeData(config);
                 loadQuirks(config);
                 loadBrightnessRamps(config);
@@ -1714,13 +1735,13 @@
     private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
         final ThermalThrottling throttlingConfig = config.getThermalThrottling();
         if (throttlingConfig == null) {
-            Slog.i(TAG, "no thermal throttling config found");
+            Slog.i(TAG, "No thermal throttling config found");
             return;
         }
 
         final BrightnessThrottlingMap map = throttlingConfig.getBrightnessThrottlingMap();
         if (map == null) {
-            Slog.i(TAG, "no brightness throttling map found");
+            Slog.i(TAG, "No brightness throttling map found");
             return;
         }
 
@@ -1747,6 +1768,43 @@
         }
     }
 
+    private void loadConcurrentDisplaysBrightnessThrottlingMap(DisplayConfiguration config) {
+        final ThermalThrottling throttlingConfig = config.getThermalThrottling();
+        if (throttlingConfig == null) {
+            Slog.i(TAG, "No concurrent displays thermal throttling config found");
+            return;
+        }
+
+        final BrightnessThrottlingMap map =
+                throttlingConfig.getConcurrentDisplaysBrightnessThrottlingMap();
+        if (map == null) {
+            Slog.i(TAG, "No concurrent displays brightness throttling map found");
+            return;
+        }
+
+        final List<BrightnessThrottlingPoint> points = map.getBrightnessThrottlingPoint();
+        // At least 1 point is guaranteed by the display device config schema
+        List<BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+                new ArrayList<>(points.size());
+
+        boolean badConfig = false;
+        for (BrightnessThrottlingPoint point : points) {
+            ThermalStatus status = point.getThermalStatus();
+            if (!thermalStatusIsValid(status)) {
+                badConfig = true;
+                break;
+            }
+
+            throttlingLevels.add(new BrightnessThrottlingData.ThrottlingLevel(
+                    convertThermalStatus(status), point.getBrightness().floatValue()));
+        }
+
+        if (!badConfig) {
+            mConcurrentDisplaysBrightnessThrottlingData =
+                    BrightnessThrottlingData.create(throttlingLevels);
+        }
+    }
+
     private void loadRefreshRateSetting(DisplayConfiguration config) {
         final RefreshRateConfigs refreshRateConfigs =
                 (config == null) ? null : config.getRefreshRate();
@@ -2529,7 +2587,8 @@
         }
     }
 
-    private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
+    @VisibleForTesting
+    static @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
         if (value == null) {
             return PowerManager.THERMAL_STATUS_NONE;
         }
@@ -2855,7 +2914,8 @@
             return throttlingLevels.hashCode();
         }
 
-        private BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
+        @VisibleForTesting
+        BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
             throttlingLevels = new ArrayList<>(inLevels.size());
             for (ThrottlingLevel level : inLevels) {
                 throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, level.brightness));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c1d8cf4..f2110fe 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -79,6 +79,7 @@
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.display.HdrConversionMode;
 import android.hardware.display.IDisplayManager;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -233,11 +234,17 @@
     private InputManagerInternal mInputManagerInternal;
     private IMediaProjectionManager mProjectionService;
     private DeviceStateManagerInternal mDeviceStateManager;
+    @GuardedBy("mSyncRoot")
     private int[] mUserDisabledHdrTypes = {};
+    private int[] mSupportedHdrOutputType;
+    @GuardedBy("mSyncRoot")
     private boolean mAreUserDisabledHdrTypesAllowed = true;
 
     // Display mode chosen by user.
     private Display.Mode mUserPreferredMode;
+    // HDR conversion mode chosen by user
+    @GuardedBy("mSyncRoot")
+    private HdrConversionMode mHdrConversionMode;
 
     // The synchronization root for the display manager.
     // This lock guards most of the display manager's state.
@@ -880,8 +887,8 @@
     }
 
     private void clearUserDisabledHdrTypesLocked() {
-        mUserDisabledHdrTypes = new int[]{};
         synchronized (mSyncRoot) {
+            mUserDisabledHdrTypes = new int[]{};
             Settings.Global.putString(mContext.getContentResolver(),
                     Settings.Global.USER_DISABLED_HDR_FORMATS, "");
         }
@@ -1907,6 +1914,24 @@
         });
     }
 
+    @GuardedBy("mSyncRoot")
+    private int[] getEnabledAutoHdrTypesLocked() {
+        IntArray autoHdrOutputTypesArray = new IntArray();
+        for (int type : getSupportedHdrOutputTypesInternal()) {
+            boolean isDisabled = false;
+            for (int disabledType : mUserDisabledHdrTypes) {
+                if (type == disabledType) {
+                    isDisabled = true;
+                    break;
+                }
+            }
+            if (!isDisabled) {
+                autoHdrOutputTypesArray.add(type);
+            }
+        }
+        return autoHdrOutputTypesArray.toArray();
+    }
+
     Display.Mode getUserPreferredDisplayModeInternal(int displayId) {
         synchronized (mSyncRoot) {
             if (displayId == Display.INVALID_DISPLAY) {
@@ -1930,6 +1955,38 @@
         }
     }
 
+    // TODO (b/264979880) - Add unit test for HDR output control methods.
+    private void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) {
+        int[] autoHdrOutputTypes = null;
+        synchronized (mSyncRoot) {
+            mHdrConversionMode = hdrConversionMode;
+
+            // For auto mode, all supported HDR types are allowed except the ones specifically
+            // disabled by the user.
+            if (hdrConversionMode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM) {
+                autoHdrOutputTypes = getEnabledAutoHdrTypesLocked();
+            }
+            DisplayControl.setHdrConversionMode(hdrConversionMode.getConversionMode(),
+                    hdrConversionMode.getPreferredHdrOutputType(), autoHdrOutputTypes);
+        }
+    }
+
+    private HdrConversionMode getHdrConversionModeInternal() {
+        synchronized (mSyncRoot) {
+            if (mHdrConversionMode != null) {
+                return mHdrConversionMode;
+            }
+        }
+        return new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM);
+    }
+
+    private @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypesInternal() {
+        if (mSupportedHdrOutputType == null) {
+            mSupportedHdrOutputType = DisplayControl.getSupportedHdrOutputTypes();
+        }
+        return mSupportedHdrOutputType;
+    }
+
     void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
         mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
     }
@@ -3191,7 +3248,9 @@
 
         @Override // Binder call
         public int[] getUserDisabledHdrTypes() {
-            return mUserDisabledHdrTypes;
+            synchronized (mSyncRoot) {
+                return mUserDisabledHdrTypes;
+            }
         }
 
         @Override // Binder call
@@ -3610,6 +3669,40 @@
         }
 
         @Override // Binder call
+        public void setHdrConversionMode(HdrConversionMode hdrConversionMode) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MODIFY_HDR_CONVERSION_MODE,
+                    "Permission required to set the HDR conversion mode.");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setHdrConversionModeInternal(hdrConversionMode);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public HdrConversionMode getHdrConversionMode() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getHdrConversionModeInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Display.HdrCapabilities.HdrType
+        @Override // Binder call
+        public int[] getSupportedHdrOutputTypes() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getSupportedHdrOutputTypesInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 263c4d7..dbd2ab0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -885,6 +885,9 @@
                 updatePowerState();
             }
         });
+
+        // TODO (b/265793751): Re-create BrightnessTracker if we're enetering/exiting concurrent
+        // displays mode
     }
 
     /**
@@ -943,6 +946,8 @@
                         return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
                     }
                 });
+        // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+        // mode
         mBrightnessThrottler.resetThrottlingData(
                 mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
     }
@@ -1160,6 +1165,8 @@
             }
             loadScreenOffBrightnessSensor();
             int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+            // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
+            // a complementary display
             if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
                 mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
                         mSensorManager,
@@ -1452,7 +1459,8 @@
         final boolean autoBrightnessEnabled = mUseAutoBrightness
                 && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                 && Float.isNaN(brightnessState)
-                && mAutomaticBrightnessController != null;
+                && mAutomaticBrightnessController != null
+                && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER;
         final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness
                 && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
         final int autoBrightnessState = autoBrightnessEnabled
@@ -1521,12 +1529,15 @@
         }
 
         boolean updateScreenBrightnessSetting = false;
+        float rawBrightnessState = brightnessState;
 
         // Apply auto-brightness.
         boolean slowChange = false;
         if (Float.isNaN(brightnessState)) {
             float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
             if (autoBrightnessEnabled) {
+                rawBrightnessState = mAutomaticBrightnessController
+                        .getRawAutomaticScreenBrightness();
                 brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
                         mTempBrightnessEvent);
                 newAutoBrightnessAdjustment =
@@ -1568,7 +1579,8 @@
         // Use default brightness when dozing unless overridden.
         if ((Float.isNaN(brightnessState))
                 && Display.isDozeState(state)) {
-            brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+            rawBrightnessState = mScreenBrightnessDozeConfig;
+            brightnessState = clampScreenBrightness(rawBrightnessState);
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
         }
 
@@ -1576,7 +1588,9 @@
         // brightness
         if (Float.isNaN(brightnessState) && autoBrightnessEnabled
                 && mScreenOffBrightnessSensorController != null) {
-            brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+            rawBrightnessState =
+                    mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+            brightnessState = rawBrightnessState;
             if (isValidBrightnessValue(brightnessState)) {
                 brightnessState = clampScreenBrightness(brightnessState);
                 updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
@@ -1587,7 +1601,8 @@
 
         // Apply manual brightness.
         if (Float.isNaN(brightnessState)) {
-            brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+            rawBrightnessState = mCurrentScreenBrightnessSetting;
+            brightnessState = clampScreenBrightness(rawBrightnessState);
             if (brightnessState != mCurrentScreenBrightnessSetting) {
                 // The manually chosen screen brightness is outside of the currently allowed
                 // range (i.e., high-brightness-mode), make sure we tell the rest of the system
@@ -1621,7 +1636,7 @@
 
         for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
             DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
-            follower.setBrightnessToFollow(brightnessState, convertToNits(brightnessState));
+            follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState));
         }
 
         if (updateScreenBrightnessSetting) {
@@ -2038,6 +2053,8 @@
     private BrightnessThrottler createBrightnessThrottlerLocked() {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+        // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+        // mode
         final DisplayDeviceConfig.BrightnessThrottlingData data =
                 ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
         return new BrightnessThrottler(mHandler, data,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0cc4e93..103a1da 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -732,6 +732,9 @@
                 updatePowerState();
             }
         });
+
+        // TODO (b/265793751): Re-create BrightnessTracker if we're enetering/exiting concurrent
+        // displays mode
     }
 
     /**
@@ -783,6 +786,8 @@
                         return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
                     }
                 });
+        // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+        // mode
         mBrightnessThrottler.resetThrottlingData(
                 mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
     }
@@ -999,6 +1004,8 @@
 
             loadScreenOffBrightnessSensor();
             int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+            // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
+            // a complementary display
             if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
                 mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
                         mSensorManager,
@@ -1175,6 +1182,7 @@
         DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
                 .updateBrightness(mPowerRequest, state);
         float brightnessState = displayBrightnessState.getBrightness();
+        float rawBrightnessState = displayBrightnessState.getBrightness();
         mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
 
         final boolean autoBrightnessEnabledInDoze =
@@ -1185,7 +1193,8 @@
                 && (Float.isNaN(brightnessState)
                         || mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY
                         || mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_BOOST)
-                && mAutomaticBrightnessController != null;
+                && mAutomaticBrightnessController != null
+                && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER;
         final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness
                 && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
         final int autoBrightnessState = autoBrightnessEnabled
@@ -1239,6 +1248,8 @@
         if (Float.isNaN(brightnessState)) {
             float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
             if (autoBrightnessEnabled) {
+                rawBrightnessState = mAutomaticBrightnessController
+                        .getRawAutomaticScreenBrightness();
                 brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
                         mTempBrightnessEvent);
                 newAutoBrightnessAdjustment =
@@ -1280,7 +1291,8 @@
         // Use default brightness when dozing unless overridden.
         if ((Float.isNaN(brightnessState))
                 && Display.isDozeState(state)) {
-            brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+            rawBrightnessState = mScreenBrightnessDozeConfig;
+            brightnessState = clampScreenBrightness(rawBrightnessState);
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
         }
 
@@ -1288,7 +1300,9 @@
         // brightness
         if (Float.isNaN(brightnessState) && autoBrightnessEnabled
                 && mScreenOffBrightnessSensorController != null) {
-            brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+            rawBrightnessState =
+                    mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+            brightnessState = rawBrightnessState;
             if (BrightnessUtils.isValidBrightnessValue(brightnessState)) {
                 brightnessState = clampScreenBrightness(brightnessState);
                 updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness()
@@ -1300,7 +1314,8 @@
 
         // Apply manual brightness.
         if (Float.isNaN(brightnessState)) {
-            brightnessState = clampScreenBrightness(currentBrightnessSetting);
+            rawBrightnessState = currentBrightnessSetting;
+            brightnessState = clampScreenBrightness(rawBrightnessState);
             if (brightnessState != currentBrightnessSetting) {
                 // The manually chosen screen brightness is outside of the currently allowed
                 // range (i.e., high-brightness-mode), make sure we tell the rest of the system
@@ -1334,7 +1349,7 @@
 
         for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
             DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
-            follower.setBrightnessToFollow(brightnessState, convertToNits(brightnessState));
+            follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState));
         }
 
         if (updateScreenBrightnessSetting) {
@@ -1745,6 +1760,8 @@
     private BrightnessThrottler createBrightnessThrottlerLocked() {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+        // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+        // mode
         final DisplayDeviceConfig.BrightnessThrottlingData data =
                 ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
         return new BrightnessThrottler(mHandler, data,
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 282ad57..262013e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -113,9 +113,8 @@
 import com.android.internal.util.HexDump;
 import com.android.server.FgThread;
 import com.android.server.location.gnss.GnssSatelliteBlocklistHelper.GnssSatelliteBlocklistCallback;
-import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
 import com.android.server.location.gnss.hal.GnssNative;
-import com.android.server.location.injector.Injector;
 import com.android.server.location.provider.AbstractLocationProvider;
 
 import java.io.FileDescriptor;
@@ -138,7 +137,7 @@
  * {@hide}
  */
 public class GnssLocationProvider extends AbstractLocationProvider implements
-        InjectNtpTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
+        InjectTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
         GnssNative.LocationCallbacks, GnssNative.SvStatusCallbacks, GnssNative.AGpsCallbacks,
         GnssNative.PsdsCallbacks, GnssNative.NotificationCallbacks,
         GnssNative.LocationRequestCallbacks, GnssNative.TimeCallbacks {
@@ -307,7 +306,7 @@
     private boolean mSuplEsEnabled = false;
 
     private final LocationExtras mLocationExtras = new LocationExtras();
-    private final NtpTimeHelper mNtpTimeHelper;
+    private final NetworkTimeHelper mNetworkTimeHelper;
     private final GnssSatelliteBlocklistHelper mGnssSatelliteBlocklistHelper;
 
     // Available only on GNSS HAL 2.0 implementations and later.
@@ -398,7 +397,7 @@
         }
     }
 
-    public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
+    public GnssLocationProvider(Context context, GnssNative gnssNative,
             GnssMetrics gnssMetrics) {
         super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
                 Collections.emptySet());
@@ -470,7 +469,7 @@
                 GnssLocationProvider.this::onNetworkAvailable,
                 mHandler.getLooper(), mNIHandler);
 
-        mNtpTimeHelper = new NtpTimeHelper(mContext, mHandler.getLooper(), this);
+        mNetworkTimeHelper = NetworkTimeHelper.create(mContext, mHandler.getLooper(), this);
         mGnssSatelliteBlocklistHelper =
                 new GnssSatelliteBlocklistHelper(mContext,
                         mHandler.getLooper(), this);
@@ -647,18 +646,19 @@
     }
 
     /**
-     * Implements {@link InjectNtpTimeCallback#injectTime}
+     * Implements {@link InjectTimeCallback#injectTime}
      */
     @Override
-    public void injectTime(long time, long timeReference, int uncertainty) {
-        mGnssNative.injectTime(time, timeReference, uncertainty);
+    public void injectTime(long unixEpochTimeMillis, long elapsedRealtimeMillis,
+            int uncertaintyMillis) {
+        mGnssNative.injectTime(unixEpochTimeMillis, elapsedRealtimeMillis, uncertaintyMillis);
     }
 
     /**
      * Implements {@link GnssNetworkConnectivityHandler.GnssNetworkListener#onNetworkAvailable()}
      */
     private void onNetworkAvailable() {
-        mNtpTimeHelper.onNetworkAvailable();
+        mNetworkTimeHelper.onNetworkAvailable();
         // Download only if supported, (prevents an unnecessary on-boot download)
         if (mSupportsPsds) {
             synchronized (mLock) {
@@ -1145,7 +1145,7 @@
         if ("delete_aiding_data".equals(command)) {
             deleteAidingData(extras);
         } else if ("force_time_injection".equals(command)) {
-            requestUtcTime();
+            demandUtcTimeInjection();
         } else if ("force_psds_injection".equals(command)) {
             if (mSupportsPsds) {
                 postWithWakeLockHeld(() -> handleDownloadPsdsData(
@@ -1514,9 +1514,9 @@
                 /* userResponse= */ 0);
     }
 
-    private void requestUtcTime() {
-        if (DEBUG) Log.d(TAG, "utcTimeRequest");
-        postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
+    private void demandUtcTimeInjection() {
+        if (DEBUG) Log.d(TAG, "demandUtcTimeInjection");
+        postWithWakeLockHeld(mNetworkTimeHelper::demandUtcTimeInjection);
     }
 
 
@@ -1721,9 +1721,16 @@
     public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
             GnssCapabilities newCapabilities) {
         mHandler.post(() -> {
-            if (mGnssNative.getCapabilities().hasOnDemandTime()) {
-                mNtpTimeHelper.enablePeriodicTimeInjection();
-                requestUtcTime();
+            boolean useOnDemandTimeInjection = mGnssNative.getCapabilities().hasOnDemandTime();
+
+            // b/73893222: There is a historic bug on Android, which means that the capability
+            // "on demand time" is interpreted as "enable periodic injection" elsewhere but an
+            // on-demand injection is done here. GNSS developers may have come to rely on the
+            // periodic behavior, so it has been kept and all methods named to reflect what is
+            // actually done. "On demand" requests are supported regardless of the capability.
+            mNetworkTimeHelper.setPeriodicTimeInjectionMode(useOnDemandTimeInjection);
+            if (useOnDemandTimeInjection) {
+                demandUtcTimeInjection();
             }
 
             restartLocationRequest();
@@ -1857,7 +1864,7 @@
 
     @Override
     public void onRequestUtcTime() {
-        requestUtcTime();
+        demandUtcTimeInjection();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 69385a9..2174f40 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -83,8 +83,7 @@
         mGnssMetrics = new GnssMetrics(mContext, IBatteryStats.Stub.asInterface(
                 ServiceManager.getService(BatteryStats.SERVICE_NAME)), mGnssNative);
 
-        mGnssLocationProvider = new GnssLocationProvider(mContext, injector, mGnssNative,
-                mGnssMetrics);
+        mGnssLocationProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
         mGnssStatusProvider = new GnssStatusProvider(injector, mGnssNative);
         mGnssNmeaProvider = new GnssNmeaProvider(injector, mGnssNative);
         mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector, mGnssNative);
diff --git a/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
new file mode 100644
index 0000000..72d6f70
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.location.gnss;
+
+import android.content.Context;
+import android.os.Looper;
+
+/**
+ * An abstraction for use by {@link GnssLocationProvider}. This class allows switching between
+ * implementations with a compile-time constant change, which is less risky than rolling back a
+ * whole class. When there is a single implementation again this class can be replaced by that
+ * implementation.
+ */
+abstract class NetworkTimeHelper {
+
+    /**
+     * The callback interface used by {@link NetworkTimeHelper} to report the time to {@link
+     * GnssLocationProvider}. The callback can happen at any time using the thread associated with
+     * the looper passed to {@link #create(Context, Looper, InjectTimeCallback)}.
+     */
+    interface InjectTimeCallback {
+        void injectTime(long unixEpochTimeMillis, long elapsedRealtimeMillis,
+                int uncertaintyMillis);
+    }
+
+    /**
+     * Creates the {@link NetworkTimeHelper} instance for use by {@link GnssLocationProvider}.
+     */
+    static NetworkTimeHelper create(
+            Context context, Looper looper, InjectTimeCallback injectTimeCallback) {
+        return new NtpNetworkTimeHelper(context, looper, injectTimeCallback);
+    }
+
+    /**
+     * Sets the "on demand time injection" mode.
+     *
+     * <p>Called by {@link GnssLocationProvider} to set the expected time injection behavior.
+     * When {@code enablePeriodicTimeInjection == true}, the time helper should periodically send
+     * the time on an undefined schedule. The time can be injected at other times for other reasons
+     * as well as be requested via {@link #demandUtcTimeInjection()}.
+     *
+     * @param periodicTimeInjectionEnabled {@code true} if the GNSS implementation requires periodic
+     *   time signals
+     */
+    abstract void setPeriodicTimeInjectionMode(boolean periodicTimeInjectionEnabled);
+
+    /**
+     * Requests an asynchronous time injection via {@link InjectTimeCallback#injectTime}, if a
+     * network time is available. {@link InjectTimeCallback#injectTime} may not be called if a
+     * network time is not available.
+     */
+    abstract void demandUtcTimeInjection();
+
+    /**
+     * Notifies that network connectivity has been established.
+     *
+     * <p>Called by {@link GnssLocationProvider} when the device establishes a data network
+     * connection.
+     */
+    abstract void onNetworkAvailable();
+
+}
diff --git a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java b/services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
similarity index 83%
rename from services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
rename to services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
index e4bc788..479dbda 100644
--- a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
+++ b/services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
@@ -30,14 +30,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
- * Handles inject NTP time to GNSS.
- *
- * <p>The client is responsible to call {@link #onNetworkAvailable()} when network is available
- * for retrieving NTP Time.
+ * Handles injecting network time to GNSS by explicitly making NTP requests when needed.
  */
-class NtpTimeHelper {
+class NtpNetworkTimeHelper extends NetworkTimeHelper {
 
-    private static final String TAG = "NtpTimeHelper";
+    private static final String TAG = "NtpNetworkTimeHelper";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // states for injecting ntp
@@ -71,23 +68,19 @@
     private final WakeLock mWakeLock;
     private final Handler mHandler;
 
-    private final InjectNtpTimeCallback mCallback;
+    private final InjectTimeCallback mCallback;
 
     // flags to trigger NTP when network becomes available
     // initialized to STATE_PENDING_NETWORK so we do NTP when the network comes up after booting
     @GuardedBy("this")
     private int mInjectNtpTimeState = STATE_PENDING_NETWORK;
 
-    // set to true if the GPS engine requested on-demand NTP time requests
+    // Enables periodic time injection in addition to injection for other reasons.
     @GuardedBy("this")
-    private boolean mOnDemandTimeInjection;
-
-    interface InjectNtpTimeCallback {
-        void injectTime(long time, long timeReference, int uncertainty);
-    }
+    private boolean mPeriodicTimeInjection;
 
     @VisibleForTesting
-    NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback,
+    NtpNetworkTimeHelper(Context context, Looper looper, InjectTimeCallback callback,
             NtpTrustedTime ntpTime) {
         mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         mCallback = callback;
@@ -97,14 +90,23 @@
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
     }
 
-    NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback) {
+    NtpNetworkTimeHelper(Context context, Looper looper, InjectTimeCallback callback) {
         this(context, looper, callback, NtpTrustedTime.getInstance(context));
     }
 
-    synchronized void enablePeriodicTimeInjection() {
-        mOnDemandTimeInjection = true;
+    @Override
+    synchronized void setPeriodicTimeInjectionMode(boolean periodicTimeInjectionEnabled) {
+        if (periodicTimeInjectionEnabled) {
+            mPeriodicTimeInjection = true;
+        }
     }
 
+    @Override
+    void demandUtcTimeInjection() {
+        retrieveAndInjectNtpTime();
+    }
+
+    @Override
     synchronized void onNetworkAvailable() {
         if (mInjectNtpTimeState == STATE_PENDING_NETWORK) {
             retrieveAndInjectNtpTime();
@@ -120,7 +122,7 @@
         return activeNetworkInfo != null && activeNetworkInfo.isConnected();
     }
 
-    synchronized void retrieveAndInjectNtpTime() {
+    private synchronized void retrieveAndInjectNtpTime() {
         if (mInjectNtpTimeState == STATE_RETRIEVING_AND_INJECTING) {
             // already downloading data
             return;
@@ -166,18 +168,15 @@
 
             if (DEBUG) {
                 Log.d(TAG, String.format(
-                        "onDemandTimeInjection=%s, refreshSuccess=%s, delay=%s",
-                        mOnDemandTimeInjection,
+                        "mPeriodicTimeInjection=%s, refreshSuccess=%s, delay=%s",
+                        mPeriodicTimeInjection,
                         refreshSuccess,
                         delay));
             }
-            // TODO(b/73893222): reconcile Capabilities bit 'on demand' name vs. de facto periodic
-            // injection.
-            if (mOnDemandTimeInjection || !refreshSuccess) {
-                /* Schedule next NTP injection.
-                 * Since this is delayed, the wake lock is released right away, and will be held
-                 * again when the delayed task runs.
-                 */
+            if (mPeriodicTimeInjection || !refreshSuccess) {
+                // Schedule next NTP injection.
+                // Since this is delayed, the wake lock is released right away, and will be held
+                // again when the delayed task runs.
                 mHandler.postDelayed(this::retrieveAndInjectNtpTime, delay);
             }
         }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index 7921619..2818caa 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -21,6 +21,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 7c80d8a..b437421 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -44,6 +44,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
+import com.android.security.SecureBox;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
 import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
index b94548b..267a72e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
@@ -23,7 +23,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.locksettings.recoverablekeystore.SecureBox;
+import com.android.security.SecureBox;
 
 import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index a7e0af3..e0de294 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -43,6 +43,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
 import android.content.pm.VersionedPackage;
 import android.net.Uri;
 import android.os.Binder;
@@ -776,19 +777,23 @@
                             userId, deleteFlags, false /*removedBySystem*/);
 
                     // Get a list of child user profiles and delete if package is
-                    // present in clone profile.
+                    // present in that profile.
                     int[] childUserIds = mUserManagerInternal.getProfileIds(userId, true);
+                    int returnCodeOfChild;
                     for (int childId : childUserIds) {
-                        if (childId != userId) {
-                            UserInfo userInfo = mUserManagerInternal.getUserInfo(childId);
-                            if (userInfo != null && userInfo.isCloneProfile()) {
-                                returnCode = deletePackageX(internalPackageName, versionCode,
-                                        childId, deleteFlags, false /*removedBySystem*/);
-                                break;
+                        if (childId == userId) continue;
+                        UserProperties userProperties = mUserManagerInternal
+                                .getUserProperties(childId);
+                        if (userProperties != null && userProperties.getDeleteAppWithParent()) {
+                            returnCodeOfChild = deletePackageX(internalPackageName, versionCode,
+                                    childId, deleteFlags, false /*removedBySystem*/);
+                            if (returnCodeOfChild != PackageManager.DELETE_SUCCEEDED) {
+                                Slog.w(TAG, "Package delete failed for user " + childId
+                                        + ", returnCode " + returnCodeOfChild);
+                                returnCode = PackageManager.DELETE_FAILED_FOR_CHILD_PROFILE;
                             }
                         }
                     }
-
                 } else {
                     int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot,
                             internalPackageName, users);
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index b8c57b8..77c32ae 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -136,7 +136,7 @@
                                 .CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
                         .setMediaSharedWithParent(true)
                         .setCredentialShareableWithParent(true)
-                );
+                        .setDeleteAppWithParent(true));
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 58f88c3..e74b459 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -206,6 +206,8 @@
     static {
         SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
         SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE);
+        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND);
     }
 
     private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8973adc..052c785 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1313,7 +1313,9 @@
                     // Bg location is one-off runtime modifier permission and has no app op
                     if (sPlatformPermissions.containsKey(permission)
                             && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)
-                            && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)) {
+                            && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)
+                            && !Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND
+                            .equals(permission)) {
                         Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
                                 + " with no app op defined!");
                     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4e9cd2e..e0bcc0e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3770,9 +3770,6 @@
     private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
         if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
         mKeyguardOccludedChanged = false;
-        if (isKeyguardOccluded() == isOccluded) {
-            return false;
-        }
         mKeyguardDelegate.setOccluded(isOccluded, notify);
         return mKeyguardDelegate.isShowing();
     }
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index a82d4ea..5096ad1 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -61,6 +61,7 @@
 
 public final class ShutdownThread extends Thread {
     // constants
+    private static final boolean DEBUG = false;
     private static final String TAG = "ShutdownThread";
     private static final int ACTION_DONE_POLL_WAIT_MS = 500;
     private static final int RADIOS_STATE_POLL_SLEEP_MS = 100;
@@ -161,7 +162,9 @@
         // any additional calls are just returned
         synchronized (sIsStartedGuard) {
             if (sIsStarted) {
-                Log.d(TAG, "Request to shutdown already running, returning.");
+                if (DEBUG) {
+                    Log.d(TAG, "Request to shutdown already running, returning.");
+                }
                 return;
             }
         }
@@ -178,7 +181,9 @@
                         ? com.android.internal.R.string.shutdown_confirm_question
                         : com.android.internal.R.string.shutdown_confirm);
 
-        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
+        if (DEBUG) {
+            Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
+        }
 
         if (confirm) {
             final CloseDialogReceiver closer = new CloseDialogReceiver(context);
@@ -348,26 +353,34 @@
     }
 
     private static boolean showSysuiReboot() {
-        Log.d(TAG, "Attempting to use SysUI shutdown UI");
+        if (DEBUG) {
+            Log.d(TAG, "Attempting to use SysUI shutdown UI");
+        }
         try {
             StatusBarManagerInternal service = LocalServices.getService(
                     StatusBarManagerInternal.class);
             if (service.showShutdownUi(mReboot, mReason)) {
                 // Sysui will handle shutdown UI.
-                Log.d(TAG, "SysUI handling shutdown UI");
+                if (DEBUG) {
+                    Log.d(TAG, "SysUI handling shutdown UI");
+                }
                 return true;
             }
         } catch (Exception e) {
             // If anything went wrong, ignore it and use fallback ui
         }
-        Log.d(TAG, "SysUI is unavailable");
+        if (DEBUG) {
+            Log.d(TAG, "SysUI is unavailable");
+        }
         return false;
     }
 
     private static void beginShutdownSequence(Context context) {
         synchronized (sIsStartedGuard) {
             if (sIsStarted) {
-                Log.d(TAG, "Shutdown sequence already running, returning.");
+                if (DEBUG) {
+                    Log.d(TAG, "Shutdown sequence already running, returning.");
+                }
                 return;
             }
             sIsStarted = true;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e9badef..718a9e3 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -20,47 +20,63 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.KEYGUARD_STATE_AOD_SHOWN;
+import static android.view.WindowManager.KEYGUARD_STATE_DREAMING;
+import static android.view.WindowManager.KEYGUARD_STATE_GOING_AWAY;
+import static android.view.WindowManager.KEYGUARD_STATE_KEYGUARD_TOP;
+import static android.view.WindowManager.KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+import static android.view.WindowManager.KEYGUARD_STATE_OCCLUDED;
+import static android.view.WindowManager.KEYGUARD_STATE_OFF;
+import static android.view.WindowManager.KEYGUARD_STATE_ON;
+import static android.view.WindowManager.KEYGUARD_STATE_ROOT;
+import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_WAKE;
+import static android.view.WindowManager.keyguardStateToString;
+import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
+
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
-import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_GOING_AWAY;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
-import android.view.Display;
 import android.view.WindowManager;
+import android.view.WindowManager.KeyguardState;
+import android.window.TransitionInfo;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.StateMachine;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
 
 /**
  * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
@@ -69,281 +85,138 @@
  * Note that everything in this class should only be accessed with the AM lock being held.
  */
 class KeyguardController {
-
     private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
 
+    private static final boolean DEBUG = true;
+
     static final String KEYGUARD_SLEEP_TOKEN_TAG = "keyguard";
 
     private static final int DEFER_WAKE_TRANSITION_TIMEOUT_MS = 5000;
 
-    private final ActivityTaskSupervisor mTaskSupervisor;
-    private WindowManagerService mWindowManager;
-
-    private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
     private final ActivityTaskManagerService mService;
-    private RootWindowContainer mRootWindowContainer;
+    private final ActivityTaskSupervisor mTaskSupervisor;
     private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
     private boolean mWaitingForWakeTransition;
+    private WindowManagerService mWindowManager;
+
+    private RootWindowContainer mRootWindowContainer;
+
+    private final SparseArray<DisplayState> mDisplayStates = new SparseArray<>();
+
+    @NonNull private final ServiceDelegate mServiceDelegate = new ServiceDelegate();
 
     KeyguardController(ActivityTaskManagerService service,
             ActivityTaskSupervisor taskSupervisor) {
         mService = service;
         mTaskSupervisor = taskSupervisor;
-        mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
+        mSleepTokenAcquirer = service.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
     }
 
     void setWindowManager(WindowManagerService windowManager) {
         mWindowManager = windowManager;
         mRootWindowContainer = mService.mRootWindowContainer;
+        mService.getTransitionController().registerLegacyListener(
+                new WindowManagerInternal.AppTransitionListener() {
+                    @Override
+                    public int onAppTransitionStartingLocked(TransitionInfo info) {
+                        final List<TransitionInfo.Change> changes = info.getChanges();
+                        if (changes.size() == 0) {
+                            Slog.e(TAG, "TransitionInfo doesn't contain change: " + info);
+                            return 0;
+                        }
+                        final ActivityManager.RunningTaskInfo taskInfo =
+                                changes.get(0).getTaskInfo();
+                        if (taskInfo == null) {
+                            Slog.e(TAG, "No RunningTaskInfo: " + info);
+                            return 0;
+                        }
+
+                        // TODO(b/242856311): Filtering condition is defined here and in SysUI
+                        // Keyguard service, which need to be in sync. For a long term, we should
+                        // define a new API for notifying occlude status from WMS to SysUI, and
+                        // the filtering logic should only exist in WM Shell.
+                        if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) == 0) {
+                            return 0;
+                        }
+
+                        boolean occludeOpeningApp = false;
+                        boolean occludeClosingApp = false;
+                        for (int i = 0; i < changes.size(); ++i) {
+                            final TransitionInfo.Change change = changes.get(i);
+                            if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) {
+                                if (change.getMode() == TRANSIT_OPEN
+                                        || change.getMode() == TRANSIT_TO_FRONT) {
+                                    occludeOpeningApp = true;
+                                }
+                                if (change.getMode() == TRANSIT_CLOSE
+                                        || change.getMode() == TRANSIT_TO_BACK) {
+                                    occludeClosingApp = true;
+                                }
+                            }
+                        }
+                        final DisplayState state = getDisplayState(taskInfo.displayId);
+                        if (occludeOpeningApp && !occludeClosingApp) {
+                            state.commitOccludedStatus(true /* occluded */);
+                        } else if (!occludeOpeningApp && occludeClosingApp) {
+                            state.commitOccludedStatus(false /* occluded */);
+                        }
+                        return 0;
+                    }
+                });
     }
 
     boolean isAodShowing(int displayId) {
-        return getDisplayState(displayId).mAodShowing;
+        return getDisplayState(displayId).isIn(KEYGUARD_STATE_AOD_SHOWN);
     }
 
     /**
-     * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
-     *         on the given display, false otherwise.
+     * @return {@code true} if either Keyguard or AOD are showing.
      */
     boolean isKeyguardOrAodShowing(int displayId) {
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        return (state.mKeyguardShowing || state.mAodShowing)
-                && !state.mKeyguardGoingAway
-                && !isDisplayOccluded(displayId);
+        return getDisplayState(displayId).isIn(KEYGUARD_STATE_KEYGUARD_TOP);
     }
 
     /**
-     * @return {@code true} for default display when AOD is showing, not going away. Otherwise, same
-     *         as {@link #isKeyguardOrAodShowing(int)}
-     * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic.
+     * @return {@codd true} if lock screen is showing.
      */
-    boolean isKeyguardUnoccludedOrAodShowing(int displayId) {
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        if (displayId == DEFAULT_DISPLAY && state.mAodShowing) {
-            return !state.mKeyguardGoingAway;
-        }
-        return isKeyguardOrAodShowing(displayId);
+    boolean isLocksScreenShowing(int displayId) {
+        // NOTE: This is only used by WindowManagerService#notifyKeyguardTrustedChanged
+        return getDisplayState(displayId).isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
     }
 
     /**
-     * @return true if Keyguard is showing, not going away, and not being occluded on the given
-     *         display, false otherwise
-     */
-    boolean isKeyguardShowing(int displayId) {
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        return state.mKeyguardShowing && !state.mKeyguardGoingAway
-                && !isDisplayOccluded(displayId);
-    }
-
-    /**
-     * @return true if Keyguard is either showing or occluded, but not going away
+     * @return {@code true} if Keyguard is either showing or occluded.
      */
     boolean isKeyguardLocked(int displayId) {
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        return state.mKeyguardShowing && !state.mKeyguardGoingAway;
+        return getDisplayState(displayId).isIn(KEYGUARD_STATE_ON);
     }
 
     /**
-     *
      * @return true if the activity is controlling keyguard state.
      */
     boolean topActivityOccludesKeyguard(ActivityRecord r) {
-        return getDisplayState(r.getDisplayId()).mTopOccludesActivity == r;
+        return getDisplayState(r.getDisplayId()).topActivityOccludesKeyguard(r);
     }
 
     /**
      * @return {@code true} if the keyguard is going away, {@code false} otherwise.
      */
     boolean isKeyguardGoingAway(int displayId) {
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        // Also check keyguard showing in case value is stale.
-        return state.mKeyguardGoingAway && state.mKeyguardShowing;
+        return getDisplayState(displayId).isIn(KEYGUARD_STATE_GOING_AWAY);
     }
 
     /**
-     * Update the Keyguard showing state.
+     * Checks whether the top activity occludes the keyguard.
      */
-    void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) {
-        if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) {
-            Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId);
-            return;
-        }
-
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        final boolean aodChanged = aodShowing != state.mAodShowing;
-        final boolean aodRemoved = state.mAodShowing && !aodShowing;
-        // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
-        // Do not reset keyguardChanged status when only AOD is removed.
-        final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing)
-                || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved);
-        if (aodRemoved) {
-            updateDeferTransitionForAod(false /* waiting */);
-        }
-        if (!keyguardChanged && !aodChanged) {
-            setWakeTransitionReady();
-            return;
-        }
-        EventLogTags.writeWmSetKeyguardShown(
-                displayId,
-                keyguardShowing ? 1 : 0,
-                aodShowing ? 1 : 0,
-                state.mKeyguardGoingAway ? 1 : 0,
-                "setKeyguardShown");
-
-        // Update the task snapshot if the screen will not be turned off. To make sure that the
-        // unlocking animation can animate consistent content. The conditions are:
-        // - Either AOD or keyguard changes to be showing. So if the states change individually,
-        //   the later one can be skipped to avoid taking snapshot again. While it still accepts
-        //   if both of them change to show at the same time.
-        // - Keyguard was not going away. Because if it was, the closing transition is able to
-        //   handle the snapshot.
-        // - The display state is ON. Because if AOD is not on or pulsing, the display state will
-        //   be OFF or DOZE (the path of screen off may have handled it).
-        if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
-                && !state.mKeyguardGoingAway && Display.isOnState(
-                        mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
-            mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
-        }
-
-        state.mKeyguardShowing = keyguardShowing;
-        state.mAodShowing = aodShowing;
-
-        if (keyguardChanged) {
-            // Irrelevant to AOD.
-            state.mKeyguardGoingAway = false;
-            if (keyguardShowing) {
-                state.mDismissalRequested = false;
-            }
-        }
-
-        // Update the sleep token first such that ensureActivitiesVisible has correct sleep token
-        // state when evaluating visibilities.
-        updateKeyguardSleepToken();
-        mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-        InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */);
-        setWakeTransitionReady();
-        if (aodChanged) {
-            // Ensure the new state takes effect.
-            mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
-        }
-    }
-
-    private void setWakeTransitionReady() {
-        if (mWindowManager.mAtmService.getTransitionController().getCollectingTransitionType()
-                == WindowManager.TRANSIT_WAKE) {
-            mWindowManager.mAtmService.getTransitionController().setReady(
-                    mRootWindowContainer.getDefaultDisplay());
-        }
+    boolean isDisplayOccluded(int displayId) {
+        return getDisplayState(displayId).isIn(KEYGUARD_STATE_OCCLUDED);
     }
 
     /**
-     * Called when Keyguard is going away.
-     *
-     * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
-     *              etc.
+     * @return Whether the dream activity is on top of default display.
      */
-    void keyguardGoingAway(int displayId, int flags) {
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        if (!state.mKeyguardShowing || state.mKeyguardGoingAway) {
-            return;
-        }
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
-        mService.deferWindowLayout();
-        state.mKeyguardGoingAway = true;
-        try {
-            EventLogTags.writeWmSetKeyguardShown(
-                    displayId,
-                    1 /* keyguardShowing */,
-                    state.mAodShowing ? 1 : 0,
-                    1 /* keyguardGoingAway */,
-                    "keyguardGoingAway");
-            final int transitFlags = convertTransitFlags(flags);
-            final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
-            dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags);
-            // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
-            // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
-            // away.
-            dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
-                    TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc);
-            updateKeyguardSleepToken();
-
-            // Some stack visibility might change (e.g. docked stack)
-            mRootWindowContainer.resumeFocusedTasksTopActivities();
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-            mRootWindowContainer.addStartingWindowsForVisibleActivities();
-            mWindowManager.executeAppTransition();
-        } finally {
-            mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
-    void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
-        final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
-        if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
-            failCallback(callback);
-            return;
-        }
-        Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
-
-        // If the client has requested to dismiss the keyguard and the Activity has the flag to
-        // turn the screen on, wakeup the screen if it's the top Activity.
-        if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
-            mTaskSupervisor.wakeUp("dismissKeyguard");
-        }
-
-        mWindowManager.dismissKeyguard(callback, message);
-    }
-
-    private void failCallback(IKeyguardDismissCallback callback) {
-        try {
-            callback.onDismissError();
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to call callback", e);
-        }
-    }
-
-    private int convertTransitFlags(int keyguardGoingAwayFlags) {
-        int result = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-        }
-        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-        }
-        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-        }
-        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
-        }
-        if ((keyguardGoingAwayFlags
-                & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
-        }
-        return result;
-    }
-
-    /**
-     * @return True if we may show an activity while Keyguard is showing because we are in the
-     *         process of dismissing it anyways, false otherwise.
-     */
-    boolean canShowActivityWhileKeyguardShowing(ActivityRecord r) {
-        // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
-        // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
-        // Keyguard.
-        final KeyguardDisplayState state = getDisplayState(r.getDisplayId());
-        return r.containsDismissKeyguardWindow() && canDismissKeyguard() && !state.mAodShowing
-                && (state.mDismissalRequested
-                || (r.canShowWhenLocked() && state.mDismissingKeyguardActivity != r));
-    }
-
-    /**
-     * @return True if we may show an activity while Keyguard is occluded, false otherwise.
-     */
-    boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
-        return showWhenLocked || dismissKeyguard
-                && !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
+    boolean isShowingDream() {
+        return getDisplayState(DEFAULT_DISPLAY).isIn(KEYGUARD_STATE_DREAMING);
     }
 
     /**
@@ -351,181 +224,15 @@
      *
      * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
      */
-    boolean checkKeyguardVisibility(ActivityRecord r) {
+    boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
         if (r.mDisplayContent.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
             return true;
         }
-
-        if (isKeyguardOrAodShowing(r.mDisplayContent.getDisplayId())) {
-            // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
-            // right away and AOD isn't visible.
-            return canShowActivityWhileKeyguardShowing(r);
-        } else if (isKeyguardLocked(r.getDisplayId())) {
-            return canShowWhileOccluded(r.containsDismissKeyguardWindow(), r.canShowWhenLocked());
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Makes sure to update lockscreen occluded/dismiss/turnScreenOn state if needed before
-     * completing set all visibility
-     * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}).
-     */
-    void updateVisibility() {
-        for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
-             displayNdx >= 0; displayNdx--) {
-            final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
-            if (display.isRemoving() || display.isRemoved()) continue;
-            final KeyguardDisplayState state = getDisplayState(display.mDisplayId);
-            state.updateVisibility(this, display);
-            if (state.mRequestDismissKeyguard) {
-                handleDismissKeyguard(display.getDisplayId());
-            }
-        }
-    }
-
-    /**
-     * Called when occluded state changed.
-     *
-     * @param topActivity the activity that controls the state whether keyguard should
-     *      be occluded. That is the activity to be shown on top of keyguard if it requests so.
-     */
-    private void handleOccludedChanged(int displayId, @Nullable ActivityRecord topActivity) {
-        // TODO(b/113840485): Handle app transition for individual display, and apply occluded
-        // state change to secondary displays.
-        // For now, only default display fully supports occluded change. Other displays only
-        // updates keyguard sleep token on that display.
-        if (displayId != DEFAULT_DISPLAY) {
-            updateKeyguardSleepToken(displayId);
-            return;
-        }
-
-        mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
-        if (isKeyguardLocked(displayId)) {
-            mService.deferWindowLayout();
-            try {
-                mRootWindowContainer.getDefaultDisplay()
-                        .requestTransitionAndLegacyPrepare(
-                                isDisplayOccluded(DEFAULT_DISPLAY)
-                                        ? TRANSIT_KEYGUARD_OCCLUDE
-                                        : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
-                updateKeyguardSleepToken(DEFAULT_DISPLAY);
-                mWindowManager.executeAppTransition();
-            } finally {
-                mService.continueWindowLayout();
-            }
-        }
-        dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null
-                ? topActivity.getRootTask() : null);
-    }
-
-    /**
-     * Called when keyguard going away state changed.
-     */
-    private void handleKeyguardGoingAwayChanged(DisplayContent dc) {
-        mService.deferWindowLayout();
-        try {
-            dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, 0 /* transitFlags */);
-            // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
-            // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
-            // away.
-            dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
-                    TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, null /* trigger */, dc);
-            updateKeyguardSleepToken();
-            mWindowManager.executeAppTransition();
-        } finally {
-            mService.continueWindowLayout();
-        }
-    }
-
-    /**
-     * Called when somebody wants to dismiss the Keyguard via the flag.
-     */
-    private void handleDismissKeyguard(int displayId) {
-        // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
-        // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
-        // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
-        if (!mWindowManager.isKeyguardSecure(mService.getCurrentUserId())) {
-            return;
-        }
-
-        mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        state.mDismissalRequested = true;
-
-        // If we are about to unocclude the Keyguard, but we can dismiss it without security,
-        // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
-        final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
-        if (state.mKeyguardShowing && canDismissKeyguard()
-                && dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) {
-            mWindowManager.executeAppTransition();
-        }
-    }
-
-    boolean isDisplayOccluded(int displayId) {
-        return getDisplayState(displayId).mOccluded;
-    }
-
-    /**
-     * @return true if Keyguard can be currently dismissed without entering credentials.
-     */
-    boolean canDismissKeyguard() {
-        return mWindowManager.mPolicy.isKeyguardTrustedLw()
-                || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
-    }
-
-    /**
-     * @return Whether the dream activity is on top of default display.
-     */
-    boolean isShowingDream() {
-        return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
-    }
-
-    private void dismissMultiWindowModeForTaskIfNeeded(int displayId,
-            @Nullable Task currentTaskControllingOcclusion) {
-        // TODO(b/113840485): Handle docked stack for individual display.
-        if (!getDisplayState(displayId).mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
-            return;
-        }
-
-        // Dismiss freeform windowing mode
-        if (currentTaskControllingOcclusion == null) {
-            return;
-        }
-        if (currentTaskControllingOcclusion.inFreeformWindowingMode()) {
-            currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        }
-    }
-
-    private void updateKeyguardSleepToken() {
-        for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
-             displayNdx >= 0; displayNdx--) {
-            final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
-            updateKeyguardSleepToken(display.mDisplayId);
-        }
-    }
-
-    private void updateKeyguardSleepToken(int displayId) {
-        final KeyguardDisplayState state = getDisplayState(displayId);
-        if (isKeyguardUnoccludedOrAodShowing(displayId)) {
-            state.mSleepTokenAcquirer.acquire(displayId);
-        } else {
-            state.mSleepTokenAcquirer.release(displayId);
-        }
-    }
-
-    private KeyguardDisplayState getDisplayState(int displayId) {
-        KeyguardDisplayState state = mDisplayStates.get(displayId);
-        if (state == null) {
-            state = new KeyguardDisplayState(mService, displayId, mSleepTokenAcquirer);
-            mDisplayStates.append(displayId, state);
-        }
-        return state;
+        return getDisplayState(r.mDisplayContent.getDisplayId()).checkKeyguardVisibility(r);
     }
 
     void onDisplayRemoved(int displayId) {
-        final KeyguardDisplayState state = mDisplayStates.get(displayId);
+        final DisplayState state = mDisplayStates.get(displayId);
         if (state != null) {
             state.onRemoved();
             mDisplayStates.remove(displayId);
@@ -538,7 +245,15 @@
         }
     };
 
-    // Defer transition until AOD dismissed.
+    /**
+     * Update if app transition should be deferred until AOD state changes.
+     *
+     * <p>Note: This is used for defer app transition before the device fully wakes up, since during
+     * wake up process, activities life cycle can be messed up due to a display sleep token.
+     *
+     * @param waiting {@code true} to defer an app transition, {@code false} to continue an app
+     *        transition.
+     */
     void updateDeferTransitionForAod(boolean waiting) {
         if (waiting == mWaitingForWakeTransition) {
             return;
@@ -549,212 +264,1115 @@
         // if AOD is showing, defer the wake transition until AOD state changed.
         if (waiting && isAodShowing(DEFAULT_DISPLAY)) {
             mWaitingForWakeTransition = true;
-            mWindowManager.mAtmService.getTransitionController().deferTransitionReady();
+            mService.getTransitionController().deferTransitionReady();
             mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS);
         } else if (!waiting) {
             // dismiss the deferring if the AOD state change or cancel awake.
             mWaitingForWakeTransition = false;
-            mWindowManager.mAtmService.getTransitionController().continueTransitionReady();
+            mService.getTransitionController().continueTransitionReady();
             mWindowManager.mH.removeCallbacks(mResetWaitTransition);
         }
     }
 
-
-    /** Represents Keyguard state per individual display. */
-    private static class KeyguardDisplayState {
-        private final int mDisplayId;
-        private boolean mKeyguardShowing;
-        private boolean mAodShowing;
-        private boolean mKeyguardGoingAway;
-        private boolean mDismissalRequested;
-        private boolean mOccluded;
-        private boolean mShowingDream;
-
-        private ActivityRecord mTopOccludesActivity;
-        private ActivityRecord mDismissingKeyguardActivity;
-        private ActivityRecord mTopTurnScreenOnActivity;
-
-        private boolean mRequestDismissKeyguard;
-        private final ActivityTaskManagerService mService;
-        private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
-
-        KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
-                ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
-            mService = service;
-            mDisplayId = displayId;
-            mSleepTokenAcquirer = acquirer;
-        }
-
-        void onRemoved() {
-            mTopOccludesActivity = null;
-            mDismissingKeyguardActivity = null;
-            mTopTurnScreenOnActivity = null;
-            mSleepTokenAcquirer.release(mDisplayId);
-        }
-
-        /**
-         * Updates keyguard status if the top task could be visible. The top task may occlude
-         * keyguard, request to dismiss keyguard or make insecure keyguard go away based on its
-         * properties.
-         */
-        void updateVisibility(KeyguardController controller, DisplayContent display) {
-            final boolean lastOccluded = mOccluded;
-            final boolean lastKeyguardGoingAway = mKeyguardGoingAway;
-
-            final ActivityRecord lastDismissKeyguardActivity = mDismissingKeyguardActivity;
-            final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity;
-
-            mRequestDismissKeyguard = false;
-            mOccluded = false;
-            mShowingDream = false;
-
-            mTopOccludesActivity = null;
-            mDismissingKeyguardActivity = null;
-            mTopTurnScreenOnActivity = null;
-
-            boolean occludedByActivity = false;
-            final Task task = getRootTaskForControllingOccluding(display);
-            final ActivityRecord top = task != null ? task.getTopNonFinishingActivity() : null;
-            if (top != null) {
-                if (top.containsDismissKeyguardWindow()) {
-                    mDismissingKeyguardActivity = top;
-                }
-                if (top.getTurnScreenOnFlag() && top.currentLaunchCanTurnScreenOn()) {
-                    mTopTurnScreenOnActivity = top;
-                }
-
-                if (top.mDismissKeyguard && mKeyguardShowing) {
-                    mKeyguardGoingAway = true;
-                } else if (top.canShowWhenLocked()) {
-                    mTopOccludesActivity = top;
-                }
-                top.mDismissKeyguard = false;
-
-                // Only the top activity may control occluded, as we can't occlude the Keyguard
-                // if the top app doesn't want to occlude it.
-                occludedByActivity = mTopOccludesActivity != null
-                        || (mDismissingKeyguardActivity != null
-                        && task.topRunningActivity() == mDismissingKeyguardActivity
-                        && controller.canShowWhileOccluded(
-                                true /* dismissKeyguard */, false /* showWhenLocked */));
-                // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
-                if (mDisplayId != DEFAULT_DISPLAY) {
-                    occludedByActivity |= display.canShowWithInsecureKeyguard()
-                            && controller.canDismissKeyguard();
-                }
+    /**
+     * TODO(b/242851358): Remove this function once SysUI migrate to the new API.
+     */
+    @KeyguardState private static int convertToState(int displayId, boolean keyguardShowing,
+            boolean aodShowing) {
+        if (displayId == DEFAULT_DISPLAY) {
+            if (aodShowing) {
+                return KEYGUARD_STATE_AOD_SHOWN;
+            } else if (keyguardShowing) {
+                return KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+            } else {
+                return KEYGUARD_STATE_OFF;
             }
-
-            mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null
-                    && top.getActivityType() == ACTIVITY_TYPE_DREAM);
-            mOccluded = mShowingDream || occludedByActivity;
-            mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity
-                    && !mOccluded && !mKeyguardGoingAway
-                    && mDismissingKeyguardActivity != null;
-            if (mOccluded && mKeyguardShowing && !display.isSleeping() && !top.fillsParent()
-                    && display.mWallpaperController.getWallpaperTarget() == null) {
-                // The occluding activity may be translucent or not fill screen. Then let wallpaper
-                // to check whether it should set itself as target to avoid blank background.
-                display.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+        } else {
+            if (keyguardShowing || aodShowing) {
+                return KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+            } else {
+                return KEYGUARD_STATE_OFF;
             }
-
-            if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity
-                    && mTopTurnScreenOnActivity != null
-                    && !mService.mWindowManager.mPowerManager.isInteractive()
-                    && (mRequestDismissKeyguard || occludedByActivity)) {
-                controller.mTaskSupervisor.wakeUp("handleTurnScreenOn");
-                mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
-            }
-
-            boolean hasChange = false;
-            if (lastOccluded != mOccluded) {
-                controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
-                hasChange = true;
-            } else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
-                controller.handleKeyguardGoingAwayChanged(display);
-                hasChange = true;
-            }
-            // Collect the participates for shell transition, so that transition won't happen too
-            // early since the transition was set ready.
-            if (hasChange && top != null && (mOccluded || mKeyguardGoingAway)) {
-                display.mTransitionController.collect(top);
-            }
-        }
-
-        /**
-         * Gets the stack used to check the occluded state.
-         * <p>
-         * Only the top non-pinned activity of the focusable stack on each display can control its
-         * occlusion state.
-         */
-        @Nullable
-        private Task getRootTaskForControllingOccluding(DisplayContent display) {
-            return display.getRootTask(task ->
-                    task != null && task.isFocusableAndVisible() && !task.inPinnedWindowingMode());
-        }
-
-        void dumpStatus(PrintWriter pw, String prefix) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append(prefix);
-            sb.append(" KeyguardShowing=")
-                    .append(mKeyguardShowing)
-                    .append(" AodShowing=")
-                    .append(mAodShowing)
-                    .append(" KeyguardGoingAway=")
-                    .append(mKeyguardGoingAway)
-                    .append(" DismissalRequested=")
-                    .append(mDismissalRequested)
-                    .append("  Occluded=")
-                    .append(mOccluded)
-                    .append(" DismissingKeyguardActivity=")
-                    .append(mDismissingKeyguardActivity)
-                    .append(" TurnScreenOnActivity=")
-                    .append(mTopTurnScreenOnActivity)
-                    .append(" at display=")
-                    .append(mDisplayId);
-            pw.println(sb.toString());
-        }
-
-        void dumpDebug(ProtoOutputStream proto, long fieldId) {
-            final long token = proto.start(fieldId);
-            proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId);
-            proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING, mKeyguardShowing);
-            proto.write(KeyguardPerDisplayProto.AOD_SHOWING, mAodShowing);
-            proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, mOccluded);
-            proto.write(KeyguardPerDisplayProto.KEYGUARD_GOING_AWAY, mKeyguardGoingAway);
-            proto.end(token);
         }
     }
 
-    void dump(PrintWriter pw, String prefix) {
-        final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
-        pw.println(prefix + "KeyguardController:");
-        pw.println(prefix + "  mKeyguardShowing=" + default_state.mKeyguardShowing);
-        pw.println(prefix + "  mAodShowing=" + default_state.mAodShowing);
-        pw.println(prefix + "  mKeyguardGoingAway=" + default_state.mKeyguardGoingAway);
-        dumpDisplayStates(pw, prefix);
-        pw.println(prefix + "  mDismissalRequested=" + default_state.mDismissalRequested);
-        pw.println();
+    /**
+     * Update the Keyguard showing state.
+     *
+     * @deprecated Use {@link #setKeyguardState(int, int)} instead. See b/242851358
+     */
+    @Deprecated
+    void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) {
+        final DisplayState state = getDisplayState(displayId);
+        EventLogTags.writeWmSetKeyguardShown(
+                displayId,
+                keyguardShowing ? 1 : 0,
+                aodShowing ? 1 : 0,
+                state.isIn(KEYGUARD_STATE_GOING_AWAY) ? 1 : 0,
+                "setKeyguardShown");
+        setKeyguardState(displayId, convertToState(displayId, keyguardShowing, aodShowing));
+    }
+
+    /**
+     * Set keyguard state.
+     */
+    private void setKeyguardState(int displayId, @KeyguardState int newState) {
+        if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) {
+            Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId);
+            return;
+        }
+        if (newState != KEYGUARD_STATE_LOCKSCREEN_SHOWN
+                && newState != KEYGUARD_STATE_AOD_SHOWN
+                && newState != KEYGUARD_STATE_OFF
+                && newState != KEYGUARD_STATE_GOING_AWAY) {
+            Slog.i(TAG, "Invalid state is requested: displayId=" + displayId
+                    + ", state=" + keyguardStateToString(newState)
+                    + ", stack=" + Debug.getCallers(30));
+            return;
+        }
+        if (isKeyguardLocked(displayId) && newState == KEYGUARD_STATE_OFF) {
+            newState = KEYGUARD_STATE_GOING_AWAY;
+        }
+
+        final DisplayState state = getDisplayState(displayId);
+        // SysUI requests to show LOCKSCREEN, but the keyguard is already occluded. Ignore the
+        // requests.
+        if (state.isIn(KEYGUARD_STATE_OCCLUDED)
+                && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) {
+            Slog.w(TAG, "Ignore setKeyguardState request: OCCLUDE -> LOCK_SCREEN_SHOWN");
+            return;
+        }
+        // SysUI requests to show AOD_SHOWN again. This can happen when SysUI still uses the old
+        // API and enables AOD first, then lock screen, i.e. #setLockScreenShown(false, true), then
+        // #setLockScreenShown(true, true)
+        if (state.isIn(KEYGUARD_STATE_AOD_SHOWN)
+                && StateMachine.isIn(newState, KEYGUARD_STATE_AOD_SHOWN)) {
+            Slog.w(TAG, "Ignore setKeyguardState request: AOD_SHOWN -> AOD_SHOWN");
+            return;
+        }
+        if (state.isIn(KEYGUARD_STATE_OFF)
+                && StateMachine.isIn(newState, KEYGUARD_STATE_GOING_AWAY)) {
+            Slog.w(TAG, "Ignore setKeyguardState request: OFF -> GOING_AWAY");
+            return;
+        }
+        if (state.isIn(KEYGUARD_STATE_AOD_SHOWN)
+                && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) {
+            ActivityRecord top = getTopNonFinishingActivity(displayId);
+            if (canOcclude(top)) {
+                newState = isTopActivityDreaming(displayId) ? KEYGUARD_STATE_DREAMING
+                        : KEYGUARD_STATE_OCCLUDED;
+            }
+        }
+        state.setKeyguardState(newState);
+    }
+
+    /**
+     * Called when Keyguard is going away.
+     *
+     * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
+     *              etc.
+     *
+     * @deprecated Use {@link #setKeyguardState(int, int)}
+     */
+    void keyguardGoingAway(int displayId, int flags) {
+        // TODO(b/242851358): Remove IActivityTaskManagerService#keyguardGoingAway and SysUI should
+        // request the state change via #setKeyguardState.
+        final DisplayState state = getDisplayState(displayId);
+        EventLogTags.writeWmSetKeyguardShown(
+                displayId,
+                state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN) ? 1 : 0,
+                state.isIn(KEYGUARD_STATE_AOD_SHOWN) ? 1 : 0,
+                1 /* keyguardGoingAway */,
+                "keyguardGoingAway");
+        setKeyguardState(displayId, KEYGUARD_STATE_GOING_AWAY);
+    }
+
+    /**
+     * Makes sure to update lockscreen state if needed before completing set all visibility
+     * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}).
+     */
+    void updateVisibility() {
+        for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
+                displayNdx >= 0; displayNdx--) {
+            final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
+            if (display.isRemoving() || display.isRemoved()) continue;
+            final DisplayState state = getDisplayState(display.mDisplayId);
+            state.updateVisibility();
+        }
+    }
+
+    void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
+        boolean ok;
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r == null) {
+            ok = false;
+        } else {
+            final DisplayState state = getDisplayState(r.getDisplayId());
+            ok = state.dismissKeyguard(r, callback, message);
+        }
+
+        if (!ok) {
+            try {
+                callback.onDismissError();
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to call callback", e);
+            }
+        }
+    }
+
+    private boolean isKeyguardSecure() {
+        return mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
+    }
+
+    /**
+     * @return true if Keyguard can be currently dismissed without entering credentials.
+     */
+    private boolean canDismissKeyguard() {
+        return mWindowManager.mPolicy.isKeyguardTrustedLw() || !isKeyguardSecure();
+    }
+
+    private boolean canOcclude(@Nullable ActivityRecord r) {
+        if (r == null) {
+            return false;
+        }
+        // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
+        if (r.getDisplayId() != DEFAULT_DISPLAY) {
+            final DisplayContent dc = r.getDisplayContent();
+            if (dc != null && dc.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
+                return true;
+            }
+        }
+        // FLAG_DISMISS_KEYGUARD activity
+        //   Insecure: Treat as FLAG_SHOW_WHEN_LOCKED
+        //   Trusted: Actually dismiss Keyguard.
+        //   Secure: Show bouncer.
+        return r.canShowWhenLocked() || (r.containsDismissKeyguardWindow() && !isKeyguardSecure());
+    }
+
+    private boolean isTopActivityDreaming(int displayId) {
+        final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+        final ActivityRecord top = getTopNonFinishingActivity(displayId);
+        return dc.getDisplayPolicy().isShowingDreamLw()
+                && top != null && top.getActivityType() == ACTIVITY_TYPE_DREAM;
+    }
+
+    @Nullable private ActivityRecord getTopNonFinishingActivity(int displayId) {
+        final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+        final Task rootTask = dc == null ? null : dc.getRootTask(t ->
+                t != null && t.isFocusableAndVisible() && !t.inPinnedWindowingMode());
+        return rootTask != null ? rootTask.getTopNonFinishingActivity() : null;
+    }
+
+    private DisplayState getDisplayState(int displayId) {
+        DisplayState state = mDisplayStates.get(displayId);
+        if (state == null) {
+            state = new DisplayState(displayId, mServiceDelegate);
+            mDisplayStates.append(displayId, state);
+        }
+        return state;
     }
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
+        final DisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
         final long token = proto.start(fieldId);
-        proto.write(AOD_SHOWING, default_state.mAodShowing);
-        proto.write(KEYGUARD_SHOWING, default_state.mKeyguardShowing);
-        proto.write(KEYGUARD_GOING_AWAY, default_state.mKeyguardGoingAway);
+        proto.write(AOD_SHOWING, default_state.isIn(KEYGUARD_STATE_AOD_SHOWN));
+        proto.write(KEYGUARD_SHOWING, default_state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN));
         writeDisplayStatesToProto(proto, KEYGUARD_PER_DISPLAY);
         proto.end(token);
     }
 
-    private void dumpDisplayStates(PrintWriter pw, String prefix) {
-        for (int i = 0; i < mDisplayStates.size(); i++) {
-            mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
-        }
-    }
-
     private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
         for (int i = 0; i < mDisplayStates.size(); i++) {
             mDisplayStates.valueAt(i).dumpDebug(proto, fieldId);
         }
     }
+
+    void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix);
+        pw.println("KeyguardController:");
+        for (int i = 0; i < mDisplayStates.size(); i++) {
+            mDisplayStates.valueAt(i).dump(pw, prefix);
+        }
+        pw.println();
+    }
+
+    /**
+     * Interface for {@link DisplayState} to access non-local information.
+     * <p>
+     * Keep this interface as small as possible, and don't let {@link DisplayState} access arbitrary
+     * large classes such as ActivityTaskSupervisor, which makes managing dependency complicated.
+     */
+    private final class ServiceDelegate {
+        boolean isKeyguardSecure() {
+            return KeyguardController.this.isKeyguardSecure();
+        }
+
+        boolean canOcclude(@Nullable ActivityRecord r) {
+            return KeyguardController.this.canOcclude(r);
+        }
+
+        boolean canDismissKeyguard() {
+            return KeyguardController.this.canDismissKeyguard();
+        }
+
+        boolean isDeviceInteractive() {
+            return mService.mWindowManager.mPowerManager.isInteractive();
+        }
+
+        void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) {
+            mWindowManager.dismissKeyguard(callback, message);
+        }
+
+        @Nullable
+        ActivityRecord getTopNonFinishingActivity(int displayId) {
+            return KeyguardController.this.getTopNonFinishingActivity(displayId);
+        }
+
+        boolean isTopActivityDreaming(int displayId) {
+            return KeyguardController.this.isTopActivityDreaming(displayId);
+        }
+
+        void wakeUp(String reason) {
+            mTaskSupervisor.wakeUp(reason);
+        }
+
+        void forceSyncOccludedStatus(boolean occluded) {
+            if (DEBUG) {
+                Slog.d(TAG, "forceSyncOccludedStatus: occluded=" + occluded);
+            }
+            mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+            mWindowManager.mPolicy.applyKeyguardOcclusionChange(true /* notify */);
+        }
+
+        void snapshotForSleeping(int displayId) {
+            if (displayId == DEFAULT_DISPLAY) {
+                mWindowManager.mTaskSnapshotController.snapshotForSleeping(displayId);
+            }
+        }
+
+        void notifyKeyguardOccludeChanged(boolean occluded) {
+            // TODO: This updates status of KeyguardDelegate. Once we delete occlude status from
+            // KeyguardDelegate, we should remove WindowManagerPolicy#onKeyguardOccludedChangedLw.
+            mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+        }
+
+        void collect(@NonNull WindowContainer wc) {
+            mService.getTransitionController().collect(wc);
+        }
+
+        void requestTransitionIfNeeded(int displayId, @WindowManager.TransitionType int transit,
+                @WindowManager.TransitionFlags int flags) {
+            if (displayId != DEFAULT_DISPLAY) {
+                return;
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "requestTransitionIfNeeded: display=" + displayId + ", transit="
+                        + transitTypeToString(transit));
+            }
+
+            final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+            if (dc == null) {
+                Slog.e(TAG, "No DisplayContent exists: displayId=" + displayId);
+                return;
+            }
+
+            if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+                dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, flags);
+                // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
+                // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard
+                // going away.
+                mService.getTransitionController().requestTransitionIfNeeded(
+                        TRANSIT_TO_BACK, flags, null /* trigger */, dc);
+            } else {
+                dc.requestTransitionAndLegacyPrepare(transit, flags);
+            }
+        }
+
+        void acquireSleepToken(int displayId, boolean ensureActivitiesVisible) {
+            mSleepTokenAcquirer.acquire(displayId);
+            if (ensureActivitiesVisible) {
+                mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            }
+        }
+
+        void releaseSleepToken(int displayId, boolean resumeTopActivities) {
+            mSleepTokenAcquirer.release(displayId);
+            if (resumeTopActivities) {
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
+                mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+                mRootWindowContainer.addStartingWindowsForVisibleActivities();
+
+            }
+        }
+
+        void deferWindowLayout() {
+            mService.deferWindowLayout();
+        }
+
+        void continueWindowLayout() {
+            mService.continueWindowLayout();
+        }
+
+        void executeAppTransition() {
+            mWindowManager.executeAppTransition();
+        }
+
+        private void updateDeferTransitionForAod(boolean waiting) {
+            KeyguardController.this.updateDeferTransitionForAod(waiting);
+        }
+
+        private void setWakeTransitionReady() {
+            if (mService.getTransitionController().getCollectingTransitionType() == TRANSIT_WAKE) {
+                mService.getTransitionController().setReady(
+                        mRootWindowContainer.getDefaultDisplay());
+            }
+        }
+
+        void requestLayoutRedoWallpaper(int displayId) {
+            final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+            if (!dc.isSleeping() && dc.mWallpaperController.getWallpaperTarget() != null) {
+                dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+            }
+        }
+    };
+
+    private static class KeyguardDisplayStateMachine extends StateMachine {
+        static final int EVENT_DISMISS_KEYGUARD_ACTIVITY = 1;
+        static final int EVENT_SHOW_WHEN_LOCKED_ACTIVITY = 2;
+        static final int EVENT_CHECK_KEYGUARD_VISIBILITY = 3;
+        static final int EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD = 4;
+        static final int EVENT_LAYOUT_CHANGES = 5;
+        static final int EVENT_DUMP = 6;
+        static final int EVENT_DISMISS_KEYGUARD_API = 7;
+        static final int EVENT_TURN_SCREEN_ON_ACTIVITY = 8;
+        final int mDisplayId;
+
+        static final class CheckKeyguardVisibilityParam {
+            boolean mRet;
+            @NonNull final ActivityRecord mActivity;
+
+            CheckKeyguardVisibilityParam(@NonNull ActivityRecord activity) {
+                mActivity = activity;
+            }
+        }
+
+        static final class DismissKeyguardParam {
+            boolean mRet;
+            @NonNull final ActivityRecord mActivity;
+            @Nullable final IKeyguardDismissCallback mCallback;
+            @Nullable final CharSequence mMessage;
+
+            DismissKeyguardParam(@NonNull ActivityRecord activity,
+                    @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+                mActivity = activity;
+                mCallback = callback;
+                mMessage = message;
+            }
+        }
+
+        static final class TopActivityOccludesKeyguardParam {
+            boolean mRet;
+            @NonNull final ActivityRecord mActivity;
+
+            TopActivityOccludesKeyguardParam(@NonNull ActivityRecord activity) {
+                mActivity = activity;
+            }
+        }
+
+        static final class DumpParam {
+            ArrayList<String> mRet = new ArrayList<>();
+            String mPrefix;
+
+            DumpParam(@NonNull String prefix) {
+                mPrefix = prefix;
+            }
+        }
+
+        KeyguardDisplayStateMachine(int displayId, @KeyguardState int initialState) {
+            super(initialState);
+            mDisplayId = displayId;
+        }
+
+        @Nullable
+        @Override
+        public Handler addStateHandler(int state, Handler handler) {
+            Handler prevHandler = super.addStateHandler(state, handler);
+            if (prevHandler != null) {
+                throw new IllegalStateException(
+                        "Duplicate state handler registration: display=" + mDisplayId
+                                + ", state=" + state);
+            }
+            return null;
+        }
+
+        @Override
+        public void transit(@KeyguardState int newState) {
+            if (DEBUG) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("[ ");
+                for (Command cmd : getCommands()) {
+                    sb.append(cmd);
+                    sb.append(' ');
+                }
+                sb.append(" ]");
+                Slog.d(TAG, "State change: display=" + mDisplayId
+                        + ", current=" + keyguardStateToString(getCurrentState())
+                        + ", lastRequested=" + keyguardStateToString(getState())
+                        + ", newState=" + keyguardStateToString(newState)
+                        + ", command=" + sb
+                        + ", stack=" + Debug.getCallers(30));
+            }
+            super.transit(newState);
+        }
+
+        @Override
+        public void enter(@KeyguardState int state) {
+            if (DEBUG) {
+                Slog.d(TAG, "enter: display=" + mDisplayId + ", state="
+                        + keyguardStateToString(state));
+            }
+            super.enter(state);
+        }
+
+        @Override
+        public void exit(@KeyguardState int state) {
+            if (DEBUG) {
+                Slog.d(TAG, "exit: display=" + mDisplayId + ", state="
+                        + keyguardStateToString(state));
+            }
+            super.exit(state);
+        }
+
+        void handleDismissKeyguardActivity() {
+            handle(EVENT_DISMISS_KEYGUARD_ACTIVITY, null /* param */);
+        }
+
+        void handleTurnScreenOnActivity() {
+            handle(EVENT_TURN_SCREEN_ON_ACTIVITY, null /* param */);
+        }
+
+        boolean handleDismissKeyguard(@NonNull ActivityRecord r,
+                @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+            DismissKeyguardParam param = new DismissKeyguardParam(r, callback, message);
+            handle(EVENT_DISMISS_KEYGUARD_API, param);
+            return param.mRet;
+        }
+
+        void handleShowWhenLockedActivity() {
+            handle(EVENT_SHOW_WHEN_LOCKED_ACTIVITY, null /* param */);
+        }
+
+        void handleLayoutChanges() {
+            handle(EVENT_LAYOUT_CHANGES, null /* param */);
+        }
+
+        boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
+            final CheckKeyguardVisibilityParam param = new CheckKeyguardVisibilityParam(r);
+            handle(EVENT_CHECK_KEYGUARD_VISIBILITY, param);
+            return param.mRet;
+        }
+
+        boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) {
+            final TopActivityOccludesKeyguardParam param = new TopActivityOccludesKeyguardParam(r);
+            handle(EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD, param);
+            return param.mRet;
+        }
+
+        ArrayList<String> handleDump(String prefix) {
+            final DumpParam param = new DumpParam(prefix);
+            handle(EVENT_DUMP, param);
+            return param.mRet;
+        }
+    }
+
+    /**
+     * Helper class for implementing handler in type-safe way.
+     */
+    private abstract static class Handler implements StateMachine.Handler {
+        @Override
+        public final boolean handle(int event, @Nullable Object param) {
+            switch (event) {
+                case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_ACTIVITY:
+                    return handleDismissKeyguardActivity();
+                case KeyguardDisplayStateMachine.EVENT_TURN_SCREEN_ON_ACTIVITY:
+                    return handleTurnScreenOnActivity();
+                case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_API: {
+                    final KeyguardDisplayStateMachine.DismissKeyguardParam typedParam =
+                            (KeyguardDisplayStateMachine.DismissKeyguardParam) param;
+                    Optional<Boolean> ret = handleDismissKeyguard(typedParam.mActivity,
+                            typedParam.mCallback, typedParam.mMessage);
+                    if (ret.isPresent()) {
+                        typedParam.mRet = ret.get();
+                        return true;
+                    }
+                    return false;
+                }
+                case KeyguardDisplayStateMachine.EVENT_SHOW_WHEN_LOCKED_ACTIVITY:
+                    return handleShowWhenLockedActivity();
+                case KeyguardDisplayStateMachine.EVENT_CHECK_KEYGUARD_VISIBILITY: {
+                    final KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam typedParam =
+                            (KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam) param;
+                    Optional<Boolean> ret = checkKeyguardVisibility(typedParam.mActivity);
+                    if (ret.isPresent()) {
+                        typedParam.mRet = ret.get();
+                        return true;
+                    }
+                    return false;
+                }
+                case KeyguardDisplayStateMachine.EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD: {
+                    final KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam typedParam =
+                            (KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam) param;
+                    Optional<Boolean> ret = topActivityOccludesKeyguardParam(typedParam.mActivity);
+                    if (ret.isPresent()) {
+                        typedParam.mRet = ret.get();
+                        return true;
+                    }
+                    return false;
+                }
+                case KeyguardDisplayStateMachine.EVENT_LAYOUT_CHANGES:
+                    return handleLayoutChanges();
+                case KeyguardDisplayStateMachine.EVENT_DUMP:
+                    final KeyguardDisplayStateMachine.DumpParam typedParam =
+                            (KeyguardDisplayStateMachine.DumpParam) param;
+                    String dumpInfo = handleDump(typedParam.mPrefix);
+                    if (dumpInfo != null) {
+                        typedParam.mRet.add(dumpInfo);
+                    }
+                    // keep collecting information for dump up to top status.
+                    return false;
+                default:
+                    Slog.e(TAG, "Handler.handle(): Unknown event(" + event + ")");
+                    return false;
+            }
+        }
+
+        Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+            return Optional.empty();
+        }
+
+        Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+            return Optional.empty();
+        }
+
+        /**
+         * Handle flags in the activity which request to dismiss the keyguard.
+         *
+         * @see ActivityOptions#setDismissKeyguard()
+         * @see WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD
+         */
+        boolean handleDismissKeyguardActivity() {
+            return false;
+        }
+
+        /**
+         * Handle flags in the activity which request to turn the screen on. This must be called
+         * after dismiss keyguard flag is handled.
+         */
+        boolean handleTurnScreenOnActivity() {
+            return false;
+        }
+        /**
+         * Handle flags in the activity which decides if the activity can be shown on top of the
+         * keyguard.
+         *
+         * @see android.app.Activity#setShowWhenLocked(boolean)
+         * @see android.app.Activity#setInheritShowWhenLocked(boolean)
+         */
+        boolean handleShowWhenLockedActivity() {
+            return false;
+        }
+
+        /**
+         * Request relayout if necessary.
+         */
+        boolean handleLayoutChanges() {
+            return false;
+        }
+
+        /**
+         * Called when the activity requests to dismiss the keyguard via KeyguardManager APIs.
+         *
+         * @param r The activity which requested to dismiss the keyguard.
+         * @return Present if the state handles, delegate to its parent state otherwise. When the
+         *         value is present, the value is {@code true} if the keyguard dismiss request is
+         *         processed, {@code false} otherwise.
+         */
+        Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r,
+                @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+            return Optional.empty();
+        }
+
+        @Nullable String handleDump(@NonNull String prefix) {
+            return null;
+        }
+    }
+
+    private static class DisplayState {
+        private final int mDisplayId;
+        @NonNull private final ServiceDelegate mServiceDelegate;
+        private final KeyguardDisplayStateMachine mStateMachine;
+
+        // TODO: Set watchdog timer to sync mLastNotifiedOccludedState == isIn(OCCLUDED)
+        private boolean mLastNotifiedOccludedState = false;
+
+        // Top activity which has a window with FLAG_DISMISS_KEYGUARD flag. Valid only when the
+        // current state is KEYGUARD_STATE_ON or one of its sub states.
+        @Nullable private ActivityRecord mDismissingKeyguardActivity;
+
+        // KeyguardController has requested to dismiss keyguard via IWindowManager#dismissKeyguard.
+        // Reset this to false again, once the KeyguardController status is updated.
+        private boolean mDismissalRequested = false;
+
+        DisplayState(int displayId, @NonNull ServiceDelegate serviceDelegate) {
+            mDisplayId = displayId;
+            mServiceDelegate = serviceDelegate;
+            mStateMachine = new KeyguardDisplayStateMachine(displayId, KEYGUARD_STATE_OFF);
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_ROOT, new Handler() {
+                @Override
+                Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+                    return Optional.of(false);
+                }
+
+                @Override
+                Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+                    return Optional.of(false);
+                }
+            });
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_OFF, new Handler() {
+                @Override
+                public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+                    return Optional.of(true);
+                }
+
+                @Override
+                Optional<Boolean> handleDismissKeyguard(
+                        @NonNull ActivityRecord r, @Nullable IKeyguardDismissCallback callback,
+                        @Nullable CharSequence message) {
+                    // Keyguard is not shown, so we don't handle the request to dismiss the
+                    // keyguard.
+                    return Optional.of(false);
+                }
+            });
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_GOING_AWAY, new Handler() {
+                @Override
+                public void enter() {
+                    mServiceDelegate.deferWindowLayout();
+                    try {
+                        mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+                                TRANSIT_KEYGUARD_GOING_AWAY,
+                                TRANSIT_FLAG_KEYGUARD_GOING_AWAY
+                                        | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER);
+                        // Some stack visibility might change (e.g. docked stack)
+                        mServiceDelegate.releaseSleepToken(mDisplayId,
+                                true /* resumeTopActivities */);
+                        mServiceDelegate.executeAppTransition();
+                    } finally {
+                        mServiceDelegate.continueWindowLayout();
+                        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                    }
+                }
+            });
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_ON, new Handler() {
+                public boolean handleDismissKeyguardActivity() {
+                    final ActivityRecord lastDismissingKeyguardActivity =
+                            mDismissingKeyguardActivity;
+                    final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+                            mDisplayId);
+                    mDismissingKeyguardActivity =
+                            (top != null && top.containsDismissKeyguardWindow()) ? top : null;
+                    if (lastDismissingKeyguardActivity != mDismissingKeyguardActivity
+                            && mDismissingKeyguardActivity != null
+                            && mServiceDelegate.isKeyguardSecure()) {
+                        // We only allow dismissing Keyguard via the flag when Keyguard is secure
+                        // for legacy reasons, because that's how apps used to dismiss Keyguard in
+                        // the secure case. In the insecure case, we actually show it on top of the
+                        // lockscreen. See #canShowWhileOccluded.
+                        mDismissalRequested = true;
+                        mServiceDelegate.dismissKeyguard(null, null);
+                    }
+                    return true;
+                }
+
+                @Override
+                Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r,
+                        @Nullable IKeyguardDismissCallback callback,
+                        @Nullable CharSequence message) {
+                    if (!r.visibleIgnoringKeyguard) {
+                        return Optional.of(false);
+                    }
+                    if (DEBUG) {
+                        Slog.d(TAG, "Activity requesting to dismiss Keyguard: " + r);
+                    }
+                    // If the client has requested to dismiss the keyguard and the Activity has the
+                    // flag to turn the screen on, wakeup the screen if it's the top Activity.
+                    // Note that it's possible that the client requests to dismiss the keyguard
+                    // before the activity adds a window. In this case the flag set on the window
+                    // is not yet visible from ActivityRecord, so we need to check the flag again
+                    // when the activity adds a window later. See #handleTurnScreenOnActivity().
+                    if (r.getTurnScreenOnFlag() && r.isTopRunningActivity()) {
+                        mServiceDelegate.wakeUp("ON/handleDismissKeyguard");
+                        r.setCurrentLaunchCanTurnScreenOn(false);
+                    }
+                    mDismissalRequested = true;
+                    mServiceDelegate.dismissKeyguard(callback, message);
+                    return Optional.of(true);
+                }
+
+                @Override
+                public void enter() {
+                    // Update the task snapshot if the screen will not be turned off. To make sure
+                    // that the unlocking animation can animate consistent content.
+                    mServiceDelegate.snapshotForSleeping(mDisplayId);
+                }
+
+                @Nullable
+                @Override
+                String handleDump(@NonNull String prefix) {
+                    StringBuffer sb = new StringBuffer();
+                    sb.append(prefix)
+                            .append("  mDismissingKeyguardActivity=")
+                            .append(mDismissingKeyguardActivity)
+                            .append("\n");
+                    return sb.toString();
+                }
+            });
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_OCCLUDED, new Handler() {
+                ActivityRecord mTopOccludesActivity;
+
+                @Override
+                Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+                    return Optional.of(mServiceDelegate.canOcclude(activity));
+                }
+
+                @Override
+                public boolean handleShowWhenLockedActivity() {
+                    final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+                            mDisplayId);
+                    final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top)
+                            ? top : null;
+                    if (mTopOccludesActivity == topOccludesActivity) {
+                        return true;
+                    }
+                    // Launch SHOW_WHEN_LOCKED or INHERIT_SHOW_WHEN_LOCKED activity on top of an
+                    // occluding activity.
+                    mTopOccludesActivity = topOccludesActivity;
+                    if (mServiceDelegate.isTopActivityDreaming(mDisplayId)) {
+                        // Dream activity is launched on top of the previous SHOW_WHEN_LOCKED
+                        // activity.
+                        setKeyguardState(KEYGUARD_STATE_DREAMING);
+                    } else if (topOccludesActivity == null) {
+                        // SHOW_WHEN_LOCKED activity finishes.
+                        setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
+                    }
+                    return true;
+                }
+
+                @Override
+                boolean handleLayoutChanges() {
+                    // The occluding activity may be translucent or not fill screen. Then let
+                    // wallpaper to check whether it should set itself as target to avoid blank
+                    // background.
+                    if (!mTopOccludesActivity.fillsParent()) {
+                        mServiceDelegate.requestLayoutRedoWallpaper(mDisplayId);
+                    }
+                    return true;
+                }
+
+                @Override
+                Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+                    return Optional.of(mTopOccludesActivity == r);
+                }
+
+                @Override
+                public void enter() {
+                    mTopOccludesActivity = mServiceDelegate.getTopNonFinishingActivity(mDisplayId);
+                    if (!mServiceDelegate.canOcclude(mTopOccludesActivity)) {
+                        Slog.e(TAG, "enter(OCCLUDE): no occluding activity");
+                        setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
+                        return;
+                    }
+
+                    if (DEBUG) {
+                        Slog.d(TAG, "handleOccludedChanged: display=" + mDisplayId
+                                + ", topActivity=" + mTopOccludesActivity);
+                    }
+                    // Collect the participates for shell transition, so that transition won't
+                    // happen too early since the transition was set ready.
+                    mServiceDelegate.collect(mTopOccludesActivity);
+                    // TODO(b/113840485): Handle app transition for individual display, and apply
+                    // occluded state change to secondary displays. For now, only default display
+                    // fully supports occluded change. Other displays only updates keyguard sleep
+                    // token on that display.
+                    if (mDisplayId != DEFAULT_DISPLAY) {
+                        mServiceDelegate.releaseSleepToken(mDisplayId,
+                                false /* resumeTopActivities */);
+                        return;
+                    }
+
+                    if (mTopOccludesActivity.getTurnScreenOnFlag()
+                            && mTopOccludesActivity.currentLaunchCanTurnScreenOn()
+                            && !mServiceDelegate.isDeviceInteractive()) {
+                        mServiceDelegate.wakeUp("OCCLUDE/enter");
+                        mTopOccludesActivity.setCurrentLaunchCanTurnScreenOn(false);
+                    }
+
+                    mServiceDelegate.notifyKeyguardOccludeChanged(true /* occluded */);
+                    mServiceDelegate.deferWindowLayout();
+                    try {
+                        mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+                                TRANSIT_KEYGUARD_OCCLUDE, 0 /* flags */);
+                        mServiceDelegate.releaseSleepToken(mDisplayId,
+                                false /* resumeTopActivities */);
+                        mServiceDelegate.executeAppTransition();
+                    } finally {
+                        mServiceDelegate.continueWindowLayout();
+                    }
+                    // Dismiss freeform windowing mode
+                    final Task currentTaskControllingOcclusion = mTopOccludesActivity.getRootTask();
+                    if (currentTaskControllingOcclusion != null
+                            && currentTaskControllingOcclusion.inFreeformWindowingMode()) {
+                        currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                    }
+                }
+
+                @Override
+                public void exit() {
+                    mTopOccludesActivity = null;
+                    if (DEBUG) {
+                        Slog.d(TAG, "handleOccludedChanged: topActivity=" + null);
+                    }
+                    // TODO(b/113840485): Handle app transition for individual display, and apply
+                    // occluded state change to secondary displays.
+                    // For now, only default display fully supports occluded change. Other displays
+                    // only updates keyguard sleep token on that display.
+                    if (mDisplayId != DEFAULT_DISPLAY) {
+                        mServiceDelegate.acquireSleepToken(
+                                mDisplayId, false /* ensureActivitiesVisible */);
+                        return;
+                    }
+
+                    mServiceDelegate.notifyKeyguardOccludeChanged(false /* occluded */);
+                    mServiceDelegate.deferWindowLayout();
+                    try {
+                        mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+                                TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
+                        mServiceDelegate.acquireSleepToken(
+                                mDisplayId, false /* ensureActivitiesVisible */);
+                        mServiceDelegate.executeAppTransition();
+                    } finally {
+                        mServiceDelegate.continueWindowLayout();
+                    }
+                }
+
+                @Nullable
+                @Override
+                String handleDump(@NonNull String prefix) {
+                    StringBuffer sb = new StringBuffer();
+                    sb.append(prefix)
+                            .append("  mTopOccludesActivity=")
+                            .append(mTopOccludesActivity)
+                            .append("\n");
+                    return sb.toString();
+                }
+            });
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_KEYGUARD_TOP, new Handler() {
+                @Override
+                public boolean handleDismissKeyguardActivity() {
+                    final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+                            mDisplayId);
+                    if (top != null && top.mDismissKeyguard) {
+                        // Top activity has been launched with ActivityOptions#setDismissKeyguard.
+                        // Authentication has already been passed, so we can turn off the keyguard
+                        // immediately.
+                        top.mDismissKeyguard = false;
+                        setKeyguardState(KEYGUARD_STATE_GOING_AWAY);
+                        // Collect the participates for shell transition, so that transition won't
+                        // happen too early since the transition was set ready.
+                        mServiceDelegate.collect(top);
+                        return true;
+                    }
+                    return false;
+                }
+
+                @Override
+                public void enter() {
+                    mServiceDelegate.acquireSleepToken(mDisplayId,
+                            true /* ensureActivitiesVisible */);
+                    InputMethodManagerInternal.get().updateImeWindowStatus(
+                            false /* disableImeIcon */);
+                    mServiceDelegate.setWakeTransitionReady();
+                }
+
+                @Override
+                public void exit() {
+                    // Sleep token is released in enter() action in other states, since we need
+                    // to call requestTransition() before updating visibility of the activities.
+                }
+            });
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_LOCKSCREEN_SHOWN, new Handler() {
+                @Override
+                public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+                    // If lock screen is showing, nothing is visible, except if we are able to
+                    // dismiss Keyguard right away. This isn't allowed if r is already the
+                    // dismissing activity, in which case we don't allow it to repeatedly
+                    // dismiss Keyguard.
+                    return Optional.of(r.containsDismissKeyguardWindow()
+                            && mServiceDelegate.canDismissKeyguard()
+                            && (mDismissalRequested
+                            || (r.canShowWhenLocked() && mDismissingKeyguardActivity != r)));
+                }
+
+                @Override
+                public boolean handleShowWhenLockedActivity() {
+                    final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+                            mDisplayId);
+                    final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top)
+                            ? top : null;
+                    if (topOccludesActivity != null) {
+                        setKeyguardState(mServiceDelegate.isTopActivityDreaming(mDisplayId)
+                                ? KEYGUARD_STATE_DREAMING : KEYGUARD_STATE_OCCLUDED);
+                    }
+                    return true;
+                }
+            });
+
+            mStateMachine.addStateHandler(KEYGUARD_STATE_AOD_SHOWN, new Handler() {
+                // Top activity which has FLAG_TURN_SCREEN_ON flag.
+                @Nullable private ActivityRecord mTopTurnScreenOnActivity;
+
+                @Override
+                public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+                    return Optional.of(false);
+                }
+
+                @Override
+                boolean handleTurnScreenOnActivity() {
+                    final ActivityRecord lastTopTurnScreenOnActivity = mTopTurnScreenOnActivity;
+                    final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+                            mDisplayId);
+                    mTopTurnScreenOnActivity = (top != null && top.getTurnScreenOnFlag()
+                            && top.currentLaunchCanTurnScreenOn()) ? top : null;
+                    if (mTopTurnScreenOnActivity != lastTopTurnScreenOnActivity
+                            && mTopTurnScreenOnActivity != null
+                            && !mServiceDelegate.isDeviceInteractive()
+                            && mDismissalRequested) {
+                        mServiceDelegate.wakeUp("AOD_SHOWN/handleTurnScreenOnActivity");
+                        mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
+                    }
+                    return true;
+                }
+
+                @Override
+                public void enter() {
+                    if (mLastNotifiedOccludedState) {
+                        if (mDisplayId == DEFAULT_DISPLAY) {
+                            mServiceDelegate.forceSyncOccludedStatus(false);
+                        }
+                        commitOccludedStatus(false);
+                    }
+                }
+
+                @Override
+                public void exit() {
+                    mServiceDelegate.updateDeferTransitionForAod(false /* waiting */);
+                }
+
+                @Nullable
+                @Override
+                String handleDump(@NonNull String prefix) {
+                    StringBuffer sb = new StringBuffer();
+                    sb.append(prefix)
+                            .append("  mTopTurnScreenOnActivity=")
+                            .append(mTopTurnScreenOnActivity)
+                            .append("\n");
+                    return sb.toString();
+                }
+            });
+        }
+
+        void onRemoved() {
+            mServiceDelegate.releaseSleepToken(mDisplayId, false /* resumeTopActivities */);
+        }
+
+        void updateVisibility() {
+            mStateMachine.handleDismissKeyguardActivity();
+            mStateMachine.handleTurnScreenOnActivity();
+            mStateMachine.handleShowWhenLockedActivity();
+            mStateMachine.handleLayoutChanges();
+        }
+
+        boolean dismissKeyguard(@NonNull ActivityRecord r,
+                @Nullable IKeyguardDismissCallback callback,
+                @Nullable CharSequence message) {
+            return mStateMachine.handleDismissKeyguard(r, callback, message);
+        }
+
+        void commitOccludedStatus(boolean occluded) {
+            mLastNotifiedOccludedState = occluded;
+        }
+
+        void setKeyguardState(@KeyguardState int newState) {
+            mDismissalRequested = false;
+            mStateMachine.transit(newState);
+        }
+
+        boolean isKeyguardTop() {
+            return mStateMachine.isIn(KEYGUARD_STATE_KEYGUARD_TOP);
+        }
+
+        boolean isIn(@KeyguardState int category) {
+            return mStateMachine.isIn(category);
+        }
+
+        boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) {
+            return mStateMachine.topActivityOccludesKeyguard(r);
+        }
+
+        boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
+            return mStateMachine.checkKeyguardVisibility(r);
+        }
+
+        void dumpDebug(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId);
+            proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING,
+                    isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN));
+            proto.write(KeyguardPerDisplayProto.AOD_SHOWING, isIn(KEYGUARD_STATE_AOD_SHOWN));
+            proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, isIn(KEYGUARD_STATE_OCCLUDED));
+            proto.end(token);
+        }
+
+        void dump(PrintWriter pw, String prefix) {
+            StringBuffer sb = new StringBuffer();
+            sb.append(prefix)
+                    .append("* display=")
+                    .append(mDisplayId)
+                    .append("\n");
+            sb.append(prefix)
+                    .append("  state=")
+                    .append(keyguardStateToString(mStateMachine.getState()))
+                    .append("\n");
+            sb.append(prefix)
+                    .append("  mLastNotifiedOccludedState=")
+                    .append(mLastNotifiedOccludedState)
+                    .append("\n");
+            sb.append(prefix)
+                    .append("  mDismissalRequested=")
+                    .append(mDismissalRequested)
+                    .append("\n");
+            pw.print(sb.toString());
+
+            ArrayList<String> dumpInfo = mStateMachine.handleDump(prefix);
+            for (int i = dumpInfo.size() - 1; i >= 0; --i) {
+                pw.print(dumpInfo.get(i));
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 5e116ba..80965a7 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -730,7 +730,7 @@
 
     void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            // TODO(shell-transitions): handle (un)occlude transition.
+            mLegacyListeners.get(i).onAppTransitionStartingLocked(info);
             mLegacyListeners.get(i).onAppTransitionStartingLocked(
                     SystemClock.uptimeMillis() + statusBarTransitionDelay,
                     AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1282acb..ad6bd3c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -44,6 +44,7 @@
 import android.view.WindowInfo;
 import android.view.WindowManager.DisplayImePolicy;
 import android.view.inputmethod.ImeTracker;
+import android.window.TransitionInfo;
 
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.server.input.InputManagerService;
@@ -212,7 +213,7 @@
      * Abstract class to be notified about {@link com.android.server.wm.AppTransition} events. Held
      * as an abstract class so a listener only needs to implement the methods of its interest.
      */
-    public static abstract class AppTransitionListener {
+    public abstract static class AppTransitionListener {
 
         /**
          * Called when an app transition is being setup and about to be executed.
@@ -251,6 +252,20 @@
         }
 
         /**
+         * Called when an app transition gets started when WM shell is enabled.
+         *
+         * @param info Information about what is changing during a transition.
+         *
+         * @return Return any bit set of {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT},
+         * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG},
+         * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
+         * or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
+         */
+        public int onAppTransitionStartingLocked(TransitionInfo info) {
+            return 0;
+        }
+
+        /**
          * Called when an app transition is finished running.
          *
          * @param token the token for app whose transition has finished
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a596eed..f6cb068 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3132,7 +3132,7 @@
     @Override
     public void notifyKeyguardTrustedChanged() {
         synchronized (mGlobalLock) {
-            if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
+            if (mAtmService.mKeyguardController.isLocksScreenShowing(DEFAULT_DISPLAY)) {
                 mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
             }
         }
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 1859333..ec42324 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android/gui/IHdrConversionConstants.h>
 #include <android_util_Binder.h>
 #include <gui/SurfaceComposerClient.h>
 #include <jni.h>
@@ -55,6 +56,58 @@
     }
 }
 
+static void nativeSetHdrConversionMode(JNIEnv* env, jclass clazz, jint hdrConversionMode,
+                                       jint preferredHdrOutputType, jintArray autoHdrOutputTypes,
+                                       jint autoHdrOutputTypesLength) {
+    gui::HdrConversionStrategy hdrConversionStrategy;
+    switch (hdrConversionMode) {
+        case gui::IHdrConversionConstants::HdrConversionModePassthrough: {
+            hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::passthrough>(true);
+            break;
+        }
+        case gui::IHdrConversionConstants::HdrConversionModeAuto: {
+            jint* autoHdrOutputTypesArray = env->GetIntArrayElements(autoHdrOutputTypes, 0);
+            std::vector<int> autoHdrOutputTypesVector(autoHdrOutputTypesLength);
+            for (int i = 0; i < autoHdrOutputTypesLength; i++) {
+                autoHdrOutputTypesVector[i] = autoHdrOutputTypesArray[i];
+            }
+            hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::autoAllowedHdrTypes>(
+                    autoHdrOutputTypesVector);
+            break;
+        }
+        case gui::IHdrConversionConstants::HdrConversionModeForce: {
+            hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::forceHdrConversion>(
+                    preferredHdrOutputType);
+            break;
+        }
+    }
+
+    SurfaceComposerClient::setHdrConversionStrategy(hdrConversionStrategy);
+}
+
+static jintArray nativeGetSupportedHdrOutputTypes(JNIEnv* env, jclass clazz) {
+    std::vector<gui::HdrConversionCapability> hdrConversionCapabilities;
+    SurfaceComposerClient::getHdrConversionCapabilities(&hdrConversionCapabilities);
+
+    // Extract unique HDR output types.
+    std::set<int> hdrOutputTypes;
+    for (const auto& hdrConversionCapability : hdrConversionCapabilities) {
+        hdrOutputTypes.insert(hdrConversionCapability.outputType);
+    }
+    jintArray array = env->NewIntArray(hdrOutputTypes.size());
+    if (array == nullptr) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+        return nullptr;
+    }
+    jint* arrayValues = env->GetIntArrayElements(array, 0);
+    int index = 0;
+    for (auto hdrOutputType : hdrOutputTypes) {
+        arrayValues[index++] = static_cast<jint>(hdrOutputType);
+    }
+    env->ReleaseIntArrayElements(array, arrayValues, 0);
+    return array;
+}
+
 static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
     const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
     ScopedLongArrayRW values(env, env->NewLongArray(displayIds.size()));
@@ -91,6 +144,10 @@
             (void*)nativeGetPhysicalDisplayIds },
     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
             (void*)nativeGetPhysicalDisplayToken },
+    {"nativeSetHdrConversionMode", "(II[II)V",
+            (void*)nativeSetHdrConversionMode },
+    {"nativeGetSupportedHdrOutputTypes", "()[I",
+            (void*)nativeGetSupportedHdrOutputTypes },
         // clang-format on
 };
 
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 7f85347..a27eb7f 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -202,12 +202,15 @@
     </xs:simpleType>
 
     <xs:complexType name="thermalThrottling">
-        <xs:complexType>
+        <xs:sequence>
             <xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap">
                 <xs:annotation name="nonnull"/>
                 <xs:annotation name="final"/>
             </xs:element>
-        </xs:complexType>
+            <xs:element type="brightnessThrottlingMap" name="concurrentDisplaysBrightnessThrottlingMap">
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:sequence>
     </xs:complexType>
 
     <xs:complexType name="brightnessThrottlingMap">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index cb08179..6c66d0d 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -237,7 +237,9 @@
   public class ThermalThrottling {
     ctor public ThermalThrottling();
     method @NonNull public final com.android.server.display.config.BrightnessThrottlingMap getBrightnessThrottlingMap();
+    method public final com.android.server.display.config.BrightnessThrottlingMap getConcurrentDisplaysBrightnessThrottlingMap();
     method public final void setBrightnessThrottlingMap(@NonNull com.android.server.display.config.BrightnessThrottlingMap);
+    method public final void setConcurrentDisplaysBrightnessThrottlingMap(com.android.server.display.config.BrightnessThrottlingMap);
   }
 
   public class ThresholdPoint {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index b7c5fc2..865cba8 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -17,11 +17,8 @@
 package com.android.server.credentials;
 
 import android.credentials.CredentialDescription;
-import android.credentials.IRegisterCredentialDescriptionCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
-import android.os.RemoteException;
 import android.util.SparseArray;
 
 import java.util.HashMap;
@@ -61,8 +58,7 @@
     /** Handle the given {@link RegisterCredentialDescriptionRequest} by creating
      * the appropriate package name mapping. */
     public void executeRegisterRequest(RegisterCredentialDescriptionRequest request,
-            String callingPackageName,
-            IRegisterCredentialDescriptionCallback callback) {
+            String callingPackageName) {
 
         if (!mCredentialDescriptions.containsKey(callingPackageName)
                 && mCredentialDescriptions.size() <= MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS) {
@@ -71,30 +67,17 @@
 
         mCredentialDescriptions.get(callingPackageName)
                 .addAll(request.getCredentialDescriptions());
-
-        try {
-            callback.onResponse();
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
     }
 
     /** Handle the given {@link UnregisterCredentialDescriptionRequest} by creating
      * the appropriate package name mapping. */
     public void executeUnregisterRequest(
             UnregisterCredentialDescriptionRequest request,
-            String callingPackageName,
-            IUnregisterCredentialDescriptionCallback callback) {
+            String callingPackageName) {
 
         if (mCredentialDescriptions.containsKey(callingPackageName)) {
             mCredentialDescriptions.get(callingPackageName)
-                    .remove(request.getCredentialDescription());
-        }
-
-        try {
-            callback.onResponse();
-        } catch (RemoteException e) {
-            e.printStackTrace();
+                    .removeAll(request.getCredentialDescriptions());
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 620b81b..bb26fa9 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -28,6 +28,7 @@
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialException;
 import android.credentials.CreateCredentialRequest;
+import android.credentials.CredentialDescription;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialOption;
 import android.credentials.GetCredentialRequest;
@@ -36,9 +37,7 @@
 import android.credentials.ICredentialManager;
 import android.credentials.IGetCredentialCallback;
 import android.credentials.IListEnabledProvidersCallback;
-import android.credentials.IRegisterCredentialDescriptionCallback;
 import android.credentials.ISetEnabledProvidersCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
 import android.credentials.ListEnabledProvidersResponse;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
@@ -274,8 +273,8 @@
                 request.getGetCredentialOptions().stream().map(
                         getCredentialOption -> getCredentialOption
                                         .getCredentialRetrievalData()
-                                        .getString(RegisterCredentialDescriptionRequest
-                                                .FLATTENED_REQUEST_STRING_KEY))
+                                        .getString(GetCredentialOption
+                                                .FLATTENED_REQUEST))
                         .collect(Collectors.toSet());
 
         // All requested credential descriptions based on the given request.
@@ -543,12 +542,10 @@
         }
 
         @Override
-        public ICancellationSignal registerCredentialDescription(
-                RegisterCredentialDescriptionRequest request,
-                IRegisterCredentialDescriptionCallback callback, String callingPackage) {
+        public void registerCredentialDescription(
+                RegisterCredentialDescriptionRequest request, String callingPackage)
+                throws IllegalArgumentException , NonCredentialProviderCallerException {
             Log.i(TAG, "registerCredentialDescription");
-            ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-
 
             List<CredentialProviderInfo> services =
                     CredentialProviderInfo.getAvailableServices(mContext,
@@ -557,31 +554,37 @@
             List<String> providers = services.stream()
                     .map(credentialProviderInfo
                             -> credentialProviderInfo.getServiceInfo().packageName).toList();
+
             if (!providers.contains(callingPackage)) {
-                try {
-                    callback.onError("UNKNOWN",
-                            "Not an existing provider.");
-                } catch (RemoteException e) {
-                    Log.i(
-                            TAG,
-                            "Issue invoking onError on IRegisterCredentialDescriptionCallback "
-                                    + "callback: "
-                                    + e.getMessage());
-                }
+                throw new NonCredentialProviderCallerException(callingPackage);
+            }
+
+            List<CredentialProviderInfo> matchingService = services.stream().filter(
+                    credentialProviderInfo ->
+                            credentialProviderInfo.getServiceInfo()
+                                    .packageName.equals(callingPackage)).toList();
+
+            CredentialProviderInfo credentialProviderInfo = matchingService.get(0);
+
+            Set<String> supportedTypes = request.getCredentialDescriptions()
+                    .stream().map(CredentialDescription::getType).filter(
+                            credentialProviderInfo::hasCapability).collect(Collectors.toSet());
+
+            if (supportedTypes.size() != request.getCredentialDescriptions().size()) {
+                throw new IllegalArgumentException("CredentialProvider does not support one or more"
+                        + "of the registered types. Check your XML entry.");
             }
 
             CredentialDescriptionRegistry session = CredentialDescriptionRegistry
                     .forUser(UserHandle.getCallingUserId());
 
-            session.executeRegisterRequest(request, callingPackage, callback);
-
-            return cancelTransport;
+            session.executeRegisterRequest(request, callingPackage);
         }
 
         @Override
-        public ICancellationSignal unRegisterCredentialDescription(
-                UnregisterCredentialDescriptionRequest request,
-                IUnregisterCredentialDescriptionCallback callback, String callingPackage) {
+        public void unregisterCredentialDescription(
+                UnregisterCredentialDescriptionRequest request, String callingPackage)
+                throws IllegalArgumentException {
             Log.i(TAG, "registerCredentialDescription");
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
 
@@ -594,24 +597,13 @@
                             -> credentialProviderInfo.getServiceInfo().packageName).toList();
 
             if (!providers.contains(callingPackage)) {
-                try {
-                    callback.onError("UNKNOWN",
-                            "Not an existing provider.");
-                } catch (RemoteException e) {
-                    Log.i(
-                            TAG,
-                            "Issue invoking onError on IRegisterCredentialDescriptionCallback "
-                                    + "callback: "
-                                    + e.getMessage());
-                }
+                throw new NonCredentialProviderCallerException(callingPackage);
             }
 
             CredentialDescriptionRegistry session = CredentialDescriptionRegistry
                     .forUser(UserHandle.getCallingUserId());
 
-            session.executeUnregisterRequest(request, callingPackage, callback);
-
-            return cancelTransport;
+            session.executeUnregisterRequest(request, callingPackage);
         }
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java b/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java
new file mode 100644
index 0000000..383d0aa
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials;
+
+/**
+ * Thrown when the calling app is not a Credential Provider.
+ */
+public class NonCredentialProviderCallerException extends RuntimeException {
+
+    private static final String MESSAGE = " is not an existing Credential Provider.";
+
+    public NonCredentialProviderCallerException(String caller) {
+        super(caller + MESSAGE);
+    }
+}
diff --git a/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
similarity index 77%
rename from services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
rename to services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
index e5a1cfe..4949091 100644
--- a/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
@@ -26,7 +26,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.NtpTrustedTime;
 
-import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -41,16 +41,16 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Unit tests for {@link NtpTimeHelper}.
+ * Unit tests for {@link NtpNetworkTimeHelper}.
  */
 @RunWith(RobolectricTestRunner.class)
 @Presubmit
-public class NtpTimeHelperTest {
+public class NtpNetworkTimeHelperTest {
 
     private static final long MOCK_NTP_TIME = 1519930775453L;
     @Mock
     private NtpTrustedTime mMockNtpTrustedTime;
-    private NtpTimeHelper mNtpTimeHelper;
+    private NtpNetworkTimeHelper mNtpNetworkTimeHelper;
     private CountDownLatch mCountDownLatch;
 
     /**
@@ -60,12 +60,12 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mCountDownLatch = new CountDownLatch(1);
-        InjectNtpTimeCallback callback =
+        InjectTimeCallback callback =
                 (time, timeReference, uncertainty) -> {
                     assertThat(time).isEqualTo(MOCK_NTP_TIME);
                     mCountDownLatch.countDown();
                 };
-        mNtpTimeHelper = new NtpTimeHelper(RuntimeEnvironment.application,
+        mNtpNetworkTimeHelper = new NtpNetworkTimeHelper(RuntimeEnvironment.application,
                 Looper.myLooper(),
                 callback, mMockNtpTrustedTime);
     }
@@ -74,13 +74,13 @@
      * Verify that cached time is returned if cached age is low.
      */
     @Test
-    public void handleInjectNtpTime_cachedAgeLow_injectTime() throws InterruptedException {
+    public void demandUtcTimeInjection_cachedAgeLow_injectTime() throws InterruptedException {
         NtpTrustedTime.TimeResult result = mock(NtpTrustedTime.TimeResult.class);
-        doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
+        doReturn(NtpNetworkTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
         doReturn(MOCK_NTP_TIME).when(result).getTimeMillis();
         doReturn(result).when(mMockNtpTrustedTime).getCachedTimeResult();
 
-        mNtpTimeHelper.retrieveAndInjectNtpTime();
+        mNtpNetworkTimeHelper.demandUtcTimeInjection();
 
         waitForTasksToBePostedOnHandlerAndRunThem();
         assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
@@ -90,14 +90,14 @@
      * Verify that failed inject time and delayed inject time are handled properly.
      */
     @Test
-    public void handleInjectNtpTime_injectTimeFailed_injectTimeDelayed()
+    public void demandUtcTimeInjection_injectTimeFailed_injectTimeDelayed()
             throws InterruptedException {
         NtpTrustedTime.TimeResult result1 = mock(NtpTrustedTime.TimeResult.class);
-        doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
+        doReturn(NtpNetworkTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
         doReturn(result1).when(mMockNtpTrustedTime).getCachedTimeResult();
         doReturn(false).when(mMockNtpTrustedTime).forceRefresh();
 
-        mNtpTimeHelper.retrieveAndInjectNtpTime();
+        mNtpNetworkTimeHelper.demandUtcTimeInjection();
         waitForTasksToBePostedOnHandlerAndRunThem();
         assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse();
 
@@ -106,15 +106,15 @@
         doReturn(1L).when(result2).getAgeMillis();
         doReturn(MOCK_NTP_TIME).when(result2).getTimeMillis();
         doReturn(result2).when(mMockNtpTrustedTime).getCachedTimeResult();
-        SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
+        SystemClock.sleep(NtpNetworkTimeHelper.RETRY_INTERVAL);
 
         waitForTasksToBePostedOnHandlerAndRunThem();
         assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
     }
 
     /**
-     * Since a thread is created in {@link NtpTimeHelper#retrieveAndInjectNtpTime} and the task to
-     * be verified is posted in the thread, we have to wait for the task to be posted and then it
+     * Since a thread is created in {@link NtpNetworkTimeHelper#demandUtcTimeInjection} and the task
+     * to be verified is posted in the thread, we have to wait for the task to be posted and then it
      * can be run.
      */
     private void waitForTasksToBePostedOnHandlerAndRunThem() throws InterruptedException {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ced2a4b..3a7b9a4 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -62,6 +62,7 @@
         "junit-params",
         "ActivityContext",
         "coretests-aidl",
+        "securebox",
     ],
 
     libs: [
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 908e717..26d681b 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -38,6 +38,9 @@
             crossProfileIntentResolutionStrategy='0'
             mediaSharedWithParent='true'
             credentialShareableWithParent='false'
+            showInSettings='23'
+            inheritDevicePolicy='450'
+            deleteAppWithParent='false'
         />
     </profile-type>
     <profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java
new file mode 100644
index 0000000..a488ab4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CallMetadataSyncInCallServiceTest {
+
+    private CallMetadataSyncInCallService mSyncInCallService;
+    @Mock
+    private CrossDeviceCall mMockCrossDeviceCall;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mSyncInCallService = new CallMetadataSyncInCallService();
+    }
+
+    @Test
+    public void getCallForId_invalid() {
+        when(mMockCrossDeviceCall.getId()).thenReturn(-1L);
+        final CrossDeviceCall call = mSyncInCallService.getCallForId(-1L,
+                List.of(mMockCrossDeviceCall));
+        assertWithMessage("Unexpectedly found a match for call id").that(call).isNull();
+    }
+
+    @Test
+    public void getCallForId_noMatch() {
+        when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+        final CrossDeviceCall call = mSyncInCallService.getCallForId(1L,
+                List.of(mMockCrossDeviceCall));
+        assertWithMessage("Unexpectedly found a match for call id").that(call).isNull();
+    }
+
+    @Test
+    public void getCallForId_hasMatch() {
+        when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+        final CrossDeviceCall call = mSyncInCallService.getCallForId(5L,
+                List.of(mMockCrossDeviceCall));
+        assertWithMessage("Unexpectedly did not find a match for call id").that(call).isNotNull();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index ddde385..fc25152 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -484,6 +484,7 @@
         // Sensor reads 100 lux. We should get max brightness.
         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+        assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
 
         // Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
         final float throttledBrightness = 0.123f;
@@ -494,6 +495,8 @@
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
                 /* shouldResetShortTermModel= */ true);
         assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
+        // The raw brightness value should not have throttling applied
+        assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
 
         // Remove throttling and notify ABC again
         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -503,6 +506,7 @@
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
                 /* shouldResetShortTermModel= */ true);
         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+        assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 91fd76e..4bc8242 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -32,6 +32,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
+import com.android.server.display.config.ThermalStatus;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,6 +44,8 @@
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -164,6 +167,51 @@
         assertArrayEquals(new int[]{-1, 10, 20, 30, 40},
                 mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
 
+        List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+                new ArrayList();
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.4f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.3f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.2f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.1f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.05f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.025f
+        ));
+        assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
+                mDisplayDeviceConfig.getBrightnessThrottlingData());
+
+        throttlingLevels.clear();
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.2f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.15f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.1f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.05f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.025f
+        ));
+        throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+                DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.0125f
+        ));
+        assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
+                mDisplayDeviceConfig.getConcurrentDisplaysBrightnessThrottlingData());
+
         // Todo: Add asserts for BrightnessThrottlingData, DensityMapping,
         // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
     }
@@ -428,16 +476,60 @@
                 +   "<ambientLightHorizonShort>50</ambientLightHorizonShort>\n"
                 +   "<screenBrightnessRampIncreaseMaxMillis>"
                 +       "2000"
-                +    "</screenBrightnessRampIncreaseMaxMillis>\n"
+                +   "</screenBrightnessRampIncreaseMaxMillis>\n"
                 +   "<thermalThrottling>\n"
                 +       "<brightnessThrottlingMap>\n"
                 +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>light</thermalStatus>\n"
+                +               "<brightness>0.4</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>moderate</thermalStatus>\n"
+                +               "<brightness>0.3</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>severe</thermalStatus>\n"
+                +               "<brightness>0.2</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>critical</thermalStatus>\n"
+                +               "<brightness>0.1</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
                 +               "<thermalStatus>emergency</thermalStatus>\n"
-                +               "<!-- Throttling to 250 nits: (250-2.0)/(500-2.0)*(0.62-0.0)+0"
-                +               ".0 = 0.30875502 -->\n"
-                +               "<brightness>0.30875502</brightness>\n"
+                +               "<brightness>0.05</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>shutdown</thermalStatus>\n"
+                +               "<brightness>0.025</brightness>\n"
                 +           "</brightnessThrottlingPoint>\n"
                 +       "</brightnessThrottlingMap>\n"
+                +       "<concurrentDisplaysBrightnessThrottlingMap>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>light</thermalStatus>\n"
+                +               "<brightness>0.2</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>moderate</thermalStatus>\n"
+                +               "<brightness>0.15</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>severe</thermalStatus>\n"
+                +               "<brightness>0.1</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>critical</thermalStatus>\n"
+                +               "<brightness>0.05</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>emergency</thermalStatus>\n"
+                +               "<brightness>0.025</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +           "<brightnessThrottlingPoint>\n"
+                +               "<thermalStatus>shutdown</thermalStatus>\n"
+                +               "<brightness>0.0125</brightness>\n"
+                +           "</brightnessThrottlingPoint>\n"
+                +       "</concurrentDisplaysBrightnessThrottlingMap>\n"
                 +   "</thermalThrottling>\n"
                 +   "<refreshRate>\n"
                 +       "<lowerBlockingZoneConfigs>\n"
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index ea5caa8..cc1100b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -55,6 +55,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index 19a606e..1cf4471 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -28,6 +28,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
+
 import com.google.common.collect.ImmutableMap;
 
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 1b983f0b..bb79fd8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -60,6 +60,7 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.security.SecureBox;
 import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
 import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
index a1cf128..05d30ed 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
@@ -23,7 +23,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.locksettings.recoverablekeystore.SecureBox;
+import com.android.security.SecureBox;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index ade1bd4..2675f05 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -66,6 +66,7 @@
                 .setCrossProfileIntentResolutionStrategy(0)
                 .setMediaSharedWithParent(false)
                 .setCredentialShareableWithParent(true)
+                .setDeleteAppWithParent(false)
                 .build();
         final UserProperties actualProps = new UserProperties(defaultProps);
         actualProps.setShowInLauncher(14);
@@ -76,6 +77,7 @@
         actualProps.setCrossProfileIntentResolutionStrategy(1);
         actualProps.setMediaSharedWithParent(true);
         actualProps.setCredentialShareableWithParent(false);
+        actualProps.setDeleteAppWithParent(true);
 
         // Write the properties to xml.
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -116,12 +118,14 @@
                 .setShowInSettings(3452)
                 .setInheritDevicePolicy(1732)
                 .setMediaSharedWithParent(true)
+                .setDeleteAppWithParent(true)
                 .build();
         final UserProperties orig = new UserProperties(defaultProps);
         orig.setShowInLauncher(2841);
         orig.setStartWithParent(false);
         orig.setShowInSettings(1437);
         orig.setInheritDevicePolicy(9456);
+        orig.setDeleteAppWithParent(false);
 
         // Test every permission level. (Currently, it's linear so it's easy.)
         for (int permLevel = 0; permLevel < 4; permLevel++) {
@@ -163,6 +167,8 @@
                 copy::getCrossProfileIntentFilterAccessControl, exposeAll);
         assertEqualGetterOrThrows(orig::getCrossProfileIntentResolutionStrategy,
                 copy::getCrossProfileIntentResolutionStrategy, exposeAll);
+        assertEqualGetterOrThrows(orig::getDeleteAppWithParent,
+                copy::getDeleteAppWithParent, exposeAll);
 
         // Items requiring hasManagePermission - put them here using hasManagePermission.
         assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -227,5 +233,6 @@
                 .isEqualTo(actual.isMediaSharedWithParent());
         assertThat(expected.isCredentialShareableWithParent())
                 .isEqualTo(actual.isCredentialShareableWithParent());
+        assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 702059d..ff9a79e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -88,7 +88,11 @@
                 .setCrossProfileIntentFilterAccessControl(10)
                 .setCrossProfileIntentResolutionStrategy(1)
                 .setMediaSharedWithParent(true)
-                .setCredentialShareableWithParent(false);
+                .setCredentialShareableWithParent(false)
+                .setShowInSettings(900)
+                .setInheritDevicePolicy(340)
+                .setDeleteAppWithParent(true);
+
         final UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("a.name")
                 .setEnabled(1)
@@ -152,6 +156,10 @@
                 .getCrossProfileIntentResolutionStrategy());
         assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
         assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
+        assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
+        assertEquals(340, type.getDefaultUserPropertiesReference()
+                .getInheritDevicePolicy());
+        assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
 
         assertEquals(23, type.getBadgeLabel(0));
         assertEquals(24, type.getBadgeLabel(1));
@@ -287,7 +295,11 @@
                 .setCrossProfileIntentFilterAccessControl(10)
                 .setCrossProfileIntentResolutionStrategy(1)
                 .setMediaSharedWithParent(false)
-                .setCredentialShareableWithParent(true);
+                .setCredentialShareableWithParent(true)
+                .setShowInSettings(20)
+                .setInheritDevicePolicy(21)
+                .setDeleteAppWithParent(true);
+
         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
         builders.put(userTypeAosp1, new UserTypeDetails.Builder()
                 .setName(userTypeAosp1)
@@ -323,6 +335,10 @@
         assertFalse(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
         assertTrue(aospType.getDefaultUserPropertiesReference()
                 .isCredentialShareableWithParent());
+        assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+        assertEquals(21, aospType.getDefaultUserPropertiesReference()
+                .getInheritDevicePolicy());
+        assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
 
         // userTypeAosp2 should be modified.
         aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -362,6 +378,11 @@
         assertTrue(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
         assertFalse(aospType.getDefaultUserPropertiesReference()
                 .isCredentialShareableWithParent());
+        assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+        assertEquals(450, aospType.getDefaultUserPropertiesReference()
+                .getInheritDevicePolicy());
+        assertFalse(aospType.getDefaultUserPropertiesReference()
+                .getDeleteAppWithParent());
 
         // userTypeOem1 should be created.
         UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index ac5bcff..9b9cb4d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -192,6 +192,7 @@
                 .isEqualTo(cloneUserProperties.isMediaSharedWithParent());
         assertThat(typeProps.isCredentialShareableWithParent())
                 .isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
+        assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
 
         // Verify clone user parent
         assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
@@ -844,8 +845,10 @@
         assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
         assertThat(userProps.isMediaSharedWithParent()).isFalse();
         assertThat(userProps.isCredentialShareableWithParent()).isTrue();
+        assertThrows(SecurityException.class, userProps::getDeleteAppWithParent);
     }
 
+
     // Make sure only max managed profiles can be created
     @MediumTest
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e663245..9fda204 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1475,7 +1475,6 @@
         // Make keyguard locked and set the top activity show-when-locked.
         KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
         int displayId = activity.getDisplayId();
-        doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId));
         final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         topActivity.setVisibleRequested(true);
         topActivity.nowVisible = true;
@@ -1485,18 +1484,24 @@
                 anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
         topActivity.setShowWhenLocked(true);
 
-        // Verify the stack-top activity is occluded keyguard.
-        assertEquals(topActivity, task.topRunningActivity());
-        assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+        try {
+            keyguardController.setKeyguardShown(displayId, true, false);
 
-        // Finish the top activity
-        topActivity.setState(PAUSED, "true");
-        topActivity.finishing = true;
-        topActivity.completeFinishing("test");
+            // Verify the stack-top activity is occluded keyguard.
+            assertEquals(topActivity, task.topRunningActivity());
+            assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
 
-        // Verify new top activity does not occlude keyguard.
-        assertEquals(activity, task.topRunningActivity());
-        assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+            // Finish the top activity
+            topActivity.setState(PAUSED, "true");
+            topActivity.finishing = true;
+            topActivity.completeFinishing("test");
+
+            // Verify new top activity does not occlude keyguard.
+            assertEquals(activity, task.topRunningActivity());
+            assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+        } finally {
+            keyguardController.setKeyguardShown(displayId, false, false);
+        }
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 3dcae91..305260b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -252,7 +252,12 @@
         mAtm.setLockScreenShown(true, true);
         mRootWindowContainer.forAllDisplays(displayContent -> {
             assertTrue(displayContent.isKeyguardLocked());
-            assertTrue(displayContent.isAodShowing());
+            // Only default display supports AOD.
+            if (displayContent.isDefaultDisplay) {
+                assertTrue(displayContent.isAodShowing());
+            } else {
+                assertFalse(displayContent.isAodShowing());
+            }
         });
 
         // Check setLockScreenShown unlocking both displays
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 4070bed..28c2d59 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -115,6 +115,10 @@
     @Deprecated
     default void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
             int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+        onPublishUpdated(new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+                .setSipResponseCode(reasonCode, reasonPhrase)
+                .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+                .build());
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 3e1c9ef..66442a6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -27,6 +27,7 @@
 import android.telephony.ims.SipDetails;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.RcsFeature;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
@@ -205,6 +206,12 @@
          * when the Telephony stack has crashed.
          */
         default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+            if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+            } else {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+                        details.getReasonHeaderCause(), details.getReasonHeaderText());
+            }
         }
     }
 
@@ -338,7 +345,14 @@
          * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
          * This may also happen in rare cases when the Telephony stack has crashed.
          */
-        default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {};
+        default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+            if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+            } else {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+                        details.getReasonHeaderCause(), details.getReasonHeaderText());
+            }
+        };
 
         /**
          * Notify the framework of the latest XML PIDF documents included in the network response