Merge "Baseline NewApi issues" into udc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 5b9970b..6078712f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26782,9 +26782,7 @@
   }
 
   public final class TableResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable {
-    ctor public TableResponse(int, int, int, @Nullable android.net.Uri, int, int);
-    ctor public TableResponse(int, int, int, @NonNull byte[], int, int);
-    ctor public TableResponse(int, int, int, @NonNull android.os.SharedMemory, int, int);
+    ctor @Deprecated public TableResponse(int, int, int, @Nullable android.net.Uri, int, int);
     method public int getSize();
     method @Nullable public byte[] getTableByteArray();
     method @Nullable public android.os.SharedMemory getTableSharedMemory();
@@ -26793,6 +26791,14 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TableResponse> CREATOR;
   }
 
+  public static final class TableResponse.Builder {
+    ctor public TableResponse.Builder(int, int, int, int, int);
+    method @NonNull public android.media.tv.TableResponse build();
+    method @NonNull public android.media.tv.TableResponse.Builder setTableByteArray(@NonNull byte[]);
+    method @NonNull public android.media.tv.TableResponse.Builder setTableSharedMemory(@NonNull android.os.SharedMemory);
+    method @NonNull public android.media.tv.TableResponse.Builder setTableUri(@NonNull android.net.Uri);
+  }
+
   public final class TimelineRequest extends android.media.tv.BroadcastInfoRequest implements android.os.Parcelable {
     ctor public TimelineRequest(int, int, int);
     ctor public TimelineRequest(int, int, int, @NonNull String);
@@ -27337,8 +27343,9 @@
     field public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; // 0x0
     field public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; // 0x1
     field public static final String TV_MESSAGE_KEY_STREAM_ID = "android.media.tv.TvInputManager.stream_id";
-    field public static final String TV_MESSAGE_TYPE_CLOSED_CAPTION = "CC";
-    field public static final String TV_MESSAGE_TYPE_WATERMARK = "Watermark";
+    field public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2; // 0x2
+    field public static final int TV_MESSAGE_TYPE_OTHER = 1000; // 0x3e8
+    field public static final int TV_MESSAGE_TYPE_WATERMARK = 1; // 0x1
     field public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; // 0x4
     field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3
     field public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16; // 0x10
@@ -27429,10 +27436,10 @@
     method public void notifyTrackSelected(int, String);
     method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
     method public void notifyTuned(@NonNull android.net.Uri);
-    method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+    method public void notifyTvMessage(int, @NonNull android.os.Bundle);
     method public void notifyVideoAvailable();
     method public void notifyVideoUnavailable(int);
-    method public void onAdBuffer(@NonNull android.media.tv.AdBuffer);
+    method public void onAdBufferReady(@NonNull android.media.tv.AdBuffer);
     method public void onAppPrivateCommand(@NonNull String, android.os.Bundle);
     method public android.view.View onCreateOverlayView();
     method public boolean onGenericMotionEvent(android.view.MotionEvent);
@@ -27451,7 +27458,7 @@
     method public void onSetInteractiveAppNotificationEnabled(boolean);
     method public abstract void onSetStreamVolume(@FloatRange(from=0.0, to=1.0) float);
     method public abstract boolean onSetSurface(@Nullable android.view.Surface);
-    method public void onSetTvMessageEnabled(@NonNull String, boolean);
+    method public void onSetTvMessageEnabled(int, boolean);
     method public void onSurfaceChanged(int, int, int);
     method public long onTimeShiftGetCurrentPosition();
     method public long onTimeShiftGetStartPosition();
@@ -27501,17 +27508,17 @@
     method @NonNull public android.net.Uri getChannelUri();
     method @NonNull public java.util.List<android.media.tv.TvContentRating> getContentRatings();
     method @NonNull public String getDescription();
-    method @NonNull public long getEndPaddingMillis();
+    method public long getEndPaddingMillis();
     method @NonNull public String getName();
     method @Nullable public android.net.Uri getProgramUri();
-    method @IntRange(from=0xffffffff) @NonNull public long getRecordingDurationMillis();
+    method @IntRange(from=0xffffffff) public long getRecordingDurationMillis();
     method @NonNull public String getRecordingId();
-    method @IntRange(from=0xffffffff) @NonNull public long getRecordingStartTimeMillis();
+    method @IntRange(from=0xffffffff) public long getRecordingStartTimeMillis();
     method @Nullable public android.net.Uri getRecordingUri();
-    method @NonNull public int getRepeatDays();
-    method @IntRange(from=0) @NonNull public long getScheduledDurationMillis();
-    method @IntRange(from=0) @NonNull public long getScheduledStartTimeMillis();
-    method @NonNull public long getStartPaddingMillis();
+    method public int getRepeatDays();
+    method @IntRange(from=0) public long getScheduledDurationMillis();
+    method @IntRange(from=0) public long getScheduledStartTimeMillis();
+    method public long getStartPaddingMillis();
     method public void setDescription(@NonNull String);
     method public void setName(@NonNull String);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -27594,7 +27601,7 @@
     method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
     method public void setStreamVolume(@FloatRange(from=0.0, to=1.0) float);
     method public void setTimeShiftPositionCallback(@Nullable android.media.tv.TvView.TimeShiftPositionCallback);
-    method public void setTvMessageEnabled(@NonNull String, boolean);
+    method public void setTvMessageEnabled(int, boolean);
     method public void setZOrderMediaOverlay(boolean);
     method public void setZOrderOnTop(boolean);
     method public void timeShiftPause();
@@ -27635,7 +27642,7 @@
     method public void onTrackSelected(String, int, String);
     method public void onTracksChanged(String, java.util.List<android.media.tv.TvTrackInfo>);
     method public void onTuned(@NonNull String, @NonNull android.net.Uri);
-    method public void onTvMessage(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
+    method public void onTvMessage(@NonNull String, int, @NonNull android.os.Bundle);
     method public void onVideoAvailable(String);
     method public void onVideoSizeChanged(String, int, int);
     method public void onVideoUnavailable(String, int);
@@ -27743,7 +27750,7 @@
     ctor public TvInteractiveAppService.Session(@NonNull android.content.Context);
     method public boolean isMediaViewEnabled();
     method @CallSuper public void layoutSurface(int, int, int, int);
-    method @CallSuper public void notifyAdBuffer(@NonNull android.media.tv.AdBuffer);
+    method @CallSuper public void notifyAdBufferReady(@NonNull android.media.tv.AdBuffer);
     method @CallSuper public final void notifyBiInteractiveAppCreated(@NonNull android.net.Uri, @Nullable String);
     method @CallSuper public void notifySessionStateChanged(int, int);
     method @CallSuper public final void notifyTeletextAppStateChanged(int);
@@ -27795,7 +27802,7 @@
     method public boolean onTrackballEvent(@NonNull android.view.MotionEvent);
     method public void onTracksChanged(@NonNull java.util.List<android.media.tv.TvTrackInfo>);
     method public void onTuned(@NonNull android.net.Uri);
-    method public void onTvMessage(@NonNull String, @NonNull android.os.Bundle);
+    method public void onTvMessage(int, @NonNull android.os.Bundle);
     method public void onTvRecordingInfo(@Nullable android.media.tv.TvRecordingInfo);
     method public void onTvRecordingInfoList(@NonNull java.util.List<android.media.tv.TvRecordingInfo>);
     method public void onVideoAvailable();
@@ -27817,7 +27824,7 @@
     method @CallSuper public void requestTimeShiftMode();
     method @CallSuper public void requestTrackInfoList();
     method @CallSuper public void requestTvRecordingInfo(@NonNull String);
-    method @CallSuper public void requestTvRecordingInfoList(@NonNull int);
+    method @CallSuper public void requestTvRecordingInfoList(int);
     method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle);
     method @CallSuper public void sendTimeShiftCommandRequest(@NonNull String, @Nullable android.os.Bundle);
     method @CallSuper public void setMediaViewEnabled(boolean);
@@ -27859,7 +27866,7 @@
     method public void notifyTimeShiftPlaybackParams(@NonNull android.media.PlaybackParams);
     method public void notifyTimeShiftStartPositionChanged(@NonNull String, long);
     method public void notifyTimeShiftStatusChanged(@NonNull String, int);
-    method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+    method public void notifyTvMessage(@NonNull int, @NonNull android.os.Bundle);
     method public void onAttachedToWindow();
     method public void onDetachedFromWindow();
     method public void onLayout(boolean, int, int, int, int);
@@ -27916,7 +27923,7 @@
     method public void onRequestTimeShiftMode(@NonNull String);
     method public void onRequestTrackInfoList(@NonNull String);
     method public void onRequestTvRecordingInfo(@NonNull String, @NonNull String);
-    method public void onRequestTvRecordingInfoList(@NonNull String, @NonNull int);
+    method public void onRequestTvRecordingInfoList(@NonNull String, int);
     method public void onSetTvRecordingInfo(@NonNull String, @NonNull String, @NonNull android.media.tv.TvRecordingInfo);
     method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect);
     method public void onStateChanged(@NonNull String, int, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fb5ee8d..765a17f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -33,6 +33,7 @@
     field public static final String ADD_TRUSTED_DISPLAY = "android.permission.ADD_TRUSTED_DISPLAY";
     field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
     field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
+    field public static final String ALLOWLISTED_WRITE_DEVICE_CONFIG = "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG";
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
     field public static final String ALLOW_PLACE_IN_MULTI_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS";
     field public static final String ALLOW_SLIPPERY_TOUCHES = "android.permission.ALLOW_SLIPPERY_TOUCHES";
@@ -287,6 +288,7 @@
     field public static final String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
     field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
     field public static final String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
+    field public static final String READ_WRITE_SYNC_DISABLED_MODE_CONFIG = "android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG";
     field public static final String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
     field public static final String RECEIVE_BLUETOOTH_MAP = "android.permission.RECEIVE_BLUETOOTH_MAP";
     field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
@@ -923,7 +925,7 @@
 
   public class KeyguardManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public long addWeakEscrowToken(@NonNull byte[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenActivatedListener);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.content.Intent createConfirmDeviceCredentialForRemoteValidationIntent(@NonNull android.app.StartLockscreenValidationRequest, @NonNull android.content.ComponentName, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.content.Intent createConfirmDeviceCredentialForRemoteValidationIntent(@NonNull android.app.RemoteLockscreenValidationSession, @NonNull android.content.ComponentName, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence);
     method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed();
@@ -935,7 +937,7 @@
     method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.StartLockscreenValidationRequest startRemoteLockscreenValidation();
+    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.RemoteLockscreenValidationSession startRemoteLockscreenValidation();
     method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean unregisterWeakEscrowTokenRemovedListener(@NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener);
     method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.RemoteLockscreenValidationResult validateRemoteLockscreen(@NonNull byte[]);
     field public static final int PASSWORD = 0; // 0x0
@@ -1024,6 +1026,7 @@
     field public static final int RESULT_GUESS_VALID = 1; // 0x1
     field public static final int RESULT_LOCKOUT = 3; // 0x3
     field public static final int RESULT_NO_REMAINING_ATTEMPTS = 4; // 0x4
+    field public static final int RESULT_SESSION_EXPIRED = 5; // 0x5
   }
 
   public static final class RemoteLockscreenValidationResult.Builder {
@@ -1033,6 +1036,23 @@
     method @NonNull public android.app.RemoteLockscreenValidationResult.Builder setTimeoutMillis(long);
   }
 
+  public final class RemoteLockscreenValidationSession implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLockType();
+    method public int getRemainingAttempts();
+    method @NonNull public byte[] getSourcePublicKey();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.RemoteLockscreenValidationSession> CREATOR;
+  }
+
+  public static final class RemoteLockscreenValidationSession.Builder {
+    ctor public RemoteLockscreenValidationSession.Builder();
+    method @NonNull public android.app.RemoteLockscreenValidationSession build();
+    method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setLockType(int);
+    method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setRemainingAttempts(int);
+    method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setSourcePublicKey(@NonNull byte[]);
+  }
+
   public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
     ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
     method public int describeContents();
@@ -1050,23 +1070,6 @@
     method public void launchAssist(@Nullable android.os.Bundle);
   }
 
-  public final class StartLockscreenValidationRequest implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getLockscreenUiType();
-    method public int getRemainingAttempts();
-    method @NonNull public byte[] getSourcePublicKey();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.StartLockscreenValidationRequest> CREATOR;
-  }
-
-  public static final class StartLockscreenValidationRequest.Builder {
-    ctor public StartLockscreenValidationRequest.Builder();
-    method @NonNull public android.app.StartLockscreenValidationRequest build();
-    method @NonNull public android.app.StartLockscreenValidationRequest.Builder setLockscreenUiType(int);
-    method @NonNull public android.app.StartLockscreenValidationRequest.Builder setRemainingAttempts(int);
-    method @NonNull public android.app.StartLockscreenValidationRequest.Builder setSourcePublicKey(@NonNull byte[]);
-  }
-
   public class StatusBarManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarMode();
@@ -10158,6 +10161,8 @@
 
   public abstract class SharedConnectivityService extends android.app.Service {
     ctor public SharedConnectivityService();
+    method public static boolean areHotspotNetworksEnabledForService(@NonNull android.content.Context);
+    method public static boolean areKnownNetworksEnabledForService(@NonNull android.content.Context);
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onConnectHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
     method public abstract void onConnectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
@@ -14611,7 +14616,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
     method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public android.os.UserHandle getSubscriptionUserHandle(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
     method public void requestEmbeddedSubscriptionInfoListRefresh(int);
@@ -14621,7 +14625,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public void setSubscriptionUserHandle(int, @Nullable android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
     field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
     field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 46651a9..14c53d1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -5,6 +5,7 @@
     field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
+    field public static final String ALLOWLISTED_WRITE_DEVICE_CONFIG = "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
     field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
@@ -39,6 +40,7 @@
     field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
+    field public static final String READ_WRITE_SYNC_DISABLED_MODE_CONFIG = "android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG";
     field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
     field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
@@ -1957,7 +1959,7 @@
   }
 
   public class TvView extends android.view.ViewGroup {
-    method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+    method public void notifyTvMessage(int, @NonNull android.os.Bundle);
   }
 
 }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d810f05..90427cb 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.StopUserOnSwitch;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.PermissionMethod;
@@ -47,6 +48,8 @@
 
 import com.android.internal.os.TimeoutRecord;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -991,4 +994,48 @@
      */
     public abstract void logFgsApiEnd(int apiType, int uid, int pid);
 
+    /**
+     * The list of the events about the {@link android.media.projection.IMediaProjection} itself.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        MEDIA_PROJECTION_TOKEN_EVENT_CREATED,
+        MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED,
+    })
+    public @interface MediaProjectionTokenEvent{};
+
+    /**
+     * An instance of {@link android.media.projection.IMediaProjection} has been created
+     * by the system.
+     *
+     * @hide
+     */
+    public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_CREATED = 0;
+
+    /**
+     * An instance of {@link android.media.projection.IMediaProjection} has been destroyed
+     * by the system.
+     *
+     * @hide
+     */
+    public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED = 1;
+
+    /**
+     * Called after the system created/destroyed a media projection for an app, if the user
+     * has granted the permission to start a media projection from this app.
+     *
+     * <p>This API is specifically for the use case of enforcing the FGS type
+     * {@code android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION},
+     * where the app who is starting this type of FGS must have been granted with the permission
+     * to start the projection via the {@link android.media.projection.MediaProjection} APIs.
+     *
+     * @param uid The uid of the app which the system created/destroyed a media projection for.
+     * @param projectionToken The {@link android.media.projection.IMediaProjection} token that
+     *                        the system created/destroyed.
+     * @param event The actual event happening to the given {@code projectionToken}.
+     */
+    public abstract void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+            @MediaProjectionTokenEvent int event);
 }
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index a7824a8..be012cf 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -59,6 +59,9 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.permission.PermissionCheckerManager;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
 
@@ -147,6 +150,108 @@
     public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L;
 
     /**
+     * The prefix for the feature flags of the permission enforcement.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX =
+            "fgs_type_perm_enforcement_flag_";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "data_sync";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_playback";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "phone_call";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "location";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "connected_device";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_projection";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "camera";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "microphone";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "health";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "remote_messaging";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "system_exempted";
+
+    /**
+     * The feature flag of the permission enforcement for
+     * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE},
+     * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
+     */
+    private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE =
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "special_use";
+
+    /**
      * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST}.
      *
      * @hide
@@ -156,8 +261,10 @@
             FOREGROUND_SERVICE_TYPE_MANIFEST,
             FGS_TYPE_NONE_DEPRECATION_CHANGE_ID,
             FGS_TYPE_NONE_DISABLED_CHANGE_ID,
-            null,
-            null
+            null /* allOfPermissions */,
+            null /* anyOfPermissions */,
+            null /* permissionEnforcementFlag */,
+            false /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -170,8 +277,10 @@
             FOREGROUND_SERVICE_TYPE_NONE,
             FGS_TYPE_NONE_DEPRECATION_CHANGE_ID,
             FGS_TYPE_NONE_DISABLED_CHANGE_ID,
-            null,
-            null
+            null /* allOfPermissions */,
+            null /* anyOfPermissions */,
+            null /* permissionEnforcementFlag */,
+            false /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -187,7 +296,9 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC)
             }, true),
-            null
+            null /* anyOfPermissions */,
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -203,7 +314,9 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK)
             }, true),
-            null
+            null /* anyOfPermissions */,
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -221,7 +334,9 @@
             }, true),
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.MANAGE_OWN_CALLS)
-            }, false)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -240,7 +355,9 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.ACCESS_COARSE_LOCATION),
                 new RegularPermission(Manifest.permission.ACCESS_FINE_LOCATION),
-            }, false)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -268,7 +385,9 @@
                 new RegularPermission(Manifest.permission.UWB_RANGING),
                 new UsbDevicePermission(),
                 new UsbAccessoryPermission(),
-            }, false)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -284,7 +403,12 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION)
             }, true),
-            null
+            new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
+                new RegularPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT),
+                new AppOpPermission(AppOpsManager.OP_PROJECT_MEDIA)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -303,7 +427,9 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.CAMERA),
                 new RegularPermission(Manifest.permission.SYSTEM_CAMERA),
-            }, false)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -326,7 +452,9 @@
                 new RegularPermission(Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT),
                 new RegularPermission(Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT),
                 new RegularPermission(Manifest.permission.RECORD_AUDIO),
-            }, false)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -347,7 +475,9 @@
                 new RegularPermission(Manifest.permission.BODY_SENSORS),
                 new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE),
                 new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
-            }, false)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -363,7 +493,9 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING)
             }, true),
-            null
+            null /* anyOfPermissions */,
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -383,7 +515,9 @@
                 new RegularPermission(Manifest.permission.SCHEDULE_EXACT_ALARM),
                 new RegularPermission(Manifest.permission.USE_EXACT_ALARM),
                 new AppOpPermission(AppOpsManager.OP_ACTIVATE_VPN),
-            }, false)
+            }, false),
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -396,7 +530,10 @@
             FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
             ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
             ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
-            null /* no type specific permissions */, null /* no type specific permissions */
+            null /* allOfPermissions */,
+            null /* anyOfPermissions */,
+            null /* permissionEnforcementFlag */,
+            false /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -412,7 +549,9 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT)
             }, true),
-            null
+            null /* anyOfPermissions */,
+            null /* permissionEnforcementFlag */,
+            false /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -428,7 +567,9 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE)
             }, true),
-            null
+            null /* anyOfPermissions */,
+            FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE /* permissionEnforcementFlag */,
+            true /* permissionEnforcementFlagDefaultValue */
     );
 
     /**
@@ -512,6 +653,14 @@
             @NonNull String packageName, int callerUid, int callerPid, boolean allowWhileInUse,
             @NonNull ForegroundServiceTypePolicyInfo policy);
 
+    /**
+     * Run the given {@code policyFunctor} on the matching policy, if the flag is known
+     * to the policy.
+     *
+     * @hide
+     */
+    public abstract void updatePermissionEnforcementFlagIfNecessary(@NonNull String flag);
+
     @GuardedBy("sLock")
     private static ForegroundServiceTypePolicy sDefaultForegroundServiceTypePolicy = null;
 
@@ -575,11 +724,31 @@
         final @Nullable ForegroundServiceTypePermissions mAnyOfPermissions;
 
         /**
+         * A permission enforcement flag, unlike the {@link #FGS_TYPE_PERMISSION_CHANGE_ID},
+         * here it applies to all apps using this FGS type.
+         */
+        final @Nullable String mPermissionEnforcementFlag;
+
+        /**
+         * The default value to {@link #mPermissionEnforcementFlag}.
+         */
+        final boolean mPermissionEnforcementFlagDefaultValue;
+
+        /**
          * A customized check for the permissions.
          */
         @Nullable ForegroundServiceTypePermission mCustomPermission;
 
         /**
+         * The value of the permission enforcement flag, will be updated by the system.
+         * If the value is {@code false}, the FGS permission check will be ignored.
+         *
+         * <p>This value could be updated via the DeviceConfig flag specified
+         * in the {@link #mPermissionEnforcementFlag}.</p>
+         */
+        volatile boolean mPermissionEnforcementFlagValue;
+
+        /**
          * Not a real change id, but a place holder.
          */
         private static final long INVALID_CHANGE_ID = 0L;
@@ -599,12 +768,17 @@
         public ForegroundServiceTypePolicyInfo(@ForegroundServiceType int type,
                 long deprecationChangeId, long disabledChangeId,
                 @Nullable ForegroundServiceTypePermissions allOfPermissions,
-                @Nullable ForegroundServiceTypePermissions anyOfPermissions) {
+                @Nullable ForegroundServiceTypePermissions anyOfPermissions,
+                @Nullable String permissionEnforcementFlag,
+                boolean permissionEnforcementFlagDefaultValue) {
             mType = type;
             mDeprecationChangeId = deprecationChangeId;
             mDisabledChangeId = disabledChangeId;
             mAllOfPermissions = allOfPermissions;
             mAnyOfPermissions = anyOfPermissions;
+            mPermissionEnforcementFlag = permissionEnforcementFlag;
+            mPermissionEnforcementFlagDefaultValue = permissionEnforcementFlagDefaultValue;
+            mPermissionEnforcementFlagValue = permissionEnforcementFlagDefaultValue;
         }
 
         /**
@@ -650,6 +824,17 @@
             return sb;
         }
 
+        private void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
+            if (mPermissionEnforcementFlag == null
+                    || !TextUtils.equals(flagName, mPermissionEnforcementFlag)) {
+                return;
+            }
+            mPermissionEnforcementFlagValue = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    mPermissionEnforcementFlag,
+                    mPermissionEnforcementFlagDefaultValue);
+        }
+
         /**
          * @hide
          */
@@ -745,6 +930,15 @@
                         ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
             platformCompat.clearOverrideForTest(changeId, packageName);
         }
+
+        /**
+         * For test only.
+         *
+         * @return The permission enforcement flag.
+         */
+        public @Nullable String getPermissionEnforcementFlagForTest() {
+            return mPermissionEnforcementFlag;
+        }
     }
 
     /**
@@ -991,6 +1185,12 @@
                 new SparseArray<>();
 
         /**
+         * The map between permission enforcement flag to its permission policy info.
+         */
+        private final ArrayMap<String, ForegroundServiceTypePolicyInfo>
+                mPermissionEnforcementToPolicyInfoMap = new ArrayMap<>();
+
+        /**
          * Constructor
          */
         public DefaultForegroundServiceTypePolicy() {
@@ -1022,10 +1222,15 @@
                     FGS_TYPE_POLICY_SYSTEM_EXEMPTED);
             mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
                     FGS_TYPE_POLICY_SHORT_SERVICE);
-            mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT,
-                    FGS_TYPE_POLICY_FILE_MANAGEMENT);
+            // TODO (b/271950506): revisit it in the next release.
+            // Hide the file management type for now. If anyone uses it, will default to "none".
             mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
                     FGS_TYPE_POLICY_SPECIAL_USE);
+            for (int i = 0, size = mForegroundServiceTypePolicies.size(); i < size; i++) {
+                final ForegroundServiceTypePolicyInfo info =
+                        mForegroundServiceTypePolicies.valueAt(i);
+                mPermissionEnforcementToPolicyInfoMap.put(info.mPermissionEnforcementFlag, info);
+            }
         }
 
         @Override
@@ -1078,8 +1283,8 @@
                 }
             }
             if (permissionResult != PERMISSION_GRANTED) {
-                return (CompatChanges.isChangeEnabled(
-                        FGS_TYPE_PERMISSION_CHANGE_ID, callerUid))
+                return policy.mPermissionEnforcementFlagValue
+                        && (CompatChanges.isChangeEnabled(FGS_TYPE_PERMISSION_CHANGE_ID, callerUid))
                         ? FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED
                         : FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE;
             }
@@ -1089,5 +1294,14 @@
             }
             return FGS_TYPE_POLICY_CHECK_OK;
         }
+
+        @Override
+        public void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
+            final ForegroundServiceTypePolicyInfo info =
+                    mPermissionEnforcementToPolicyInfoMap.get(flagName);
+            if (info != null) {
+                info.updatePermissionEnforcementFlagIfNecessary(flagName);
+            }
+        }
     }
 }
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index be0d1c9..0ef8e92 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -146,13 +146,13 @@
     public static final String EXTRA_CHECKBOX_LABEL = "android.app.extra.CHECKBOX_LABEL";
 
     /**
-     * A {@link StartLockscreenValidationRequest} extra to be sent along with
+     * A {@link RemoteLockscreenValidationSession} extra to be sent along with
      * {@link #ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL} containing the data needed to prompt for
      * a remote device's lock screen.
      * @hide
      */
-    public static final String EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST =
-            "android.app.extra.START_LOCKSCREEN_VALIDATION_REQUEST";
+    public static final String EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION =
+            "android.app.extra.REMOTE_LOCKSCREEN_VALIDATION_SESSION";
 
     /**
      * Result code returned by the activity started by
@@ -359,8 +359,7 @@
     /**
      * Get an Intent to launch an activity to prompt the user to confirm the
      * credentials (pin, pattern or password) of a remote device.
-     * @param startLockscreenValidationRequest contains information necessary to start remote device
-     *                                         credential validation.
+     * @param session contains information necessary to start remote device credential validation.
      * @param remoteLockscreenValidationServiceComponent
      *          the {@link ComponentName} of the implementation of
      *          {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService}
@@ -376,15 +375,14 @@
     @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
     @NonNull
     public Intent createConfirmDeviceCredentialForRemoteValidationIntent(
-            @NonNull StartLockscreenValidationRequest startLockscreenValidationRequest,
+            @NonNull RemoteLockscreenValidationSession session,
             @NonNull ComponentName remoteLockscreenValidationServiceComponent,
             @Nullable CharSequence title,
             @Nullable CharSequence description,
             @Nullable CharSequence checkboxLabel,
             @Nullable CharSequence alternateButtonLabel) {
         Intent intent = new Intent(ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL)
-                .putExtra(
-                        EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST, startLockscreenValidationRequest)
+                .putExtra(EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION, session)
                 .putExtra(Intent.EXTRA_COMPONENT_NAME, remoteLockscreenValidationServiceComponent)
                 .putExtra(EXTRA_TITLE, title)
                 .putExtra(EXTRA_DESCRIPTION, description)
@@ -1157,7 +1155,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
     @NonNull
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
         return mLockPatternUtils.startRemoteLockscreenValidation();
     }
 
@@ -1165,11 +1163,10 @@
      * Verifies credentials guess from a remote device.
      *
      * <p>Secret must be encrypted using {@code SecureBox} library
-     * with public key from {@code StartLockscreenValidationRequest}
+     * with public key from {@code RemoteLockscreenValidationSession}
      * and header set to {@code "encrypted_remote_credentials"} in UTF-8 encoding.
      *
-     * @throws IllegalStateException if there is no active lock screen validation session or
-     * there was a decryption error.
+     * @throws IllegalStateException if there was a decryption error.
      *
      * @hide
      */
diff --git a/core/java/android/app/RemoteLockscreenValidationResult.java b/core/java/android/app/RemoteLockscreenValidationResult.java
index 0245f8c..bbb3567 100644
--- a/core/java/android/app/RemoteLockscreenValidationResult.java
+++ b/core/java/android/app/RemoteLockscreenValidationResult.java
@@ -55,10 +55,16 @@
      */
     public static final int RESULT_NO_REMAINING_ATTEMPTS = 4;
 
+    /**
+     * New lockscreen validation session is required to verify guess.
+     */
+    public static final int RESULT_SESSION_EXPIRED = 5;
+
     @IntDef({RESULT_GUESS_VALID,
             RESULT_GUESS_INVALID,
             RESULT_LOCKOUT,
-            RESULT_NO_REMAINING_ATTEMPTS})
+            RESULT_NO_REMAINING_ATTEMPTS,
+            RESULT_SESSION_EXPIRED})
     @Retention(RetentionPolicy.SOURCE)
     @interface ResultCode {}
 
diff --git a/core/java/android/app/StartLockscreenValidationRequest.aidl b/core/java/android/app/RemoteLockscreenValidationSession.aidl
similarity index 93%
rename from core/java/android/app/StartLockscreenValidationRequest.aidl
rename to core/java/android/app/RemoteLockscreenValidationSession.aidl
index 367dfee..edc8d56 100644
--- a/core/java/android/app/StartLockscreenValidationRequest.aidl
+++ b/core/java/android/app/RemoteLockscreenValidationSession.aidl
@@ -17,4 +17,4 @@
 package android.app;
 
 /** {@hide} */
-parcelable StartLockscreenValidationRequest;
+parcelable RemoteLockscreenValidationSession;
diff --git a/core/java/android/app/StartLockscreenValidationRequest.java b/core/java/android/app/RemoteLockscreenValidationSession.java
similarity index 69%
rename from core/java/android/app/StartLockscreenValidationRequest.java
rename to core/java/android/app/RemoteLockscreenValidationSession.java
index e818195..c6592e3 100644
--- a/core/java/android/app/StartLockscreenValidationRequest.java
+++ b/core/java/android/app/RemoteLockscreenValidationSession.java
@@ -30,44 +30,45 @@
  * @hide
  */
 @SystemApi
-public final class StartLockscreenValidationRequest implements Parcelable {
+public final class RemoteLockscreenValidationSession implements Parcelable {
 
     @LockTypes
-    private int mLockscreenUiType;
+    private int mLockType;
 
     private byte[] mSourcePublicKey;
 
     private int mRemainingAttempts;
 
-    public static final @NonNull Parcelable.Creator<StartLockscreenValidationRequest> CREATOR = new
-            Parcelable.Creator<StartLockscreenValidationRequest>() {
+    public static final @NonNull Parcelable.Creator<RemoteLockscreenValidationSession> CREATOR = new
+            Parcelable.Creator<RemoteLockscreenValidationSession>() {
         @Override
-        public StartLockscreenValidationRequest createFromParcel(Parcel source) {
-            return new StartLockscreenValidationRequest(source);
+        public RemoteLockscreenValidationSession createFromParcel(Parcel source) {
+            return new RemoteLockscreenValidationSession(source);
         }
 
         @Override
-        public StartLockscreenValidationRequest[] newArray(int size) {
-            return new StartLockscreenValidationRequest[size];
+        public RemoteLockscreenValidationSession[] newArray(int size) {
+            return new RemoteLockscreenValidationSession[size];
         }
     };
 
 
     /**
-     * Builder for {@code StartLockscreenValidationRequest}
+     * Builder for {@code RemoteLockscreenValidationSession}
      */
     public static final class Builder {
-        private StartLockscreenValidationRequest mInstance = new StartLockscreenValidationRequest();
+        private RemoteLockscreenValidationSession mInstance =
+                new RemoteLockscreenValidationSession();
 
         /**
          * Sets UI type.
          * Default value is {@code LockTypes.PASSWORD}
          *
-         * @param lockscreenUiType The UI format
+         * @param lockType The UI format
          * @return This builder.
          */
-        public @NonNull Builder setLockscreenUiType(@LockTypes int lockscreenUiType) {
-            mInstance.mLockscreenUiType = lockscreenUiType;
+        public @NonNull Builder setLockType(@LockTypes int lockType) {
+            mInstance.mLockType = lockType;
             return this;
         }
 
@@ -92,11 +93,11 @@
         }
 
         /**
-         * Creates {@code StartLockscreenValidationRequest}
+         * Creates {@code RemoteLockscreenValidationSession}
          *
          * @throws NullPointerException if required fields are not set.
          */
-        public @NonNull StartLockscreenValidationRequest build() {
+        public @NonNull RemoteLockscreenValidationSession build() {
             Objects.requireNonNull(mInstance.mSourcePublicKey);
             return mInstance;
         }
@@ -105,8 +106,8 @@
     /**
      * Specifies lock screen credential type.
      */
-    public @LockTypes int getLockscreenUiType() {
-        return mLockscreenUiType;
+    public @LockTypes int getLockType() {
+        return mLockType;
     }
 
     /**
@@ -127,16 +128,16 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeInt(mLockscreenUiType);
+        out.writeInt(mLockType);
         out.writeByteArray(mSourcePublicKey);
         out.writeInt(mRemainingAttempts);
     }
 
-    private StartLockscreenValidationRequest() {
+    private RemoteLockscreenValidationSession() {
     }
 
-    private StartLockscreenValidationRequest(Parcel in) {
-        mLockscreenUiType = in.readInt();
+    private RemoteLockscreenValidationSession(Parcel in) {
+        mLockType = in.readInt();
         mSourcePublicKey = in.createByteArray();
         mRemainingAttempts = in.readInt();
     }
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 4532661..4b3eb3a 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -1392,10 +1392,6 @@
         return sql.replaceAll("[\\s]*\\n+[\\s]*", " ");
     }
 
-    void clearPreparedStatementCache() {
-        mPreparedStatementCache.evictAll();
-    }
-
     /**
      * Holder type for a prepared statement.
      *
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 6023d66..069c264 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1126,16 +1126,6 @@
         mConnectionWaiterPool = waiter;
     }
 
-    void clearAcquiredConnectionsPreparedStatementCache() {
-        synchronized (mLock) {
-            if (!mAcquiredConnections.isEmpty()) {
-                for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
-                    connection.clearPreparedStatementCache();
-                }
-            }
-        }
-    }
-
     /**
      * Dumps debugging information about this connection pool.
      *
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index db898c3..c08294f 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2088,12 +2088,10 @@
             try (SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs)) {
                 return statement.executeUpdateDelete();
             } finally {
-                // If schema was updated, close non-primary connections and clear prepared
-                // statement caches of active connections, otherwise they might have outdated
-                // schema information.
+                // If schema was updated, close non-primary connections, otherwise they might
+                // have outdated schema information
                 if (statementType == DatabaseUtils.STATEMENT_DDL) {
                     mConnectionPoolLocked.closeAvailableNonPrimaryConnectionsAndLogExceptions();
-                    mConnectionPoolLocked.clearAcquiredConnectionsPreparedStatementCache();
                 }
             }
         } finally {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index fbc0184..6cd4a2e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1189,8 +1189,8 @@
             PackageManager packageManager = context.getPackageManager();
 
             try {
-                return packageManager.getProperty(context.getOpPackageName(),
-                            PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT).getBoolean();
+                return packageManager.getProperty(PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT,
+                        context.getOpPackageName()).getBoolean();
             } catch (PackageManager.NameNotFoundException e) {
                 // No such property
             }
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index cb678b9..5a48176 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -26,7 +26,6 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.SurfaceUtils;
-import android.util.Log;
 import android.util.Range;
 import android.util.Size;
 import android.util.SparseIntArray;
@@ -2019,7 +2018,7 @@
     /**
      * @hide
      */
-    public static final int HAL_DATASPACE_HEIF = 0x1003;
+    public static final int HAL_DATASPACE_HEIF = 0x1004;
     /**
      * @hide
      */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d14abfd..d2f9ff0 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3396,8 +3396,10 @@
         public static final int MATCH_ALL_APN_SET_ID = -1;
 
         /**
-         * A unique carrier id associated with this APN
-         * {@see TelephonyManager#getSimCarrierId()}
+         * A unique carrier id associated with this APN {@link TelephonyManager#getSimCarrierId()}
+         * In case of matching carrier information, this should be used by default instead of
+         * those fields of {@link #MCC}, {@link #MNC}, {@link #NUMERIC}, {@link #MVNO_TYPE},
+         * {@link #MVNO_MATCH_DATA}, etc.
          * <p>Type: STRING</p>
          */
         public static final String CARRIER_ID = "carrier_id";
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 0ef8bb64..e81ca1a 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -28,6 +28,7 @@
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
@@ -434,14 +435,14 @@
      * one value for a field or set an authentication intent.
      */
     public static final class Builder {
-        private ArrayList<AutofillId> mFieldIds;
-        private ArrayList<AutofillValue> mFieldValues;
-        private ArrayList<RemoteViews> mFieldPresentations;
-        private ArrayList<RemoteViews> mFieldDialogPresentations;
-        private ArrayList<InlinePresentation> mFieldInlinePresentations;
-        private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
-        private ArrayList<DatasetFieldFilter> mFieldFilters;
-        private ArrayList<String> mAutofillDatatypes;
+        private ArrayList<AutofillId> mFieldIds = new ArrayList<>();
+        private ArrayList<AutofillValue> mFieldValues = new ArrayList();
+        private ArrayList<RemoteViews> mFieldPresentations = new ArrayList();
+        private ArrayList<RemoteViews> mFieldDialogPresentations = new ArrayList();
+        private ArrayList<InlinePresentation> mFieldInlinePresentations = new ArrayList();
+        private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations = new ArrayList();
+        private ArrayList<DatasetFieldFilter> mFieldFilters = new ArrayList();
+        private ArrayList<String> mAutofillDatatypes = new ArrayList();
         @Nullable private ClipData mFieldContent;
         private RemoteViews mPresentation;
         private RemoteViews mDialogPresentation;
@@ -452,6 +453,15 @@
         @Nullable private String mId;
 
         /**
+         * Usually, a field will be associated with a single autofill id and/or datatype.
+         * There could be null field value corresponding to different autofill ids or datatye
+         * values, but the implementation is ok with duplicating that information.
+         * This map is just for the purpose of optimization, to reduce the size of the pelled data
+         * over the binder transaction.
+         */
+        private ArrayMap<Field, Integer> mFieldToIndexdMap = new ArrayMap<>();
+
+        /**
          * Creates a new builder.
          *
          * @param presentation The presentation used to visualize this dataset.
@@ -1051,29 +1061,40 @@
          */
         public @NonNull Builder setField(@NonNull AutofillId id, @Nullable Field field) {
             throwIfDestroyed();
+
+            if (mFieldToIndexdMap.containsKey(field)) {
+                int index = mFieldToIndexdMap.get(field);
+                if (mFieldIds.get(index) == null) {
+                    mFieldIds.set(index, id);
+                    return this;
+                }
+                // if the Autofill Id is already set, ignore and proceed as if setting in a new
+                // value.
+            }
+            int index;
             if (field == null) {
-                setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
+                index = setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
             } else {
                 final DatasetFieldFilter filter = field.getDatasetFieldFilter();
                 final Presentations presentations = field.getPresentations();
                 if (presentations == null) {
-                    setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
+                    index = setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
                             filter, null);
                 } else {
-                    setLifeTheUniverseAndEverything(id, field.getValue(),
+                    index = setLifeTheUniverseAndEverything(id, field.getValue(),
                             presentations.getMenuPresentation(),
                             presentations.getInlinePresentation(),
                             presentations.getInlineTooltipPresentation(), filter,
                             presentations.getDialogPresentation());
                 }
             }
+            mFieldToIndexdMap.put(field, index);
             return this;
         }
 
         /**
-         * Adds a field to this Dataset with a specific type and no
-         * AutofillId. This is used to send back Field information
-         * when Autofilling with platform detections is on.
+         * Adds a field to this Dataset with a specific type. This is used to send back Field
+         * information when Autofilling with platform detections is on.
          * Platform detections are on when receiving a populated list from
          * FillRequest#getHints().
          *
@@ -1086,9 +1107,6 @@
          * has two credential pairs, then two Datasets should be created,
          * and so on.
          *
-         * Using this will remove any data populated with
-         * setField(@NonNull AutofillId id, @Nullable Field field).
-         *
          * @param hint An autofill hint returned from {@link
          *         FillRequest#getHints()}.
          *
@@ -1102,19 +1120,29 @@
         public @NonNull Dataset.Builder setField(@NonNull String hint, @NonNull Field field) {
             throwIfDestroyed();
 
+            if (mFieldToIndexdMap.containsKey(field)) {
+                int index = mFieldToIndexdMap.get(field);
+                if (mAutofillDatatypes.get(index) == null) {
+                    mAutofillDatatypes.set(index, hint);
+                    return this;
+                }
+                // if the hint is already set, ignore and proceed as if setting in a new hint.
+            }
+
+            int index;
             final DatasetFieldFilter filter = field.getDatasetFieldFilter();
             final Presentations presentations = field.getPresentations();
             if (presentations == null) {
-                setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
+                index = setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
                         filter, null);
             } else {
-                setLifeTheUniverseAndEverything(hint, field.getValue(),
+                index = setLifeTheUniverseAndEverything(hint, field.getValue(),
                         presentations.getMenuPresentation(),
                         presentations.getInlinePresentation(),
                         presentations.getInlineTooltipPresentation(), filter,
                         presentations.getDialogPresentation());
             }
-
+            mFieldToIndexdMap.put(field, index);
             return this;
         }
 
@@ -1172,67 +1200,64 @@
             return this;
         }
 
-        private void setLifeTheUniverseAndEverything(String datatype,
+        /** Returns the index at which this id was modified or inserted */
+        private int setLifeTheUniverseAndEverything(@NonNull String datatype,
                 @Nullable AutofillValue value,
                 @Nullable RemoteViews presentation,
                 @Nullable InlinePresentation inlinePresentation,
                 @Nullable InlinePresentation tooltip,
                 @Nullable DatasetFieldFilter filter,
                 @Nullable RemoteViews dialogPresentation) {
-            if (mAutofillDatatypes == null) {
-                mFieldValues = new ArrayList<>();
-                mFieldPresentations = new ArrayList<>();
-                mFieldDialogPresentations = new ArrayList<>();
-                mFieldInlinePresentations = new ArrayList<>();
-                mFieldInlineTooltipPresentations = new ArrayList<>();
-                mFieldFilters = new ArrayList<>();
-                mAutofillDatatypes = new ArrayList<>();
-                mFieldIds = null;
+            Objects.requireNonNull(datatype, "datatype cannot be null");
+            final int existingIdx = mAutofillDatatypes.indexOf(datatype);
+            if (existingIdx >= 0) {
+                mAutofillDatatypes.add(datatype);
+                mFieldValues.set(existingIdx, value);
+                mFieldPresentations.set(existingIdx, presentation);
+                mFieldDialogPresentations.set(existingIdx, dialogPresentation);
+                mFieldInlinePresentations.set(existingIdx, inlinePresentation);
+                mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
+                mFieldFilters.set(existingIdx, filter);
+                return existingIdx;
             }
+            mFieldIds.add(null);
+            mAutofillDatatypes.add(datatype);
             mFieldValues.add(value);
             mFieldPresentations.add(presentation);
             mFieldDialogPresentations.add(dialogPresentation);
             mFieldInlinePresentations.add(inlinePresentation);
             mFieldInlineTooltipPresentations.add(tooltip);
             mFieldFilters.add(filter);
-            mAutofillDatatypes.add(datatype);
+            return mFieldIds.size() - 1;
         }
 
-        private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
+        /** Returns the index at which this id was modified or inserted */
+        private int setLifeTheUniverseAndEverything(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
                 @Nullable InlinePresentation inlinePresentation,
                 @Nullable InlinePresentation tooltip,
                 @Nullable DatasetFieldFilter filter,
                 @Nullable RemoteViews dialogPresentation) {
             Objects.requireNonNull(id, "id cannot be null");
-            if (mFieldIds != null) {
-                final int existingIdx = mFieldIds.indexOf(id);
-                if (existingIdx >= 0) {
-                    mFieldValues.set(existingIdx, value);
-                    mFieldPresentations.set(existingIdx, presentation);
-                    mFieldDialogPresentations.set(existingIdx, dialogPresentation);
-                    mFieldInlinePresentations.set(existingIdx, inlinePresentation);
-                    mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
-                    mFieldFilters.set(existingIdx, filter);
-                    return;
-                }
-            } else {
-                mFieldIds = new ArrayList<>();
-                mFieldValues = new ArrayList<>();
-                mFieldPresentations = new ArrayList<>();
-                mFieldDialogPresentations = new ArrayList<>();
-                mFieldInlinePresentations = new ArrayList<>();
-                mFieldInlineTooltipPresentations = new ArrayList<>();
-                mFieldFilters = new ArrayList<>();
-                mAutofillDatatypes = null;
+            final int existingIdx = mFieldIds.indexOf(id);
+            if (existingIdx >= 0) {
+                mFieldValues.set(existingIdx, value);
+                mFieldPresentations.set(existingIdx, presentation);
+                mFieldDialogPresentations.set(existingIdx, dialogPresentation);
+                mFieldInlinePresentations.set(existingIdx, inlinePresentation);
+                mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
+                mFieldFilters.set(existingIdx, filter);
+                return existingIdx;
             }
             mFieldIds.add(id);
+            mAutofillDatatypes.add(null);
             mFieldValues.add(value);
             mFieldPresentations.add(presentation);
             mFieldDialogPresentations.add(dialogPresentation);
             mFieldInlinePresentations.add(inlinePresentation);
             mFieldInlineTooltipPresentations.add(tooltip);
             mFieldFilters.add(filter);
+            return mFieldIds.size() - 1;
         }
 
         /**
@@ -1249,11 +1274,12 @@
             throwIfDestroyed();
             mDestroyed = true;
             if (mFieldIds == null && mAutofillDatatypes == null) {
-                throw new IllegalStateException("at least one value must be set");
+                throw new IllegalStateException("at least one of field or datatype must be set");
             }
             if (mFieldIds != null && mAutofillDatatypes != null) {
-                if (mFieldIds.size() > 0 && mAutofillDatatypes.size() > 0) {
-                    throw new IllegalStateException("both field and datatype were populated");
+                if (mFieldIds.size() == 0 && mAutofillDatatypes.size() == 0) {
+                    throw new IllegalStateException(
+                            "at least one of field or datatype must be set");
                 }
             }
             if (mFieldContent != null) {
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 9cd5aa0..3190c69 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -328,7 +328,7 @@
             @NonNull Context context,
             @UserIdInt int userId,
             boolean disableSystemAppVerificationForTests,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         requireNonNull(context, "context must not be null");
 
         final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
@@ -342,7 +342,7 @@
                                 si,
                                 /* isSystemProvider= */ true,
                                 disableSystemAppVerificationForTests,
-                                enabledServices.contains(si));
+                                enabledServices.contains(si.getComponentName()));
                 if (cpi.isSystemProvider()) {
                     providerInfos.add(cpi);
                 } else {
@@ -371,31 +371,6 @@
     }
 
     /**
-     * Returns a valid credential provider that has the given package name. Returns null if no
-     * match is found.
-     */
-    @Nullable
-    public static CredentialProviderInfo getCredentialProviderFromPackageName(
-            @NonNull Context context,
-            int userId,
-            @NonNull String packageName,
-            int providerFilter,
-            @NonNull Set<ServiceInfo> enabledServices) {
-        requireNonNull(context, "context must not be null");
-        requireNonNull(packageName, "package name must not be null");
-        requireNonNull(enabledServices, "enabledServices must not be null");
-
-        for (CredentialProviderInfo credentialProviderInfo : getCredentialProviderServices(context,
-                userId, providerFilter, enabledServices)) {
-            if (credentialProviderInfo.getServiceInfo()
-                    .packageName.equals(packageName)) {
-                return credentialProviderInfo;
-            }
-        }
-        return null;
-    }
-
-    /**
      * Returns the valid credential provider services available for the user with the given {@code
      * userId}.
      */
@@ -404,7 +379,7 @@
             @NonNull Context context,
             int userId,
             int providerFilter,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         requireNonNull(context, "context must not be null");
 
         // Get the device policy.
@@ -433,7 +408,7 @@
             @NonNull Context context,
             int userId,
             int providerFilter,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         requireNonNull(context, "context must not be null");
 
         // Get the device policy.
@@ -539,7 +514,7 @@
             @NonNull Context context,
             @UserIdInt int userId,
             boolean disableSystemAppVerificationForTests,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         final List<CredentialProviderInfo> services = new ArrayList<>();
         final List<ResolveInfo> resolveInfos =
                 context.getPackageManager()
@@ -549,6 +524,11 @@
                                 userId);
         for (ResolveInfo resolveInfo : resolveInfos) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            if (serviceInfo == null) {
+                Log.i(TAG, "No serviceInfo found for resolveInfo so skipping this provider");
+                continue;
+            }
+
             try {
                 CredentialProviderInfo cpi =
                         CredentialProviderInfoFactory.create(
@@ -556,7 +536,7 @@
                                 serviceInfo,
                                 /* isSystemProvider= */ false,
                                 disableSystemAppVerificationForTests,
-                                enabledServices.contains(serviceInfo));
+                                enabledServices.contains(serviceInfo.getComponentName()));
                 if (!cpi.isSystemProvider()) {
                     services.add(cpi);
                 }
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index a2fa139..a389223 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -58,11 +58,13 @@
             setTitle(title);
         }
 
-        final Bundle extras = getIntent().getExtras();
-        mCallback = (DreamService.DreamActivityCallbacks) extras.getBinder(EXTRA_CALLBACK);
-
-        if (mCallback != null) {
+        final Object callback = getIntent().getExtras().getBinder(EXTRA_CALLBACK);
+        if (callback instanceof DreamService.DreamActivityCallbacks) {
+            mCallback = (DreamService.DreamActivityCallbacks) callback;
             mCallback.onActivityCreated(this);
+        } else {
+            mCallback = null;
+            finishAndRemoveTask();
         }
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ef76ce3..800fc97 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -163,6 +163,7 @@
 import android.view.translation.ViewTranslationRequest;
 import android.view.translation.ViewTranslationResponse;
 import android.widget.Checkable;
+import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.ScrollBarDrawable;
 import android.window.OnBackInvokedDispatcher;
@@ -10356,7 +10357,7 @@
         final AutofillManager afm = getAutofillManager();
         // keep default behavior
         if (afm == null) return false;
-        return afm.isMatchingAutofillableHeuristics(this);
+        return afm.isMatchingAutofillableHeuristicsForNotImportantViews(this);
     }
 
     private boolean isAutofillable() {
@@ -10372,26 +10373,47 @@
             return false;
         }
 
+        // Experiment imeAction heuristic on important views. If the important view doesn't pass
+        // heuristic check, also check augmented autofill in case augmented autofill is enabled
+        // for the activity
+        // TODO: refactor to have both important views and not important views use the same
+        // heuristic check
+        if (isImportantForAutofill()
+            && afm.isTriggerFillRequestOnFilteredImportantViewsEnabled()
+            && this instanceof EditText
+            && !afm.isPassingImeActionCheck((EditText) this)
+            && !notifyAugmentedAutofillIfNeeded(afm)) {
+            // TODO: add a log to indicate what has filtered out the view
+            return false;
+        }
+
         if (!isImportantForAutofill()) {
             // If view matches heuristics and is not denied, it will be treated same as view that's
             // important for autofill
-            if (afm.isMatchingAutofillableHeuristics(this)
+            if (afm.isMatchingAutofillableHeuristicsForNotImportantViews(this)
                     && !afm.isActivityDeniedForAutofillForUnimportantView()) {
                 return getAutofillViewId() > LAST_APP_AUTOFILL_ID;
             }
             // View is not important for "regular" autofill, so we must check if Augmented Autofill
             // is enabled for the activity
-            final AutofillOptions options = mContext.getAutofillOptions();
-            if (options == null || !options.isAugmentedAutofillEnabled(mContext)) {
+            if (!notifyAugmentedAutofillIfNeeded(afm)){
                 return false;
             }
-
-            afm.notifyViewEnteredForAugmentedAutofill(this);
         }
 
         return getAutofillViewId() > LAST_APP_AUTOFILL_ID;
     }
 
+    /** @hide **/
+    public boolean notifyAugmentedAutofillIfNeeded(AutofillManager afm) {
+        final AutofillOptions options = mContext.getAutofillOptions();
+        if (options == null || !options.isAugmentedAutofillEnabled(mContext)) {
+            return false;
+        }
+        afm.notifyViewEnteredForAugmentedAutofill(this);
+        return true;
+    }
+
     /** @hide */
     public boolean canNotifyAutofillEnterExitEvent() {
         return isAutofillable() && isAttachedToWindow();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8b4da1..24dcb69 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5718,7 +5718,7 @@
     private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 34;
     private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 35;
     private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
-
+    private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -6006,6 +6006,11 @@
                 case MSG_REQUEST_SCROLL_CAPTURE:
                     handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
                     break;
+                case MSG_PAUSED_FOR_SYNC_TIMEOUT:
+                    Log.e(mTag, "Timedout waiting to unpause for sync");
+                    mNumPausedForSync = 0;
+                    scheduleTraversals();
+                    break;
             }
         }
     }
@@ -11584,9 +11589,16 @@
             mActiveSurfaceSyncGroup = new SurfaceSyncGroup(mTag);
             mActiveSurfaceSyncGroup.setAddedToSyncListener(() -> {
                 Runnable runnable = () -> {
-                    mNumPausedForSync--;
-                    if (!mIsInTraversal && mNumPausedForSync == 0) {
-                        scheduleTraversals();
+                    // Check if it's already 0 because the timeout could have reset the count to
+                    // 0 and we don't want to go negative.
+                    if (mNumPausedForSync > 0) {
+                        mNumPausedForSync--;
+                    }
+                    if (mNumPausedForSync == 0) {
+                        mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+                        if (!mIsInTraversal) {
+                            scheduleTraversals();
+                        }
                     }
                 };
 
@@ -11613,6 +11625,8 @@
         }
 
         mNumPausedForSync++;
+        mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+        mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, 1000);
         return mActiveSurfaceSyncGroup;
     };
 
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 6d78e60..7da69e7 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -156,6 +156,14 @@
             "trigger_fill_request_on_unimportant_view";
 
     /**
+     * Whether to apply heuristic check on important views.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_FILTERED_IMPORTANT_VIEWS =
+            "trigger_fill_request_on_filtered_important_views";
+
+    /**
      * Continas imeAction ids that is irrelevant for autofill. For example, ime_action_search. We
      * use this to avoid trigger fill request on unimportant views.
      *
@@ -167,6 +175,14 @@
     @SuppressLint("IntentName")
     public static final String DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS =
             "non_autofillable_ime_action_ids";
+
+    /**
+     * Whether to enable autofill on all view types (not just checkbox, spinner, datepicker etc...)
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES =
+            "should_enable_autofill_on_all_view_types";
     // END AUTOFILL FOR ALL APPS FLAGS //
 
 
@@ -315,6 +331,28 @@
     }
 
     /**
+     * Whether to apply heuristic check on important views before triggering fill request
+     *
+     * @hide
+     */
+    public static boolean isTriggerFillRequestOnFilteredImportantViewsEnabled() {
+        return DeviceConfig.getBoolean(
+            DeviceConfig.NAMESPACE_AUTOFILL,
+            DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_FILTERED_IMPORTANT_VIEWS, false);
+    }
+
+    /**
+     * Whether to enable autofill on all view types.
+     *
+     * @hide
+     */
+    public static boolean shouldEnableAutofillOnAllViewTypes(){
+        return DeviceConfig.getBoolean(
+            DeviceConfig.NAMESPACE_AUTOFILL,
+            DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES, false);
+    }
+
+    /**
      * Get the non-autofillable ime actions from flag. This will be used in filtering
      * condition to trigger fill request.
      *
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 14c781b..0deaa76 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -674,6 +674,12 @@
     // Indicate whether trigger fill request on unimportant views is enabled
     private boolean mIsTriggerFillRequestOnUnimportantViewEnabled = false;
 
+    // Indicate whether to apply heuristic check on important views before trigger fill request
+    private boolean mIsTriggerFillRequestOnFilteredImportantViewsEnabled;
+
+    // Indicate whether to enable autofill for all view types
+    private boolean mShouldEnableAutofillOnAllViewTypes;
+
     // A set containing all non-autofillable ime actions passed by flag
     private Set<String> mNonAutofillableImeActionIdSet = new ArraySet<>();
 
@@ -855,6 +861,12 @@
         mIsTriggerFillRequestOnUnimportantViewEnabled =
             AutofillFeatureFlags.isTriggerFillRequestOnUnimportantViewEnabled();
 
+        mIsTriggerFillRequestOnFilteredImportantViewsEnabled =
+            AutofillFeatureFlags.isTriggerFillRequestOnFilteredImportantViewsEnabled();
+
+        mShouldEnableAutofillOnAllViewTypes =
+            AutofillFeatureFlags.shouldEnableAutofillOnAllViewTypes();
+
         mNonAutofillableImeActionIdSet =
             AutofillFeatureFlags.getNonAutofillableImeActionIdSetFromFlag();
 
@@ -865,14 +877,39 @@
         mIsPackageFullyDeniedForAutofillForUnimportantView =
             isPackageFullyDeniedForAutofillForUnimportantView(denyListString, packageName);
 
-        mIsPackagePartiallyDeniedForAutofillForUnimportantView =
-            isPackagePartiallyDeniedForAutofillForUnimportantView(denyListString, packageName);
+        if (!mIsPackageFullyDeniedForAutofillForUnimportantView) {
+            mIsPackagePartiallyDeniedForAutofillForUnimportantView =
+                isPackagePartiallyDeniedForAutofillForUnimportantView(denyListString, packageName);
+        }
 
         if (mIsPackagePartiallyDeniedForAutofillForUnimportantView) {
             setDeniedActivitySetWithDenyList(denyListString, packageName);
         }
     }
 
+    /**
+     * Whether to apply heuristic check on important views before triggering fill request
+     *
+     * @hide
+     */
+    public boolean isTriggerFillRequestOnFilteredImportantViewsEnabled() {
+        return mIsTriggerFillRequestOnFilteredImportantViewsEnabled;
+    }
+
+    /**
+     * Whether view passes the imeAction check
+     *
+     * @hide
+     */
+    public boolean isPassingImeActionCheck(EditText editText) {
+        final int actionId = editText.getImeOptions();
+        if (mNonAutofillableImeActionIdSet.contains(String.valueOf(actionId))) {
+            // TODO: add a log to indicate what has filtered out the view
+            return false;
+        }
+        return true;
+    }
+
     private boolean isPackageFullyDeniedForAutofillForUnimportantView(
             @NonNull String denyListString, @NonNull String packageName) {
         // If "PackageName:;" is in the string, then it means the package name is in denylist
@@ -957,25 +994,32 @@
      *
      * @hide
      */
-    public final boolean isMatchingAutofillableHeuristics(@NonNull View view) {
+    public final boolean isMatchingAutofillableHeuristicsForNotImportantViews(@NonNull View view) {
         if (!mIsTriggerFillRequestOnUnimportantViewEnabled) {
             return false;
         }
+
+        // TODO: remove the autofill type check when this function is applied on both important and
+        // not important views.
+        // This check is needed here because once the view type check is lifted, addiditional
+        // unimportant views will be added to the assist structure which may cuase system health
+        // regression (viewGroup#populateChidlrenForAutofill() calls this function to decide whether
+        // to include child view)
+        if (view.getAutofillType() == View.AUTOFILL_TYPE_NONE) return false;
+
         if (view instanceof EditText) {
-            final int actionId = ((EditText) view).getImeOptions();
-            if (mNonAutofillableImeActionIdSet.contains(String.valueOf(actionId))) {
-                return false;
-            }
-            return true;
+            return isPassingImeActionCheck((EditText) view);
         }
+
         if (view instanceof CheckBox
-                || view instanceof Spinner
-                || view instanceof DatePicker
-                || view instanceof TimePicker
-                || view instanceof RadioGroup) {
+            || view instanceof Spinner
+            || view instanceof DatePicker
+            || view instanceof TimePicker
+            || view instanceof RadioGroup) {
             return true;
         }
-        return false;
+
+        return mShouldEnableAutofillOnAllViewTypes;
     }
 
 
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 78de954..0672d63 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -22,6 +22,8 @@
 import android.os.Binder;
 import android.os.BinderProxy;
 import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -60,6 +62,7 @@
     private static final int MAX_COUNT = 100;
 
     private static final AtomicInteger sCounter = new AtomicInteger(0);
+    private static final int TRANSACTION_READY_TIMEOUT = 1000;
 
     private static Supplier<Transaction> sTransactionFactory = Transaction::new;
 
@@ -111,6 +114,13 @@
      */
     private final Binder mToken = new Binder();
 
+    private static final Object sHandlerThreadLock = new Object();
+    @GuardedBy("sHandlerThreadLock")
+    private static HandlerThread sHandlerThread;
+    private Handler mHandler;
+
+    private boolean mTimeoutAdded;
+
     private static boolean isLocalBinder(IBinder binder) {
         return !(binder instanceof BinderProxy);
     }
@@ -538,6 +548,11 @@
                             + transactionReadyCallback.hashCode());
         }
 
+        // Start the timeout when this SurfaceSyncGroup has been added to a parent SurfaceSyncGroup.
+        // This is because if the other SurfaceSyncGroup has bugs and doesn't complete, this SSG
+        // will get stuck. It's better to complete this SSG even if the parent SSG is broken.
+        addTimeout();
+
         boolean finished = false;
         Runnable addedToSyncListener = null;
         synchronized (mLock) {
@@ -641,6 +656,9 @@
         }
         mTransactionReadyConsumer.accept(mTransaction);
         mFinished = true;
+        if (mTimeoutAdded) {
+            mHandler.removeCallbacksAndMessages(this);
+        }
     }
 
     /**
@@ -701,6 +719,12 @@
             }
         }
 
+        // Start the timeout when another SSG has been added to this SurfaceSyncGroup. This is
+        // because if the other SurfaceSyncGroup has bugs and doesn't complete, it will affect this
+        // SSGs. So it's better to just add a timeout in case the other SSG doesn't invoke the
+        // callback and complete this SSG.
+        addTimeout();
+
         return transactionReadyCallback;
     }
 
@@ -731,6 +755,41 @@
         }
     }
 
+    private void addTimeout() {
+        synchronized (sHandlerThreadLock) {
+            if (sHandlerThread == null) {
+                sHandlerThread = new HandlerThread("SurfaceSyncGroupTimer");
+                sHandlerThread.start();
+            }
+        }
+
+        synchronized (mLock) {
+            if (mTimeoutAdded) {
+                // We only need one timeout for the entire SurfaceSyncGroup since we just want to
+                // ensure it doesn't stay stuck forever.
+                return;
+            }
+
+            if (mHandler == null) {
+                mHandler = new Handler(sHandlerThread.getLooper());
+            }
+
+            mTimeoutAdded = true;
+        }
+
+        Runnable runnable = () -> {
+            Log.e(TAG, "Failed to receive transaction ready in " + TRANSACTION_READY_TIMEOUT
+                    + "ms. Marking SurfaceSyncGroup as ready " + mName);
+            // Clear out any pending syncs in case the other syncs can't complete or timeout due to
+            // a crash.
+            synchronized (mLock) {
+                mPendingSyncs.clear();
+            }
+            markSyncReady();
+        };
+        mHandler.postDelayed(runnable, this, TRANSACTION_READY_TIMEOUT);
+    }
+
     /**
      * A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
      * implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index a646df3..a781454 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -18,7 +18,7 @@
 
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.app.trust.IStrongAuthTracker;
 import android.os.Bundle;
 import android.security.keystore.recovery.WrappedApplicationKey;
@@ -95,7 +95,7 @@
             in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
-    StartLockscreenValidationRequest startRemoteLockscreenValidation();
+    RemoteLockscreenValidationSession startRemoteLockscreenValidation();
     RemoteLockscreenValidationResult validateRemoteLockscreen(in byte[] encryptedCredential);
     boolean hasSecureLockScreen();
     boolean tryUnlockWithCachedUnifiedChallenge(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index b86020e..4d91410 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -29,7 +29,7 @@
 import android.annotation.UserIdInt;
 import android.app.PropertyInvalidatedCache;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.PasswordMetrics;
 import android.app.trust.IStrongAuthTracker;
@@ -1875,7 +1875,7 @@
      * Starts a session to verify lockscreen credentials provided by a remote device.
      */
     @NonNull
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
         try {
             return getLockSettings().startRemoteLockscreenValidation();
         } catch (RemoteException e) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8fdefab..5be18fb4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4203,6 +4203,16 @@
     <permission android:name="android.permission.WRITE_DEVICE_CONFIG"
         android:protectionLevel="signature|verifier|configurator"/>
 
+    <!-- @SystemApi @TestApi @hide Allows an application to read/write sync disabled mode config.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG"
+        android:protectionLevel="signature|verifier|configurator"/>
+
+    <!-- @SystemApi @TestApi @hide Allows an application to modify only allowlisted settings.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG"
+        android:protectionLevel="signature|verifier|configurator"/>
+
     <!-- @SystemApi @hide Allows an application to read config settings.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.READ_DEVICE_CONFIG"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16511a6..4d2747a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2772,7 +2772,8 @@
 
     <!-- Whether the device allows users to start in background visible on the default display.
          Should be false for most devices, except passenger-only automotive build (i.e., when
-         Android runs in a separate system in the back seat to manage the passenger displays) -->
+         Android runs in a separate system in the back seat to manage the passenger displays).
+         When set to true, config_multiuserVisibleBackgroundUsers must also be true. -->
     <bool name="config_multiuserVisibleBackgroundUsersOnDefaultDisplay">false</bool>
 
     <!-- Whether to automatically switch to the designated Dock User (the user chosen for
@@ -3020,10 +3021,18 @@
             >com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
 
     <!-- Package name of the system service that implements the shared connectivity service -->
-    <string translatable="false" name="shared_connectivity_service_package"></string>
+    <string translatable="false" name="config_sharedConnectivityServicePackage"></string>
 
     <!-- Intent action used when binding to the shared connectivity service -->
-    <string translatable="false" name="shared_connectivity_service_intent_action"></string>
+    <string translatable="false" name="config_sharedConnectivityServiceIntentAction"></string>
+
+    <!-- The system and settings UI can support all the features of instant tether. If set to false,
+     instant tether will run in notifications mode -->
+    <bool name="config_hotspotNetworksEnabledForService">false</bool>
+
+    <!-- The system and settings UI can support all the features of known networks. If set to false,
+         known networks will run in notifications mode -->
+    <bool name="config_knownNetworksEnabledForService">false</bool>
 
     <!-- Component name of the activity that shows the usb containment status. -->
     <string name="config_usbContaminantActivity" translatable="false"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c34d31c..a15833d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4960,8 +4960,10 @@
   <java-symbol type="bool" name="config_stopSystemPackagesByDefault"/>
   <java-symbol type="string" name="config_wearServiceComponent" />
 
-  <java-symbol type="string" name="shared_connectivity_service_package" />
-  <java-symbol type="string" name="shared_connectivity_service_intent_action" />
+  <java-symbol type="string" name="config_sharedConnectivityServicePackage" />
+  <java-symbol type="string" name="config_sharedConnectivityServiceIntentAction" />
+  <java-symbol type="bool" name="config_hotspotNetworksEnabledForService"/>
+  <java-symbol type="bool" name="config_knownNetworksEnabledForService"/>
 
   <!-- Whether to show weather on the lockscreen by default. -->
   <java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index ccf8085..b6fc137 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -91,7 +91,7 @@
         mConfigsCacheGenerationStore = new MemoryIntArray(2);
         mConfigsCacheGenerationStore.set(0, 123);
         mConfigsCacheGenerationStore.set(1, 456);
-        mSettingsCacheGenerationStore = new MemoryIntArray(3);
+        mSettingsCacheGenerationStore = new MemoryIntArray(2);
         mSettingsCacheGenerationStore.set(0, 234);
         mSettingsCacheGenerationStore.set(1, 567);
         mConfigsStorage = new HashMap<>();
@@ -163,10 +163,6 @@
                     Bundle incomingBundle = invocationOnMock.getArgument(4);
                     String key = invocationOnMock.getArgument(3);
                     String value = incomingBundle.getString(Settings.NameValueTable.VALUE);
-                    boolean newSetting = false;
-                    if (!mSettingsStorage.containsKey(key)) {
-                        newSetting = true;
-                    }
                     mSettingsStorage.put(key, value);
                     int currentGeneration;
                     // Different settings have different generation codes
@@ -177,18 +173,12 @@
                         currentGeneration = mSettingsCacheGenerationStore.get(1);
                         mSettingsCacheGenerationStore.set(1, ++currentGeneration);
                     }
-                    if (newSetting) {
-                        // Tracking the generation of all unset settings.
-                        // Increment when a new setting is inserted
-                        currentGeneration = mSettingsCacheGenerationStore.get(2);
-                        mSettingsCacheGenerationStore.set(2, ++currentGeneration);
-                    }
                     return null;
                 });
 
         // Returns the value corresponding to a setting, or null if the setting
-        // doesn't have a value stored for it. Returns the generation key
-        // if the caller asked for the generation key.
+        // doesn't have a value stored for it. Returns the generation key if the value isn't null
+        // and the caller asked for the generation key.
         when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()),
                 eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer(
                 invocationOnMock -> {
@@ -199,15 +189,9 @@
                     Bundle bundle = new Bundle();
                     bundle.putSerializable(Settings.NameValueTable.VALUE, value);
 
-                    if (incomingBundle.containsKey(
+                    if (value != null && incomingBundle.containsKey(
                             Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
-                        int index;
-                        if (value != null) {
-                            index = key.equals(SETTING) ? 0 : 1;
-                        } else {
-                            // special index for unset settings
-                            index = 2;
-                        }
+                        int index = key.equals(SETTING) ? 0 : 1;
                         bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
                                 mSettingsCacheGenerationStore);
                         bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
@@ -377,38 +361,16 @@
     }
 
     @Test
-    public void testCaching_unsetSetting() throws Exception {
+    public void testCaching_nullSetting() throws Exception {
         String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
         verify(mMockIContentProvider, times(1)).call(any(), any(),
                 eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
         assertThat(returnedValue).isNull();
 
         String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        // The first unset setting's generation number is cached
-        verifyNoMoreInteractions(mMockIContentProvider);
-        assertThat(cachedValue).isNull();
-
-        String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
+        // Empty list won't be cached
         verify(mMockIContentProvider, times(2)).call(any(), any(),
                 eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue2).isNull();
-
-        String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING);
-        // The second unset setting's generation number is cached
-        verifyNoMoreInteractions(mMockIContentProvider);
-        assertThat(cachedValue2).isNull();
-
-        Settings.Secure.putString(mMockContentResolver, SETTING, "a");
-        // The generation for unset settings should have changed
-        returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
-        verify(mMockIContentProvider, times(3)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue2).isNull();
-
-        // The generation tracker for the first setting should have change because it's set now
-        returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        verify(mMockIContentProvider, times(4)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue).isEqualTo("a");
+        assertThat(cachedValue).isNull();
     }
 }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 913eaf2..ffc5ff2 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1099,6 +1099,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
+    "-1104347731": {
+      "message": "Setting requested orientation %s for %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1103716954": {
       "message": "Not removing %s due to exit animation",
       "level": "VERBOSE",
diff --git a/data/keyboards/Vendor_004c_Product_0265.idc b/data/keyboards/Vendor_004c_Product_0265.idc
deleted file mode 120000
index 707dfcf..0000000
--- a/data/keyboards/Vendor_004c_Product_0265.idc
+++ /dev/null
@@ -1 +0,0 @@
-Vendor_05ac_Product_0265.idc
\ No newline at end of file
diff --git a/data/keyboards/Vendor_03f6_Product_a001.idc b/data/keyboards/Vendor_03f6_Product_a001.idc
deleted file mode 100644
index bcb4ee3..0000000
--- a/data/keyboards/Vendor_03f6_Product_a001.idc
+++ /dev/null
@@ -1,22 +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.
-
-#
-# Brydge Touchpad
-#
-
-# Reports from this touchpad sometimes get bunched together due to Bluetooth
-# batching, leading to bad timestamps that mess up finger velocity calculations.
-# To fix this, set a fake delta using the touchpad's known report rate.
-gestureProp.Fake_Timestamp_Delta = 0.010
diff --git a/data/keyboards/Vendor_046d_Product_4011.idc b/data/keyboards/Vendor_046d_Product_4011.idc
deleted file mode 100644
index 3a23830..0000000
--- a/data/keyboards/Vendor_046d_Product_4011.idc
+++ /dev/null
@@ -1,32 +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.
-
-#
-# Logitech Wireless Touchpad
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -313.240741792594
-gestureProp.Pressure_Calibration_Slope = 4.39678062436752
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Palm_Pressure = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_046d_Product_4101.idc b/data/keyboards/Vendor_046d_Product_4101.idc
deleted file mode 100644
index 47e2530..0000000
--- a/data/keyboards/Vendor_046d_Product_4101.idc
+++ /dev/null
@@ -1,31 +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.
-
-#
-# Logitech T650
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -0.439288351750068
-gestureProp.Pressure_Calibration_Slope = 3.05998553523335
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_046d_Product_4102.idc b/data/keyboards/Vendor_046d_Product_4102.idc
deleted file mode 100644
index e33a28a..0000000
--- a/data/keyboards/Vendor_046d_Product_4102.idc
+++ /dev/null
@@ -1,24 +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.
-
-#
-# Logitech TK820
-#
-
-gestureProp.Touchpad_Stack_Version = 2
-# Pressure jumps around a lot on this touchpad, so allow that:
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Pressure_Calibration_Offset = -18.8078435
-gestureProp.Pressure_Calibration_Slope = 2.466208137
diff --git a/data/keyboards/Vendor_046d_Product_b00c.idc b/data/keyboards/Vendor_046d_Product_b00c.idc
deleted file mode 100644
index a49970c..0000000
--- a/data/keyboards/Vendor_046d_Product_b00c.idc
+++ /dev/null
@@ -1,31 +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.
-
-#
-# Logitech T651
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -4.46520447177073
-gestureProp.Pressure_Calibration_Slope = 3.21071719332644
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_05ac_Product_0265.idc b/data/keyboards/Vendor_05ac_Product_0265.idc
deleted file mode 100644
index d72de64..0000000
--- a/data/keyboards/Vendor_05ac_Product_0265.idc
+++ /dev/null
@@ -1,34 +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.
-
-#
-# Apple Magic Trackpad 2 configuration file
-#   Bluetooth vendor ID 004c
-#   USB vendor ID 05ac
-#
-
-gestureProp.Pressure_Calibration_Offset = 30
-gestureProp.Palm_Pressure = 250.0
-gestureProp.Palm_Width = 20.0
-gestureProp.Multiple_Palm_Width = 20.0
-
-# Enable Stationary Wiggle Filter
-gestureProp.Stationary_Wiggle_Filter_Enabled = 1
-gestureProp.Finger_Moving_Energy = 0.0008
-gestureProp.Finger_Moving_Hysteresis = 0.0004
-
-# Avoid accidental scroll/move on finger lift
-gestureProp.Max_Stationary_Move_Speed = 47
-gestureProp.Max_Stationary_Move_Speed_Hysteresis = 1
-gestureProp.Max_Stationary_Move_Suppress_Distance = 0.2
diff --git a/data/keyboards/Vendor_05ac_Product_030e.idc b/data/keyboards/Vendor_05ac_Product_030e.idc
deleted file mode 100644
index 23a2e18..0000000
--- a/data/keyboards/Vendor_05ac_Product_030e.idc
+++ /dev/null
@@ -1,38 +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.
-
-#
-# Apple Magic Trackpad
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-# We are using raw touch major value as pressure value, so set the Palm
-# pressure threshold high.
-gestureProp.Palm_Pressure = 1000
-gestureProp.Compute_Surface_Area_from_Pressure = 0
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-# NOTE: bias on X-axis is uncalibrated
-gestureProp.Touchpad_Device_Output_Bias_on_X-Axis = -283.3226025266607
-gestureProp.Touchpad_Device_Output_Bias_on_Y-Axis = -283.3226025266607
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-# Drumroll suppression causes janky movement on this touchpad.
-gestureProp.Drumroll_Suppression_Enable = 0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
index 68ff806..65955b1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -33,7 +34,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/** A representation of a folding feature for both Extension and Sidecar.
+/**
+ * A representation of a folding feature for both Extension and Sidecar.
  * For Sidecar this is the same as combining {@link androidx.window.sidecar.SidecarDeviceState} and
  * {@link androidx.window.sidecar.SidecarDisplayFeature}. For Extensions this is the mirror of
  * {@link androidx.window.extensions.layout.FoldingFeature}.
@@ -67,10 +69,11 @@
     public static final int COMMON_STATE_UNKNOWN = -1;
 
     /**
-     * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
-     * and Extensions do not match exactly.
+     * A common state that contains no folding features. For example, an in-folding device in the
+     * "closed" device state.
      */
-    public static final int COMMON_STATE_FLAT = 3;
+    public static final int COMMON_STATE_NO_FOLDING_FEATURES = 1;
+
     /**
      * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
      * Sidecar and Extensions do not match exactly.
@@ -78,9 +81,27 @@
     public static final int COMMON_STATE_HALF_OPENED = 2;
 
     /**
-     * The possible states for a folding hinge.
+     * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
+     * and Extensions do not match exactly.
      */
-    @IntDef({COMMON_STATE_UNKNOWN, COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
+    public static final int COMMON_STATE_FLAT = 3;
+
+    /**
+     * A common state where the hinge state should be derived using the base state from
+     * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)} instead of the
+     * emulated state. This is an internal state and must not be passed to clients.
+     */
+    public static final int COMMON_STATE_USE_BASE_STATE = 1000;
+
+    /**
+     * The possible states for a folding hinge. Common in this context means normalized between
+     * extensions and sidecar.
+     */
+    @IntDef({COMMON_STATE_UNKNOWN,
+            COMMON_STATE_NO_FOLDING_FEATURES,
+            COMMON_STATE_HALF_OPENED,
+            COMMON_STATE_FLAT,
+            COMMON_STATE_USE_BASE_STATE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {
     }
@@ -167,7 +188,7 @@
             }
             String stateString = featureMatcher.group(6);
             stateString = stateString == null ? "" : stateString;
-            final int state;
+            @State final int state;
             switch (stateString) {
                 case PATTERN_STATE_FLAT:
                     state = COMMON_STATE_FLAT;
@@ -191,8 +212,8 @@
     @NonNull
     private final Rect mRect;
 
-    CommonFoldingFeature(int type, int state, @NonNull Rect rect) {
-        assertValidState(state);
+    CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) {
+        assertReportableState(state);
         this.mType = type;
         this.mState = state;
         if (rect.width() == 0 && rect.height() == 0) {
@@ -231,13 +252,22 @@
     }
 
     @Override
+    public String toString() {
+        return "CommonFoldingFeature=[Type: " + mType + ", state: " + mState + "]";
+    }
+
+    @Override
     public int hashCode() {
         return Objects.hash(mType, mState, mRect);
     }
 
-    private static void assertValidState(@Nullable Integer state) {
-        if (state != null && state != COMMON_STATE_FLAT
-                && state != COMMON_STATE_HALF_OPENED && state != COMMON_STATE_UNKNOWN) {
+    /**
+     * Checks if the provided folding feature state should be reported to clients. See
+     * {@link androidx.window.extensions.layout.FoldingFeature}
+     */
+    private static void assertReportableState(@State int state) {
+        if (state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED
+                && state != COMMON_STATE_UNKNOWN) {
             throw new IllegalArgumentException("Invalid state: " + state
                     + "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED");
         }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 0bdf98c..66f27f5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -19,6 +19,7 @@
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
 import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE;
 import static androidx.window.common.CommonFoldingFeature.parseListFromString;
 
 import android.annotation.NonNull;
@@ -52,13 +53,54 @@
             DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    /**
+     * Emulated device state {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} to
+     * {@link CommonFoldingFeature.State} map.
+     */
     private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
 
+    /**
+     * Emulated device state received via
+     * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
+     * "Emulated" states differ from "base" state in the sense that they may not correspond 1:1 with
+     * physical device states. They represent the state of the device when various software
+     * features and APIs are applied. The emulated states generally consist of all "base" states,
+     * but may have additional states such as "concurrent" or "rear display". Concurrent mode for
+     * example is activated via public API and can be active in both the "open" and "half folded"
+     * device states.
+     */
     private int mCurrentDeviceState = INVALID_DEVICE_STATE;
 
+    /**
+     * Base device state received via
+     * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)}.
+     * "Base" in this context means the "physical" state of the device.
+     */
+    private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE;
+
     @NonNull
     private final BaseDataProducer<String> mRawFoldSupplier;
 
+    private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
+        @Override
+        public void onStateChanged(int state) {
+            mCurrentDeviceState = state;
+            mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
+                    .this::notifyFoldingFeatureChange);
+        }
+
+        @Override
+        public void onBaseStateChanged(int state) {
+            mCurrentBaseDeviceState = state;
+
+            if (mDeviceStateToPostureMap.get(mCurrentDeviceState)
+                    == COMMON_STATE_USE_BASE_STATE) {
+                mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
+                        .this::notifyFoldingFeatureChange);
+            }
+        }
+    };
+
     public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
             @NonNull BaseDataProducer<String> rawFoldSupplier) {
         mRawFoldSupplier = rawFoldSupplier;
@@ -92,12 +134,8 @@
         }
 
         if (mDeviceStateToPostureMap.size() > 0) {
-            DeviceStateCallback deviceStateCallback = (state) -> {
-                mCurrentDeviceState = state;
-                mRawFoldSupplier.getData(this::notifyFoldingFeatureChange);
-            };
             Objects.requireNonNull(context.getSystemService(DeviceStateManager.class))
-                    .registerCallback(context.getMainExecutor(), deviceStateCallback);
+                    .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
         }
     }
 
@@ -178,11 +216,18 @@
     }
 
     private List<CommonFoldingFeature> calculateFoldingFeature(String displayFeaturesString) {
-        final int globalHingeState = globalHingeState();
-        return parseListFromString(displayFeaturesString, globalHingeState);
+        return parseListFromString(displayFeaturesString, currentHingeState());
     }
 
-    private int globalHingeState() {
-        return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+    @CommonFoldingFeature.State
+    private int currentHingeState() {
+        @CommonFoldingFeature.State
+        int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+
+        if (posture == CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
+            posture = mDeviceStateToPostureMap.get(mCurrentBaseDeviceState, COMMON_STATE_UNKNOWN);
+        }
+
+        return posture;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 852fae6..ef53839 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -139,6 +139,30 @@
     private static final boolean BUBBLE_BAR_ENABLED =
             SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
 
+
+    /**
+     * Common interface to send updates to bubble views.
+     */
+    public interface BubbleViewCallback {
+        /** Called when the provided bubble should be removed. */
+        void removeBubble(Bubble removedBubble);
+        /** Called when the provided bubble should be added. */
+        void addBubble(Bubble addedBubble);
+        /** Called when the provided bubble should be updated. */
+        void updateBubble(Bubble updatedBubble);
+        /** Called when the provided bubble should be selected. */
+        void selectionChanged(BubbleViewProvider selectedBubble);
+        /** Called when the provided bubble's suppression state has changed. */
+        void suppressionChanged(Bubble bubble, boolean isSuppressed);
+        /** Called when the expansion state of bubbles has changed. */
+        void expansionChanged(boolean isExpanded);
+        /**
+         * Called when the order of the bubble list has changed. Depending on the expanded state
+         * the pointer might need to be updated.
+         */
+        void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer);
+    }
+
     private final Context mContext;
     private final BubblesImpl mImpl = new BubblesImpl();
     private Bubbles.BubbleExpandListener mExpandListener;
@@ -162,7 +186,6 @@
     // Used to post to main UI thread
     private final ShellExecutor mMainExecutor;
     private final Handler mMainHandler;
-
     private final ShellExecutor mBackgroundExecutor;
 
     private BubbleLogger mLogger;
@@ -1320,6 +1343,58 @@
         });
     }
 
+    private final BubbleViewCallback mBubbleViewCallback = new BubbleViewCallback() {
+        @Override
+        public void removeBubble(Bubble removedBubble) {
+            if (mStackView != null) {
+                mStackView.removeBubble(removedBubble);
+            }
+        }
+
+        @Override
+        public void addBubble(Bubble addedBubble) {
+            if (mStackView != null) {
+                mStackView.addBubble(addedBubble);
+            }
+        }
+
+        @Override
+        public void updateBubble(Bubble updatedBubble) {
+            if (mStackView != null) {
+                mStackView.updateBubble(updatedBubble);
+            }
+        }
+
+        @Override
+        public void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer) {
+            if (mStackView != null) {
+                mStackView.updateBubbleOrder(bubbleOrder, updatePointer);
+            }
+        }
+
+        @Override
+        public void suppressionChanged(Bubble bubble, boolean isSuppressed) {
+            if (mStackView != null) {
+                mStackView.setBubbleSuppressed(bubble, isSuppressed);
+            }
+        }
+
+        @Override
+        public void expansionChanged(boolean isExpanded) {
+            if (mStackView != null) {
+                mStackView.setExpanded(isExpanded);
+            }
+        }
+
+        @Override
+        public void selectionChanged(BubbleViewProvider selectedBubble) {
+            if (mStackView != null) {
+                mStackView.setSelectedBubble(selectedBubble);
+            }
+
+        }
+    };
+
     @SuppressWarnings("FieldCanBeLocal")
     private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
 
@@ -1342,7 +1417,8 @@
             // Lazy load overflow bubbles from disk
             loadOverflowBubblesFromDisk();
 
-            mStackView.updateOverflowButtonDot();
+            // If bubbles in the overflow have a dot, make sure the overflow shows a dot
+            updateOverflowButtonDot();
 
             // Update bubbles in overflow.
             if (mOverflowListener != null) {
@@ -1357,9 +1433,7 @@
                 final Bubble bubble = removed.first;
                 @Bubbles.DismissReason final int reason = removed.second;
 
-                if (mStackView != null) {
-                    mStackView.removeBubble(bubble);
-                }
+                mBubbleViewCallback.removeBubble(bubble);
 
                 // Leave the notification in place if we're dismissing due to user switching, or
                 // because DND is suppressing the bubble. In both of those cases, we need to be able
@@ -1389,49 +1463,47 @@
             }
             mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
 
-            if (update.addedBubble != null && mStackView != null) {
+            if (update.addedBubble != null) {
                 mDataRepository.addBubble(mCurrentUserId, update.addedBubble);
-                mStackView.addBubble(update.addedBubble);
+                mBubbleViewCallback.addBubble(update.addedBubble);
             }
 
-            if (update.updatedBubble != null && mStackView != null) {
-                mStackView.updateBubble(update.updatedBubble);
+            if (update.updatedBubble != null) {
+                mBubbleViewCallback.updateBubble(update.updatedBubble);
             }
 
-            if (update.suppressedBubble != null && mStackView != null) {
-                mStackView.setBubbleSuppressed(update.suppressedBubble, true);
+            if (update.suppressedBubble != null) {
+                mBubbleViewCallback.suppressionChanged(update.suppressedBubble, true);
             }
 
-            if (update.unsuppressedBubble != null && mStackView != null) {
-                mStackView.setBubbleSuppressed(update.unsuppressedBubble, false);
+            if (update.unsuppressedBubble != null) {
+                mBubbleViewCallback.suppressionChanged(update.unsuppressedBubble, false);
             }
 
             boolean collapseStack = update.expandedChanged && !update.expanded;
 
             // At this point, the correct bubbles are inflated in the stack.
             // Make sure the order in bubble data is reflected in bubble row.
-            if (update.orderChanged && mStackView != null) {
+            if (update.orderChanged) {
                 mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
                 // if the stack is going to be collapsed, do not update pointer position
                 // after reordering
-                mStackView.updateBubbleOrder(update.bubbles, !collapseStack);
+                mBubbleViewCallback.bubbleOrderChanged(update.bubbles, !collapseStack);
             }
 
             if (collapseStack) {
-                mStackView.setExpanded(false);
+                mBubbleViewCallback.expansionChanged(/* expanded= */ false);
                 mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
             }
 
-            if (update.selectionChanged && mStackView != null) {
-                mStackView.setSelectedBubble(update.selectedBubble);
+            if (update.selectionChanged) {
+                mBubbleViewCallback.selectionChanged(update.selectedBubble);
             }
 
             // Expanding? Apply this last.
             if (update.expandedChanged && update.expanded) {
-                if (mStackView != null) {
-                    mStackView.setExpanded(true);
-                    mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
-                }
+                mBubbleViewCallback.expansionChanged(/* expanded= */ true);
+                mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
             }
 
             mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
@@ -1442,6 +1514,19 @@
         }
     };
 
+    private void updateOverflowButtonDot() {
+        BubbleOverflow overflow = mBubbleData.getOverflow();
+        if (overflow == null) return;
+
+        for (Bubble b : mBubbleData.getOverflowBubbles()) {
+            if (b.showDot()) {
+                overflow.setShowDot(true);
+                return;
+            }
+        }
+        overflow.setShowDot(false);
+    }
+
     private boolean handleDismissalInterception(BubbleEntry entry,
             @Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
         if (isSummaryOfBubbles(entry)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 7d71089e..0b947c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1359,16 +1359,6 @@
         updateOverflowVisibility();
     }
 
-    void updateOverflowButtonDot() {
-        for (Bubble b : mBubbleData.getOverflowBubbles()) {
-            if (b.showDot()) {
-                mBubbleOverflow.setShowDot(true);
-                return;
-            }
-        }
-        mBubbleOverflow.setShowDot(false);
-    }
-
     /**
      * Handle theme changes.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index ab96856..3d1ed87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -31,6 +31,7 @@
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipAppOpsListener;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -70,7 +71,7 @@
             ShellInit shellInit,
             ShellController shellController,
             TvPipBoundsState tvPipBoundsState,
-            PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             TvPipBoundsController tvPipBoundsController,
             PipAppOpsListener pipAppOpsListener,
@@ -91,7 +92,7 @@
                         shellInit,
                         shellController,
                         tvPipBoundsState,
-                        pipSizeSpecHandler,
+                        pipDisplayLayoutState,
                         tvPipBoundsAlgorithm,
                         tvPipBoundsController,
                         pipAppOpsListener,
@@ -141,14 +142,15 @@
     @WMSingleton
     @Provides
     static TvPipBoundsState provideTvPipBoundsState(Context context,
-            PipSizeSpecHandler pipSizeSpecHandler) {
-        return new TvPipBoundsState(context, pipSizeSpecHandler);
+            PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) {
+        return new TvPipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState);
     }
 
     @WMSingleton
     @Provides
-    static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
-        return new PipSizeSpecHandler(context);
+    static PipSizeSpecHandler providePipSizeSpecHelper(Context context,
+            PipDisplayLayoutState pipDisplayLayoutState) {
+        return new PipSizeSpecHandler(context, pipDisplayLayoutState);
     }
 
     // Handler needed for loadDrawableAsync() in PipControlsViewController
@@ -203,7 +205,7 @@
             TvPipMenuController tvPipMenuController,
             SyncTransactionQueue syncTransactionQueue,
             TvPipBoundsState tvPipBoundsState,
-            PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             PipTransitionState pipTransitionState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipAnimationController pipAnimationController,
@@ -215,7 +217,7 @@
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new TvPipTaskOrganizer(context,
-                syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipSizeSpecHandler,
+                syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipDisplayLayoutState,
                 tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController,
                 pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
                 splitScreenControllerOptional, displayController, pipUiEventLogger,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 948bf2d..ba0f073 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -65,6 +65,7 @@
 import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -344,6 +345,7 @@
             PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
             PipBoundsState pipBoundsState,
             PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             PipMotionHelper pipMotionHelper,
             PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController,
@@ -360,18 +362,18 @@
         return Optional.ofNullable(PipController.create(
                 context, shellInit, shellCommandHandler, shellController,
                 displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
-                pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipMotionHelper,
-                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
-                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
-                taskStackListener, pipParamsChangedForwarder, displayInsetsController,
-                oneHandedController, mainExecutor));
+                pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipDisplayLayoutState,
+                pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+                pipTransitionState, pipTouchHandler, pipTransitionController,
+                windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+                displayInsetsController, oneHandedController, mainExecutor));
     }
 
     @WMSingleton
     @Provides
     static PipBoundsState providePipBoundsState(Context context,
-            PipSizeSpecHandler pipSizeSpecHandler) {
-        return new PipBoundsState(context, pipSizeSpecHandler);
+            PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) {
+        return new PipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState);
     }
 
     @WMSingleton
@@ -388,8 +390,9 @@
 
     @WMSingleton
     @Provides
-    static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
-        return new PipSizeSpecHandler(context);
+    static PipSizeSpecHandler providePipSizeSpecHelper(Context context,
+            PipDisplayLayoutState pipDisplayLayoutState) {
+        return new PipSizeSpecHandler(context, pipDisplayLayoutState);
     }
 
     @WMSingleton
@@ -446,7 +449,7 @@
             SyncTransactionQueue syncTransactionQueue,
             PipTransitionState pipTransitionState,
             PipBoundsState pipBoundsState,
-            PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PhonePipMenuController menuPhoneController,
             PipAnimationController pipAnimationController,
@@ -458,7 +461,7 @@
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
-                syncTransactionQueue, pipTransitionState, pipBoundsState, pipSizeSpecHandler,
+                syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState,
                 pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
                 pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
                 splitScreenControllerOptional, displayController, pipUiEventLogger,
@@ -477,12 +480,12 @@
     static PipTransitionController providePipTransitionController(Context context,
             ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
             PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipBoundsState pipBoundsState, PipSizeSpecHandler pipSizeSpecHandler,
+            PipBoundsState pipBoundsState, PipDisplayLayoutState pipDisplayLayoutState,
             PipTransitionState pipTransitionState, PhonePipMenuController pipMenuController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreenController> splitScreenOptional) {
         return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
-                pipBoundsState, pipSizeSpecHandler, pipTransitionState, pipMenuController,
+                pipBoundsState, pipDisplayLayoutState, pipTransitionState, pipMenuController,
                 pipBoundsAlgorithm, pipAnimationController, pipSurfaceTransactionHelper,
                 splitScreenOptional);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 9796e4c..2d84d21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -267,6 +267,11 @@
             mLaunchRootTask = taskInfo;
         }
 
+        if (mHomeTask != null && mHomeTask.taskId == taskInfo.taskId
+                && !taskInfo.equals(mHomeTask)) {
+            mHomeTask = taskInfo;
+        }
+
         super.onTaskInfoChanged(taskInfo);
     }
 
@@ -376,6 +381,7 @@
         final WindowContainerTransaction wct = getWindowContainerTransaction();
         final Rect taskBounds = calculateBounds();
         wct.setBounds(mLaunchRootTask.token, taskBounds);
+        wct.setBounds(mHomeTask.token, new Rect(0, 0, mDisplayWidth, mDisplayHeight));
         mSyncQueue.queue(wct);
         final SurfaceControl finalLeash = mLaunchRootLeash;
         mSyncQueue.runInSync(t -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index 2624ee5..d961d86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -70,4 +70,9 @@
      * Sets the next pip animation type to be the alpha animation.
      */
     oneway void setPipAnimationTypeToAlpha() = 5;
+
+    /**
+     * Sets the height and visibility of the Launcher keep clear area.
+     */
+    oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 6;
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 5be18d8..f664808 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -30,7 +30,6 @@
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Size;
-import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
@@ -77,6 +76,7 @@
     private final @NonNull Rect mExpandedBounds = new Rect();
     private final @NonNull Rect mNormalMovementBounds = new Rect();
     private final @NonNull Rect mExpandedMovementBounds = new Rect();
+    private final @NonNull PipDisplayLayoutState mPipDisplayLayoutState;
     private final Point mMaxSize = new Point();
     private final Point mMinSize = new Point();
     private final @NonNull Context mContext;
@@ -86,8 +86,6 @@
     private @Nullable PipReentryState mPipReentryState;
     private final @Nullable PipSizeSpecHandler mPipSizeSpecHandler;
     private @Nullable ComponentName mLastPipComponentName;
-    private int mDisplayId = Display.DEFAULT_DISPLAY;
-    private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
     private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
     private boolean mIsImeShowing;
     private int mImeHeight;
@@ -120,10 +118,12 @@
     private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
     private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
 
-    public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler) {
+    public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState) {
         mContext = context;
         reloadResources();
         mPipSizeSpecHandler = pipSizeSpecHandler;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
     }
 
     /** Reloads the resources. */
@@ -290,31 +290,16 @@
         return mLastPipComponentName;
     }
 
-    /** Get the current display id. */
-    public int getDisplayId() {
-        return mDisplayId;
-    }
-
-    /** Set the current display id for the associated display layout. */
-    public void setDisplayId(int displayId) {
-        mDisplayId = displayId;
-    }
-
     /** Returns the display's bounds. */
     @NonNull
     public Rect getDisplayBounds() {
-        return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
-    }
-
-    /** Update the display layout. */
-    public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
-        mDisplayLayout.set(displayLayout);
+        return mPipDisplayLayoutState.getDisplayBounds();
     }
 
     /** Get a copy of the display layout. */
     @NonNull
     public DisplayLayout getDisplayLayout() {
-        return new DisplayLayout(mDisplayLayout);
+        return mPipDisplayLayoutState.getDisplayLayout();
     }
 
     @VisibleForTesting
@@ -568,7 +553,6 @@
         pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
         pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
         pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
-        pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
         pw.println(innerPrefix + "mStashedState=" + mStashedState);
         pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
new file mode 100644
index 0000000..0f76af4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
@@ -0,0 +1,91 @@
+/*
+ * 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.pip;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.dagger.WMSingleton;
+
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+/**
+ * Acts as a source of truth for display related information for PIP.
+ */
+@WMSingleton
+public class PipDisplayLayoutState {
+    private static final String TAG = PipDisplayLayoutState.class.getSimpleName();
+
+    private Context mContext;
+    private int mDisplayId;
+    @NonNull private DisplayLayout mDisplayLayout;
+
+    @Inject
+    public PipDisplayLayoutState(Context context) {
+        mContext = context;
+        mDisplayLayout = new DisplayLayout();
+    }
+
+    /** Update the display layout. */
+    public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
+        mDisplayLayout.set(displayLayout);
+    }
+
+    /** Get a copy of the display layout. */
+    @NonNull
+    public DisplayLayout getDisplayLayout() {
+        return new DisplayLayout(mDisplayLayout);
+    }
+
+    /** Get the display bounds */
+    @NonNull
+    public Rect getDisplayBounds() {
+        return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+    }
+
+    /**
+     * Apply a rotation to this layout and its parameters.
+     * @param targetRotation
+     */
+    public void rotateTo(@Surface.Rotation int targetRotation) {
+        mDisplayLayout.rotateTo(mContext.getResources(), targetRotation);
+    }
+
+    /** Get the current display id */
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /** Set the current display id for the associated display layout. */
+    public void setDisplayId(int displayId) {
+        mDisplayId = displayId;
+    }
+
+    /** Dumps internal state. */
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
+        pw.println(innerPrefix + "getDisplayBounds=" + getDisplayBounds());
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e9d2571..d5b9c5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -79,13 +79,11 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
@@ -128,7 +126,7 @@
     private final Context mContext;
     private final SyncTransactionQueue mSyncTransactionQueue;
     private final PipBoundsState mPipBoundsState;
-    private final PipSizeSpecHandler mPipSizeSpecHandler;
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     private final @NonNull PipMenuController mPipMenuController;
     private final PipAnimationController mPipAnimationController;
@@ -316,7 +314,7 @@
             @NonNull SyncTransactionQueue syncTransactionQueue,
             @NonNull PipTransitionState pipTransitionState,
             @NonNull PipBoundsState pipBoundsState,
-            @NonNull PipSizeSpecHandler pipSizeSpecHandler,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState,
             @NonNull PipBoundsAlgorithm boundsHandler,
             @NonNull PipMenuController pipMenuController,
             @NonNull PipAnimationController pipAnimationController,
@@ -332,7 +330,7 @@
         mSyncTransactionQueue = syncTransactionQueue;
         mPipTransitionState = pipTransitionState;
         mPipBoundsState = pipBoundsState;
-        mPipSizeSpecHandler = pipSizeSpecHandler;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
         mPipBoundsAlgorithm = boundsHandler;
         mPipMenuController = pipMenuController;
         mPipTransitionController = pipTransitionController;
@@ -653,7 +651,7 @@
 
         // If the displayId of the task is different than what PipBoundsHandler has, then update
         // it. This is possible if we entered PiP on an external display.
-        if (info.displayId != mPipBoundsState.getDisplayId()
+        if (info.displayId != mPipDisplayLayoutState.getDisplayId()
                 && mOnDisplayIdChangeCallback != null) {
             mOnDisplayIdChangeCallback.accept(info.displayId);
         }
@@ -1179,6 +1177,20 @@
     }
 
     /**
+     * Directly update the animator bounds.
+     */
+    public void updateAnimatorBounds(Rect bounds) {
+        final PipAnimationController.PipTransitionAnimator animator =
+                mPipAnimationController.getCurrentAnimator();
+        if (animator != null && animator.isRunning()) {
+            if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
+                animator.updateEndValue(bounds);
+            }
+            animator.setDestinationBounds(bounds);
+        }
+    }
+
+    /**
      * Handles all changes to the PictureInPictureParams.
      */
     protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) {
@@ -1621,15 +1633,15 @@
         return animator;
     }
 
-    /** Computes destination bounds in old rotation and returns source hint rect if available. */
+    /** Computes destination bounds in old rotation and returns source hint rect if available.
+     *
+     * Note: updates the internal state of {@link PipDisplayLayoutState} by applying a rotation
+     * transformation onto the display layout.
+     */
     private @Nullable Rect computeRotatedBounds(int rotationDelta, int direction,
             Rect outDestinationBounds, Rect sourceHintRect) {
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
-            DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
-            layoutCopy.rotateTo(mContext.getResources(), mNextRotation);
-            mPipBoundsState.setDisplayLayout(layoutCopy);
-            mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+            mPipDisplayLayoutState.rotateTo(mNextRotation);
 
             final Rect displayBounds = mPipBoundsState.getDisplayBounds();
             outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index a91a342..2848f34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -64,8 +64,6 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -85,7 +83,7 @@
 
     private final Context mContext;
     private final PipTransitionState mPipTransitionState;
-    private final PipSizeSpecHandler mPipSizeSpecHandler;
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
     private final Optional<SplitScreenController> mSplitScreenOptional;
@@ -116,7 +114,7 @@
             @NonNull ShellTaskOrganizer shellTaskOrganizer,
             @NonNull Transitions transitions,
             PipBoundsState pipBoundsState,
-            PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             PipTransitionState pipTransitionState,
             PipMenuController pipMenuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
@@ -127,7 +125,7 @@
                 pipBoundsAlgorithm, pipAnimationController);
         mContext = context;
         mPipTransitionState = pipTransitionState;
-        mPipSizeSpecHandler = pipSizeSpecHandler;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
         mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
@@ -313,11 +311,7 @@
             // initial state under the new rotation.
             int rotationDelta = deltaRotation(startRotation, endRotation);
             if (rotationDelta != Surface.ROTATION_0) {
-                DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
-                layoutCopy.rotateTo(mContext.getResources(), endRotation);
-                mPipBoundsState.setDisplayLayout(layoutCopy);
-                mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+                mPipDisplayLayoutState.rotateTo(endRotation);
 
                 final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
                 wct.setBounds(mRequestedEnterTask, destinationBounds);
@@ -398,7 +392,7 @@
                 // Launcher may update the Shelf height during the animation, which will update the
                 // destination bounds. Because this is in fixed rotation, We need to make sure the
                 // finishTransaction is using the updated bounds in the display rotation.
-                final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+                final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
                 final Rect finishBounds = new Rect(destinationBounds);
                 rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation);
                 mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds);
@@ -640,7 +634,7 @@
             @NonNull TaskInfo taskInfo) {
         startTransaction.apply();
         finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
-                mPipBoundsState.getDisplayBounds());
+                mPipDisplayLayoutState.getDisplayBounds());
         mPipOrganizer.onExitPipFinished(taskInfo);
         finishCallback.onTransitionFinished(null, null);
     }
@@ -834,13 +828,9 @@
     /** Computes destination bounds in old rotation and updates source hint rect if available. */
     private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation,
             TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) {
-        DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
+        mPipDisplayLayoutState.rotateTo(endRotation);
 
-        layoutCopy.rotateTo(mContext.getResources(), endRotation);
-        mPipBoundsState.setDisplayLayout(layoutCopy);
-        mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
-
-        final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+        final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
         outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
         // Transform the destination bounds to current display coordinates.
         rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
index c6b5ce9..db6138a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -93,6 +93,11 @@
         return hasEnteredPip(mState);
     }
 
+    /** Returns true if activity is currently entering PiP mode. */
+    public boolean isEnteringPip() {
+        return isEnteringPip(mState);
+    }
+
     public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
         mInSwipePipToHomeTransition = inSwipePipToHomeTransition;
     }
@@ -130,6 +135,11 @@
         return state == ENTERED_PIP;
     }
 
+    /** Returns true if activity is currently entering PiP mode. */
+    public static boolean isEnteringPip(@TransitionState int state) {
+        return state == ENTERING_PIP;
+    }
+
     public interface OnPipTransitionStateChangedListener {
         void onPipTransitionStateChanged(@TransitionState int oldState,
                 @TransitionState int newState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a1483a8..db6ef1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -83,6 +83,7 @@
 import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
@@ -101,6 +102,7 @@
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -138,6 +140,7 @@
     private PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
     private PipBoundsState mPipBoundsState;
     private PipSizeSpecHandler mPipSizeSpecHandler;
+    private PipDisplayLayoutState mPipDisplayLayoutState;
     private PipMotionHelper mPipMotionHelper;
     private PipTouchHandler mTouchHandler;
     private PipTransitionController mPipTransitionController;
@@ -181,14 +184,20 @@
             // early bail out if the keep clear areas feature is disabled
             return;
         }
-        // only move if already in pip, other transitions account for keep clear areas
-        if (mPipTransitionState.hasEnteredPip()) {
+        // only move if we're in PiP or transitioning into PiP
+        if (!mPipTransitionState.shouldBlockResizeRequest()) {
             Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
                     mPipBoundsAlgorithm);
             // only move if the bounds are actually different
             if (destBounds != mPipBoundsState.getBounds()) {
-                mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
-                        mEnterAnimationDuration, null);
+                if (mPipTransitionState.hasEnteredPip()) {
+                    // if already in PiP, schedule separate animation
+                    mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
+                            mEnterAnimationDuration, null);
+                } else if (mPipTransitionState.isEnteringPip()) {
+                    // while entering PiP we just need to update animator bounds
+                    mPipTaskOrganizer.updateAnimatorBounds(destBounds);
+                }
             }
         }
     }
@@ -303,7 +312,7 @@
 
                 @Override
                 public void onDisplayAdded(int displayId) {
-                    if (displayId != mPipBoundsState.getDisplayId()) {
+                    if (displayId != mPipDisplayLayoutState.getDisplayId()) {
                         return;
                     }
                     onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
@@ -312,7 +321,7 @@
 
                 @Override
                 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
-                    if (displayId != mPipBoundsState.getDisplayId()) {
+                    if (displayId != mPipDisplayLayoutState.getDisplayId()) {
                         return;
                     }
                     onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
@@ -322,7 +331,7 @@
                 @Override
                 public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
                         Set<Rect> unrestricted) {
-                    if (mPipBoundsState.getDisplayId() == displayId) {
+                    if (mPipDisplayLayoutState.getDisplayId() == displayId) {
                         if (mEnablePipKeepClearAlgorithm) {
                             mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
 
@@ -382,6 +391,7 @@
             PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
             PipBoundsState pipBoundsState,
             PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             PipMotionHelper pipMotionHelper,
             PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController,
@@ -404,8 +414,8 @@
         return new PipController(context, shellInit, shellCommandHandler, shellController,
                 displayController, pipAnimationController, pipAppOpsListener,
                 pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler,
-                pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
-                pipTransitionState, pipTouchHandler, pipTransitionController,
+                pipDisplayLayoutState, pipMotionHelper, pipMediaController, phonePipMenuController,
+                pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
                 windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
                 displayInsetsController, oneHandedController, mainExecutor)
                 .mImpl;
@@ -422,6 +432,7 @@
             PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
             @NonNull PipBoundsState pipBoundsState,
             PipSizeSpecHandler pipSizeSpecHandler,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState,
             PipMotionHelper pipMotionHelper,
             PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController,
@@ -448,6 +459,7 @@
         mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
         mPipBoundsState = pipBoundsState;
         mPipSizeSpecHandler = pipSizeSpecHandler;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
         mPipMotionHelper = pipMotionHelper;
         mPipTaskOrganizer = pipTaskOrganizer;
         mPipTransitionState = pipTransitionState;
@@ -475,7 +487,7 @@
                 INPUT_CONSUMER_PIP, mMainExecutor);
         mPipTransitionController.registerPipTransitionCallback(this);
         mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
-            mPipBoundsState.setDisplayId(displayId);
+            mPipDisplayLayoutState.setDisplayId(displayId);
             onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
                     false /* saveRestoreSnapFraction */);
         });
@@ -515,11 +527,10 @@
 
         // Ensure that we have the display info in case we get calls to update the bounds before the
         // listener calls back
-        mPipBoundsState.setDisplayId(mContext.getDisplayId());
+        mPipDisplayLayoutState.setDisplayId(mContext.getDisplayId());
 
         DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
-        mPipSizeSpecHandler.setDisplayLayout(layout);
-        mPipBoundsState.setDisplayLayout(layout);
+        mPipDisplayLayoutState.setDisplayLayout(layout);
 
         try {
             mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener);
@@ -614,12 +625,12 @@
                     }
                 });
 
-        mDisplayInsetsController.addInsetsChangedListener(mPipBoundsState.getDisplayId(),
+        mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
                 new DisplayInsetsController.OnInsetsChangedListener() {
                     @Override
                     public void insetsChanged(InsetsState insetsState) {
-                        DisplayLayout pendingLayout =
-                                mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId());
+                        DisplayLayout pendingLayout = mDisplayController
+                                .getDisplayLayout(mPipDisplayLayoutState.getDisplayId());
                         if (mIsInFixedRotation
                                 || pendingLayout.rotation()
                                 != mPipBoundsState.getDisplayLayout().rotation()) {
@@ -627,8 +638,8 @@
                             return;
                         }
                         int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
-                        onDisplayChangedUncheck(
-                                mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
+                        onDisplayChangedUncheck(mDisplayController
+                                        .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()),
                                 false /* saveRestoreSnapFraction */);
                         int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
                         if (!mEnablePipKeepClearAlgorithm) {
@@ -714,7 +725,7 @@
     }
 
     private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
-        if (!mPipBoundsState.getDisplayLayout().isSameGeometry(layout)) {
+        if (!mPipDisplayLayoutState.getDisplayLayout().isSameGeometry(layout)) {
             PipAnimationController.PipTransitionAnimator animator =
                     mPipAnimationController.getCurrentAnimator();
             if (animator != null && animator.isRunning()) {
@@ -728,11 +739,10 @@
     private void onDisplayChangedUncheck(DisplayLayout layout, boolean saveRestoreSnapFraction) {
         Runnable updateDisplayLayout = () -> {
             final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
-                    && mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
+                    && mPipDisplayLayoutState.getDisplayLayout().rotation() != layout.rotation();
 
             // update the internal state of objects subscribed to display changes
-            mPipSizeSpecHandler.setDisplayLayout(layout);
-            mPipBoundsState.setDisplayLayout(layout);
+            mPipDisplayLayoutState.setDisplayLayout(layout);
 
             final WindowContainerTransaction wct =
                     fromRotation ? new WindowContainerTransaction() : null;
@@ -756,11 +766,13 @@
                     mPipBoundsState.getStashedState());
 
             // Scale PiP on density dpi change, so it appears to be the same size physically.
-            final boolean densityDpiChanged = mPipBoundsState.getDisplayLayout().densityDpi() != 0
-                    && (mPipBoundsState.getDisplayLayout().densityDpi() != layout.densityDpi());
+            final boolean densityDpiChanged =
+                    mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
+                    && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
+                            != layout.densityDpi());
             if (densityDpiChanged) {
                 final float scale = (float) layout.densityDpi()
-                        / mPipBoundsState.getDisplayLayout().densityDpi();
+                        / mPipDisplayLayoutState.getDisplayLayout().densityDpi();
                 postChangeBounds.set(0, 0,
                         (int) (postChangeBounds.width() * scale),
                         (int) (postChangeBounds.height() * scale));
@@ -775,8 +787,8 @@
             pipSnapAlgorithm.applySnapFraction(postChangeBounds, postChangeMovementBounds,
                     snapFraction, mPipBoundsState.getStashedState(),
                     mPipBoundsState.getStashOffset(),
-                    mPipBoundsState.getDisplayBounds(),
-                    mPipBoundsState.getDisplayLayout().stableInsets());
+                    mPipDisplayLayoutState.getDisplayBounds(),
+                    mPipDisplayLayoutState.getDisplayLayout().stableInsets());
 
             if (densityDpiChanged) {
                 // Using PipMotionHelper#movePip directly here may cause race condition since
@@ -874,6 +886,21 @@
         }
     }
 
+    private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
+        if (visible) {
+            Rect rect = new Rect(
+                    0, mPipBoundsState.getDisplayBounds().bottom - height,
+                    mPipBoundsState.getDisplayBounds().right,
+                    mPipBoundsState.getDisplayBounds().bottom);
+            Set<Rect> restrictedKeepClearAreas = new HashSet<>(
+                    mPipBoundsState.getRestrictedKeepClearAreas());
+            restrictedKeepClearAreas.add(rect);
+            mPipBoundsState.setKeepClearAreas(restrictedKeepClearAreas,
+                    mPipBoundsState.getUnrestrictedKeepClearAreas());
+            updatePipPositionForKeepClearAreas();
+        }
+    }
+
     private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
         mOnIsInPipStateChangedListener = callback;
         if (mOnIsInPipStateChangedListener != null) {
@@ -1018,7 +1045,7 @@
         // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
         // passing to mTouchHandler/mPipTaskOrganizer
         final Rect outBounds = new Rect(toBounds);
-        final int rotation = mPipBoundsState.getDisplayLayout().rotation();
+        final int rotation = mPipDisplayLayoutState.getDisplayLayout().rotation();
 
         mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
         mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds());
@@ -1042,11 +1069,7 @@
     private void onDisplayRotationChangedNotInPip(Context context, int toRotation) {
         // Update the display layout, note that we have to do this on every rotation even if we
         // aren't in PIP since we need to update the display layout to get the right resources
-        DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
-        layoutCopy.rotateTo(context.getResources(), toRotation);
-        mPipBoundsState.setDisplayLayout(layoutCopy);
-        mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+        mPipDisplayLayoutState.rotateTo(toRotation);
     }
 
     /**
@@ -1059,7 +1082,7 @@
             Rect outInsetBounds,
             int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
         // Bail early if the event is not sent to current display
-        if ((displayId != mPipBoundsState.getDisplayId()) || (fromRotation == toRotation)) {
+        if ((displayId != mPipDisplayLayoutState.getDisplayId()) || (fromRotation == toRotation)) {
             return false;
         }
 
@@ -1083,11 +1106,7 @@
                         mPipBoundsState.getStashedState());
 
         // Update the display layout
-        DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
-
-        layoutCopy.rotateTo(context.getResources(), toRotation);
-        mPipBoundsState.setDisplayLayout(layoutCopy);
-        mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+        mPipDisplayLayoutState.rotateTo(toRotation);
 
         // Calculate the stack bounds in the new orientation based on same fraction along the
         // rotated movement bounds.
@@ -1095,8 +1114,8 @@
                 postChangeStackBounds, false /* adjustForIme */);
         pipSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
                 snapFraction, mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(),
-                mPipBoundsState.getDisplayBounds(),
-                mPipBoundsState.getDisplayLayout().stableInsets());
+                mPipDisplayLayoutState.getDisplayBounds(),
+                mPipDisplayLayoutState.getDisplayLayout().stableInsets());
 
         mPipBoundsAlgorithm.getInsetBounds(outInsetBounds);
         outBounds.set(postChangeStackBounds);
@@ -1114,6 +1133,7 @@
         mPipBoundsState.dump(pw, innerPrefix);
         mPipInputConsumer.dump(pw, innerPrefix);
         mPipSizeSpecHandler.dump(pw, innerPrefix);
+        mPipDisplayLayoutState.dump(pw, innerPrefix);
     }
 
     /**
@@ -1240,6 +1260,14 @@
         }
 
         @Override
+        public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
+            executeRemoteCallWithTaskPermission(mController, "setLauncherKeepClearAreaHeight",
+                    (controller) -> {
+                        controller.setLauncherKeepClearAreaHeight(visible, height);
+                    });
+        }
+
+        @Override
         public void setPipAnimationListener(IPipAnimationListener listener) {
             executeRemoteCallWithTaskPermission(mController, "setPipAnimationListener",
                     (controller) -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
index d03d075..23988a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
@@ -31,6 +31,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 
 import java.io.PrintWriter;
 
@@ -40,10 +41,9 @@
 public class PipSizeSpecHandler {
     private static final String TAG = PipSizeSpecHandler.class.getSimpleName();
 
-    @NonNull private final DisplayLayout mDisplayLayout = new DisplayLayout();
+    @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
 
-    @VisibleForTesting
-    final SizeSpecSource mSizeSpecSourceImpl;
+    private final SizeSpecSource mSizeSpecSourceImpl;
 
     /** The preferred minimum (and default minimum) size specified by apps. */
     @Nullable private Size mOverrideMinSize;
@@ -361,8 +361,9 @@
         }
     }
 
-    public PipSizeSpecHandler(Context context) {
+    public PipSizeSpecHandler(Context context, PipDisplayLayoutState pipDisplayLayoutState) {
         mContext = context;
+        mPipDisplayLayoutState = pipDisplayLayoutState;
 
         boolean enablePipSizeLargeScreen = SystemProperties
                 .getBoolean("persist.wm.debug.enable_pip_size_large_screen", false);
@@ -403,15 +404,9 @@
         mSizeSpecSourceImpl.reloadResources();
     }
 
-    /** Returns the display's bounds. */
     @NonNull
-    public Rect getDisplayBounds() {
-        return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
-    }
-
-    /** Update the display layout. */
-    public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
-        mDisplayLayout.set(displayLayout);
+    private Rect getDisplayBounds() {
+        return mPipDisplayLayoutState.getDisplayBounds();
     }
 
     public Point getScreenEdgeInsets() {
@@ -423,11 +418,12 @@
      */
     public Rect getInsetBounds() {
         Rect insetBounds = new Rect();
-        Rect insets = mDisplayLayout.stableInsets();
+        DisplayLayout displayLayout = mPipDisplayLayoutState.getDisplayLayout();
+        Rect insets = displayLayout.stableInsets();
         insetBounds.set(insets.left + mScreenEdgeInsets.x,
                 insets.top + mScreenEdgeInsets.y,
-                mDisplayLayout.width() - insets.right - mScreenEdgeInsets.x,
-                mDisplayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+                displayLayout.width() - insets.right - mScreenEdgeInsets.x,
+                displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
         return insetBounds;
     }
 
@@ -522,8 +518,8 @@
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
-        pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl.toString());
-        pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
+        pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl);
         pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
+        pw.println(innerPrefix + "mScreenEdgeInsets=" + mScreenEdgeInsets);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index 22b3f49..e1737ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -31,6 +31,7 @@
 
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 
 import java.lang.annotation.Retention;
@@ -75,8 +76,9 @@
     private Insets mPipMenuTemporaryDecorInsets = Insets.NONE;
 
     public TvPipBoundsState(@NonNull Context context,
-            @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
-        super(context, pipSizeSpecHandler);
+            @NonNull PipSizeSpecHandler pipSizeSpecHandler,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState) {
+        super(context, pipSizeSpecHandler, pipDisplayLayoutState);
         mContext = context;
         updateDefaultGravity();
         mPreviousCollapsedGravity = mDefaultGravity;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index a437a3b..d73723c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -51,11 +51,11 @@
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipAppOpsListener;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.ShellController;
@@ -119,7 +119,7 @@
 
     private final ShellController mShellController;
     private final TvPipBoundsState mTvPipBoundsState;
-    private final PipSizeSpecHandler mPipSizeSpecHandler;
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
     private final TvPipBoundsController mTvPipBoundsController;
     private final PipAppOpsListener mAppOpsListener;
@@ -154,7 +154,7 @@
             ShellInit shellInit,
             ShellController shellController,
             TvPipBoundsState tvPipBoundsState,
-            PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             TvPipBoundsController tvPipBoundsController,
             PipAppOpsListener pipAppOpsListener,
@@ -174,7 +174,7 @@
                 shellInit,
                 shellController,
                 tvPipBoundsState,
-                pipSizeSpecHandler,
+                pipDisplayLayoutState,
                 tvPipBoundsAlgorithm,
                 tvPipBoundsController,
                 pipAppOpsListener,
@@ -196,7 +196,7 @@
             ShellInit shellInit,
             ShellController shellController,
             TvPipBoundsState tvPipBoundsState,
-            PipSizeSpecHandler pipSizeSpecHandler,
+            PipDisplayLayoutState pipDisplayLayoutState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             TvPipBoundsController tvPipBoundsController,
             PipAppOpsListener pipAppOpsListener,
@@ -220,10 +220,11 @@
         DisplayLayout layout = new DisplayLayout(context, context.getDisplay());
 
         mTvPipBoundsState = tvPipBoundsState;
-        mTvPipBoundsState.setDisplayLayout(layout);
-        mTvPipBoundsState.setDisplayId(context.getDisplayId());
-        mPipSizeSpecHandler = pipSizeSpecHandler;
-        mPipSizeSpecHandler.setDisplayLayout(layout);
+
+        mPipDisplayLayoutState = pipDisplayLayoutState;
+        mPipDisplayLayoutState.setDisplayLayout(layout);
+        mPipDisplayLayoutState.setDisplayId(context.getDisplayId());
+
         mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
         mTvPipBoundsController = tvPipBoundsController;
         mTvPipBoundsController.setListener(this);
@@ -392,7 +393,7 @@
     @Override
     public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
             Set<Rect> unrestricted) {
-        if (mTvPipBoundsState.getDisplayId() == displayId) {
+        if (mPipDisplayLayoutState.getDisplayId() == displayId) {
             boolean unrestrictedAreasChanged = !Objects.equals(unrestricted,
                     mTvPipBoundsState.getUnrestrictedKeepClearAreas());
             mTvPipBoundsState.setKeepClearAreas(restricted, unrestricted);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index be9b936..f6856f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -28,6 +28,7 @@
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipMenuController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
@@ -36,7 +37,6 @@
 import com.android.wm.shell.pip.PipTransitionState;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.PipUtils;
-import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.Objects;
@@ -51,7 +51,7 @@
             @NonNull SyncTransactionQueue syncTransactionQueue,
             @NonNull PipTransitionState pipTransitionState,
             @NonNull PipBoundsState pipBoundsState,
-            @NonNull PipSizeSpecHandler pipSizeSpecHandler,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState,
             @NonNull PipBoundsAlgorithm boundsHandler,
             @NonNull PipMenuController pipMenuController,
             @NonNull PipAnimationController pipAnimationController,
@@ -63,8 +63,8 @@
             @NonNull PipUiEventLogger pipUiEventLogger,
             @NonNull ShellTaskOrganizer shellTaskOrganizer,
             ShellExecutor mainExecutor) {
-        super(context, syncTransactionQueue, pipTransitionState, pipBoundsState, pipSizeSpecHandler,
-                boundsHandler, pipMenuController, pipAnimationController,
+        super(context, syncTransactionQueue, pipTransitionState, pipBoundsState,
+                pipDisplayLayoutState, boundsHandler, pipMenuController, pipAnimationController,
                 surfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
                 splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer,
                 mainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 18a3849..a5546e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -207,6 +207,7 @@
     private boolean mIsDividerRemoteAnimating;
     private boolean mIsDropEntering;
     private boolean mIsExiting;
+    private boolean mIsRootTranslucent;
 
     private DefaultMixedHandler mMixedHandler;
     private final Toast mSplitUnsupportedToast;
@@ -422,6 +423,11 @@
             }
         }
 
+        if (!isSplitActive()) {
+            // prevent the fling divider to center transitioni if split screen didn't active.
+            mIsDropEntering = true;
+        }
+
         setSideStagePosition(sideStagePosition, wct);
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         targetStage.evictAllChildren(evictWct);
@@ -436,28 +442,13 @@
         // reparent the task to an invisible split root will make the activity invisible.  Reorder
         // the root task to front to make the entering transition from pip to split smooth.
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, true);
         wct.reorder(targetStage.mRootTaskInfo.token, true);
-        wct.setForceTranslucent(targetStage.mRootTaskInfo.token, true);
-        // prevent the fling divider to center transition
-        mIsDropEntering = true;
-
         targetStage.addTask(task, wct);
 
-        if (ENABLE_SHELL_TRANSITIONS) {
-            prepareEnterSplitScreen(wct);
-            mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
-                    null, this, null /* consumedCallback */, (finishWct, finishT) -> {
-                        if (!evictWct.isEmpty()) {
-                            finishWct.merge(evictWct, true);
-                        }
-                    } /* finishedCallback */);
-        } else {
-            if (!evictWct.isEmpty()) {
-                wct.merge(evictWct, true /* transfer */);
-            }
-            mTaskOrganizer.applyTransaction(wct);
+        if (!evictWct.isEmpty()) {
+            wct.merge(evictWct, true /* transfer */);
         }
+        mTaskOrganizer.applyTransaction(wct);
         return true;
     }
 
@@ -716,7 +707,7 @@
         mSplitLayout.setDivideRatio(splitRatio);
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
+        setRootForceTranslucent(false, wct);
 
         // Make sure the launch options will put tasks in the corresponding split roots
         mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -923,7 +914,7 @@
 
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
+        setRootForceTranslucent(false, wct);
 
         // TODO(b/268008375): Merge APIs to start a split pair into one.
         if (mainTaskId != INVALID_TASK_ID) {
@@ -1351,7 +1342,7 @@
             mSideStage.removeAllTasks(wct, false /* toTop */);
             mMainStage.deactivate(wct, false /* toTop */);
             wct.reorder(mRootTaskInfo.token, false /* onTop */);
-            wct.setForceTranslucent(mRootTaskInfo.token, true);
+            setRootForceTranslucent(true, wct);
             wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
             onTransitionAnimationComplete();
         } else {
@@ -1383,7 +1374,7 @@
                     mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
                     mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
                     finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
-                    finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
+                    setRootForceTranslucent(true, wct);
                     finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
                     mSyncQueue.queue(finishedWCT);
                     mSyncQueue.runInSync(at -> {
@@ -1505,7 +1496,7 @@
         mMainStage.activate(wct, true /* includingTopTask */);
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
+        setRootForceTranslucent(false, wct);
     }
 
     void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -1709,6 +1700,7 @@
 
         mRootTaskInfo = null;
         mRootTaskLeash = null;
+        mIsRootTranslucent = false;
     }
 
 
@@ -1727,7 +1719,7 @@
         // Make the stages adjacent to each other so they occlude what's behind them.
         wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
         wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
-        wct.setForceTranslucent(mRootTaskInfo.token, true);
+        setRootForceTranslucent(true, wct);
         mSplitLayout.getInvisibleBounds(mTempRect1);
         wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
         mSyncQueue.queue(wct);
@@ -1751,7 +1743,7 @@
             mSideStage.evictOtherChildren(wct, taskId);
             updateWindowBounds(mSplitLayout, wct);
             wct.reorder(mRootTaskInfo.token, true);
-            wct.setForceTranslucent(mRootTaskInfo.token, false);
+            setRootForceTranslucent(false, wct);
 
             mSyncQueue.queue(wct);
             mSyncQueue.runInSync(t -> {
@@ -1775,6 +1767,13 @@
         mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, mSplitLayout);
     }
 
+    private void setRootForceTranslucent(boolean translucent, WindowContainerTransaction wct) {
+        if (mIsRootTranslucent == translucent) return;
+
+        mIsRootTranslucent = translucent;
+        wct.setForceTranslucent(mRootTaskInfo.token, translucent);
+    }
+
     private void onStageVisibilityChanged(StageListenerImpl stageListener) {
         // If split didn't active, just ignore this callback because we should already did these
         // on #applyExitSplitScreen.
@@ -1801,12 +1800,11 @@
             // Split entering background.
             wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
                     true /* setReparentLeafTaskIfRelaunch */);
-            if (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping) {
-                wct.setForceTranslucent(mRootTaskInfo.token, true);
-            }
+            setRootForceTranslucent(true, wct);
         } else {
             wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
                     false /* setReparentLeafTaskIfRelaunch */);
+            setRootForceTranslucent(false, wct);
         }
 
         mSyncQueue.queue(wct);
@@ -1938,7 +1936,7 @@
                 mMainStage.activate(wct, true /* includingTopTask */);
                 updateWindowBounds(mSplitLayout, wct);
                 wct.reorder(mRootTaskInfo.token, true);
-                wct.setForceTranslucent(mRootTaskInfo.token, false);
+                setRootForceTranslucent(false, wct);
             }
 
             mSyncQueue.queue(wct);
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 9224b3c..6b7ca42 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
@@ -193,6 +193,7 @@
         private final DragDetector mDragDetector;
 
         private int mDragPointerId = -1;
+        private boolean mIsDragging;
 
         private CaptionTouchEventListener(
                 RunningTaskInfo taskInfo,
@@ -223,19 +224,15 @@
             if (v.getId() != R.id.caption) {
                 return false;
             }
-            mDragDetector.onMotionEvent(e);
-
-            if (e.getAction() != MotionEvent.ACTION_DOWN) {
-                return false;
+            if (e.getAction() == MotionEvent.ACTION_DOWN) {
+                final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+                if (!taskInfo.isFocused) {
+                    final WindowContainerTransaction wct = new WindowContainerTransaction();
+                    wct.reorder(mTaskToken, true /* onTop */);
+                    mSyncQueue.queue(wct);
+                }
             }
-            final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-            if (taskInfo.isFocused) {
-                return false;
-            }
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.reorder(mTaskToken, true /* onTop */);
-            mSyncQueue.queue(wct);
-            return true;
+            return mDragDetector.onMotionEvent(e);
         }
 
         /**
@@ -253,20 +250,24 @@
                     mDragPointerId = e.getPointerId(0);
                     mDragPositioningCallback.onDragPositioningStart(
                             0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
-                    break;
+                    mIsDragging = false;
+                    return false;
                 }
                 case MotionEvent.ACTION_MOVE: {
                     int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningMove(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
-                    break;
+                    mIsDragging = true;
+                    return true;
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
                     int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningEnd(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
-                    break;
+                    final boolean wasDragging = mIsDragging;
+                    mIsDragging = false;
+                    return wasDragging;
                 }
             }
             return true;
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 2aa6d12..6b45149 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
@@ -223,6 +223,7 @@
         private final DragPositioningCallback mDragPositioningCallback;
         private final DragDetector mDragDetector;
 
+        private boolean mIsDragging;
         private int mDragPointerId = -1;
 
         private DesktopModeTouchEventListener(
@@ -273,23 +274,7 @@
             if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
                 return false;
             }
-            switch (e.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    mDragDetector.onMotionEvent(e);
-                    final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-                    if (taskInfo.isFocused) {
-                        return mDragDetector.isDragEvent();
-                    }
-                    return false;
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    boolean res = mDragDetector.isDragEvent();
-                    mDragDetector.onMotionEvent(e);
-                    return res;
-                default:
-                    mDragDetector.onMotionEvent(e);
-                    return mDragDetector.isDragEvent();
-            }
+            return mDragDetector.onMotionEvent(e);
         }
 
         /**
@@ -313,13 +298,15 @@
                     mDragPointerId = e.getPointerId(0);
                     mDragPositioningCallback.onDragPositioningStart(
                             0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
-                    break;
+                    mIsDragging = false;
+                    return false;
                 }
                 case MotionEvent.ACTION_MOVE: {
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningMove(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
-                    break;
+                    mIsDragging = true;
+                    return true;
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
@@ -336,7 +323,9 @@
                                     c -> c.moveToFullscreen(taskInfo));
                         }
                     }
-                    break;
+                    final boolean wasDragging = mIsDragging;
+                    mIsDragging = false;
+                    return wasDragging;
                 }
             }
             return true;
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 cf1850b..65b5a7a 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
@@ -56,10 +56,15 @@
      * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
     */
     boolean onMotionEvent(MotionEvent ev) {
+        final boolean isTouchScreen =
+                (ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
+        if (!isTouchScreen) {
+            // Only touches generate noisy moves, so mouse/trackpad events don't need to filtered
+            // to take the slop threshold into consideration.
+            return mEventHandler.handleMotionEvent(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);
@@ -72,8 +77,12 @@
                     int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
                     float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
                     float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
+                    // Touches generate noisy moves, so only once the move is past the touch
+                    // slop threshold should it be considered a drag.
                     mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
                 }
+                // The event handler should only be notified about 'move' events if a drag has been
+                // detected.
                 if (mIsDragEvent) {
                     return mEventHandler.handleMotionEvent(ev);
                 } else {
@@ -94,10 +103,6 @@
         mTouchSlop = touchSlop;
     }
 
-    boolean isDragEvent() {
-        return mIsDragEvent;
-    }
-
     private void resetState() {
         mIsDragEvent = false;
         mInputDownPoint.set(0, 0);
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 a3d364a..0bce3ac 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,6 +40,7 @@
     private final DisplayController mDisplayController;
     private final WindowDecoration mWindowDecoration;
 
+    private final Rect mTempBounds = new Rect();
     private final Rect mTaskBoundsAtDragStart = new Rect();
     private final PointF mRepositionStartPoint = new PointF();
     private final Rect mRepositionTaskBounds = new Rect();
@@ -117,17 +118,32 @@
         final float deltaX = x - mRepositionStartPoint.x;
         final float deltaY = y - mRepositionStartPoint.y;
         mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
+
+        final Rect stableBounds = mTempBounds;
+        // Make sure the new resizing destination in any direction falls within the stable bounds.
+        // If not, set the bounds back to the old location that was valid to avoid conflicts with
+        // some regions such as the gesture area.
+        mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
+                .getStableBounds(stableBounds);
         if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
-            mRepositionTaskBounds.left += deltaX;
+            final int candidateLeft = mRepositionTaskBounds.left + (int) deltaX;
+            mRepositionTaskBounds.left = (candidateLeft > stableBounds.left)
+                    ? candidateLeft : oldLeft;
         }
         if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
-            mRepositionTaskBounds.right += deltaX;
+            final int candidateRight = mRepositionTaskBounds.right + (int) deltaX;
+            mRepositionTaskBounds.right = (candidateRight < stableBounds.right)
+                    ? candidateRight : oldRight;
         }
         if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
-            mRepositionTaskBounds.top += deltaY;
+            final int candidateTop = mRepositionTaskBounds.top + (int) deltaY;
+            mRepositionTaskBounds.top = (candidateTop > stableBounds.top)
+                    ? candidateTop : oldTop;
         }
         if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
-            mRepositionTaskBounds.bottom += deltaY;
+            final int candidateBottom = mRepositionTaskBounds.bottom + (int) deltaY;
+            mRepositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
+                    ? candidateBottom : oldBottom;
         }
         if (mCtrlType == CTRL_TYPE_UNDEFINED) {
             mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
new file mode 100644
index 0000000..5cd66ab
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.graphics.Rect
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the snapping of a PIP window via dragging, releasing, and checking its final location.
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker){
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            val stringExtras: Map<String, String> =
+                mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
+
+            // cache the starting bounds here
+            val startBounds = Rect()
+
+            setup {
+                // Launch the PIP activity and wait for it to enter PiP mode
+                setRotation(Rotation.ROTATION_0)
+                RemoveAllTasksButHomeRule.removeAllTasksButHome()
+                pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
+
+                val initRegion = pipApp.dragPipWindowAwayFromEdge(wmHelper, 50)
+                startBounds
+                    .set(initRegion.left, initRegion.top, initRegion.right, initRegion.bottom)
+            }
+            transitions {
+                // continue the transition until the PIP snaps
+                pipApp.waitForPipToSnapTo(wmHelper, startBounds)
+            }
+        }
+
+    /** Checks that the visible region area of [pipApp] always moves right during the animation. */
+    @Postsubmit
+    @Test
+    fun pipLayerMovesRight() {
+        flicker.assertLayers {
+            val pipLayerList = layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+            pipLayerList.zipWithNext { previous, current ->
+                current.visibleRegion.isToTheRight(previous.visibleRegion.region)
+            }
+        }
+    }
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTest> {
+            return FlickerTestFactory.nonRotationTests(
+                supportedRotations = listOf(Rotation.ROTATION_0)
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index ec264a6..addc233 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -55,25 +55,28 @@
     private static final float MAX_ASPECT_RATIO = 2f;
     private static final int DEFAULT_MIN_EDGE_SIZE = 100;
 
+    /** The minimum possible size of the override min size's width or height */
+    private static final int OVERRIDABLE_MIN_SIZE = 40;
+
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
     private DisplayInfo mDefaultDisplayInfo;
-    private PipBoundsState mPipBoundsState;
-    private PipSizeSpecHandler mPipSizeSpecHandler;
+    private PipBoundsState mPipBoundsState; private PipSizeSpecHandler mPipSizeSpecHandler;
+    private PipDisplayLayoutState mPipDisplayLayoutState;
 
 
     @Before
     public void setUp() throws Exception {
         initializeMockResources();
-        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
-        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+        mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
                 new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
                 mPipSizeSpecHandler);
 
         DisplayLayout layout =
                 new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true);
-        mPipBoundsState.setDisplayLayout(layout);
-        mPipSizeSpecHandler.setDisplayLayout(layout);
+        mPipDisplayLayoutState.setDisplayLayout(layout);
     }
 
     private void initializeMockResources() {
@@ -88,6 +91,9 @@
                 R.dimen.default_minimal_size_pip_resizable_task,
                 DEFAULT_MIN_EDGE_SIZE);
         res.addOverride(
+                R.dimen.overridable_minimal_size_pip_resizable_task,
+                OVERRIDABLE_MIN_SIZE);
+        res.addOverride(
                 R.string.config_defaultPictureInPictureScreenEdgeInsets,
                 "16x16");
         res.addOverride(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 341a451..f320004 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -27,11 +27,13 @@
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.testing.TestableResources;
 import android.util.Size;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.function.TriConsumer;
+import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 
@@ -52,13 +54,23 @@
     private static final Size DEFAULT_SIZE = new Size(10, 10);
     private static final float DEFAULT_SNAP_FRACTION = 1.0f;
 
+    /** The minimum possible size of the override min size's width or height */
+    private static final int OVERRIDABLE_MIN_SIZE = 40;
+
     private PipBoundsState mPipBoundsState;
     private ComponentName mTestComponentName1;
     private ComponentName mTestComponentName2;
 
     @Before
     public void setUp() {
-        mPipBoundsState = new PipBoundsState(mContext, new PipSizeSpecHandler(mContext));
+        final TestableResources res = mContext.getOrCreateTestableResources();
+        res.addOverride(
+                R.dimen.overridable_minimal_size_pip_resizable_task,
+                OVERRIDABLE_MIN_SIZE);
+
+        PipDisplayLayoutState pipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+        mPipBoundsState = new PipBoundsState(mContext,
+                new PipSizeSpecHandler(mContext, pipDisplayLayoutState), pipDisplayLayoutState);
         mTestComponentName1 = new ComponentName(mContext, "component1");
         mTestComponentName2 = new ComponentName(mContext, "component2");
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index e907cd3..15bb10e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -88,6 +88,7 @@
     private PipTransitionState mPipTransitionState;
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
     private PipSizeSpecHandler mPipSizeSpecHandler;
+    private PipDisplayLayoutState mPipDisplayLayoutState;
 
     private ComponentName mComponent1;
     private ComponentName mComponent2;
@@ -97,15 +98,16 @@
         MockitoAnnotations.initMocks(this);
         mComponent1 = new ComponentName(mContext, "component1");
         mComponent2 = new ComponentName(mContext, "component2");
-        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
-        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+        mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
         mPipTransitionState = new PipTransitionState();
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
                 new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
                 mPipSizeSpecHandler);
         mMainExecutor = new TestShellExecutor();
         mPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockSyncTransactionQueue,
-                mPipTransitionState, mPipBoundsState, mPipSizeSpecHandler,
+                mPipTransitionState, mPipBoundsState, mPipDisplayLayoutState,
                 mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
                 mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
                 mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
@@ -259,8 +261,7 @@
         final DisplayInfo info = new DisplayInfo();
         DisplayLayout layout = new DisplayLayout(info,
                 mContext.getResources(), true, true);
-        mPipBoundsState.setDisplayLayout(layout);
-        mPipSizeSpecHandler.setDisplayLayout(layout);
+        mPipDisplayLayoutState.setDisplayLayout(layout);
         mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
         mPipTaskOrganizer.setSurfaceControlTransactionFactory(
                 MockSurfaceControlHelper::createMockSurfaceControlTransaction);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 4a68287..0e14c69 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -59,6 +59,7 @@
 import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -108,6 +109,7 @@
     @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
     @Mock private PipBoundsState mMockPipBoundsState;
     @Mock private PipSizeSpecHandler mMockPipSizeSpecHandler;
+    @Mock private PipDisplayLayoutState mMockPipDisplayLayoutState;
     @Mock private TaskStackListenerImpl mMockTaskStackListener;
     @Mock private ShellExecutor mMockExecutor;
     @Mock private Optional<OneHandedController> mMockOneHandedController;
@@ -130,12 +132,12 @@
         mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
                 mShellController, mMockDisplayController, mMockPipAnimationController,
                 mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
-                mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
-                mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
-                mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
-                mMockWindowManagerShellWrapper, mMockTaskStackListener,
-                mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
-                mMockOneHandedController, mMockExecutor);
+                mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipDisplayLayoutState,
+                mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController,
+                mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler,
+                mMockPipTransitionController, mMockWindowManagerShellWrapper,
+                mMockTaskStackListener, mMockPipParamsChangedForwarder,
+                mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor);
         mShellInit.init();
         when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
         when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -221,12 +223,12 @@
         assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
                 mShellController, mMockDisplayController, mMockPipAnimationController,
                 mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
-                mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
-                mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
-                mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
-                mMockWindowManagerShellWrapper, mMockTaskStackListener,
-                mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
-                mMockOneHandedController, mMockExecutor));
+                mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipDisplayLayoutState,
+                mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController,
+                mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler,
+                mMockPipTransitionController, mMockWindowManagerShellWrapper,
+                mMockTaskStackListener, mMockPipParamsChangedForwarder,
+                mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor));
     }
 
     @Test
@@ -283,8 +285,8 @@
         when(mMockPipBoundsState.getMinSize()).thenReturn(new Point(1, 1));
         when(mMockPipBoundsState.getMaxSize()).thenReturn(new Point(MAX_VALUE, MAX_VALUE));
         when(mMockPipBoundsState.getBounds()).thenReturn(bounds);
-        when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
-        when(mMockPipBoundsState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
+        when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
+        when(mMockPipDisplayLayoutState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
         when(mMockDisplayController.getDisplayLayout(displayId)).thenReturn(mMockDisplayLayout2);
 
         when(mMockPipTaskOrganizer.isInPip()).thenReturn(true);
@@ -299,8 +301,8 @@
         final int displayId = 1;
         final Rect bounds = new Rect(0, 0, 10, 10);
         when(mMockPipBoundsAlgorithm.getDefaultBounds()).thenReturn(bounds);
-        when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
-        when(mMockPipBoundsState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
+        when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
+        when(mMockPipDisplayLayoutState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
         when(mMockDisplayController.getDisplayLayout(displayId)).thenReturn(mMockDisplayLayout2);
 
         when(mMockPipTaskOrganizer.isInPip()).thenReturn(false);
@@ -314,7 +316,7 @@
     public void onKeepClearAreasChanged_featureDisabled_pipBoundsStateDoesntChange() {
         final int displayId = 1;
         final Rect keepClearArea = new Rect(0, 0, 10, 10);
-        when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
+        when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
 
         mPipController.mDisplaysChangedListener.onKeepClearAreasChanged(
                 displayId, Set.of(keepClearArea), Set.of());
@@ -327,7 +329,7 @@
         mPipController.setEnablePipKeepClearAlgorithm(true);
         final int displayId = 1;
         final Rect keepClearArea = new Rect(0, 0, 10, 10);
-        when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
+        when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
 
         mPipController.mDisplaysChangedListener.onKeepClearAreasChanged(
                 displayId, Set.of(keepClearArea), Set.of());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index c7b9eb3..5b62a94 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -37,6 +37,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -87,11 +88,14 @@
 
     private PipSizeSpecHandler mPipSizeSpecHandler;
 
+    private PipDisplayLayoutState mPipDisplayLayoutState;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
-        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+        mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
         final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
         final PipKeepClearAlgorithmInterface pipKeepClearAlgorithm =
                 new PipKeepClearAlgorithmInterface() {};
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
index d9ff7d1..390c830 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
@@ -33,6 +33,7 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -74,6 +75,7 @@
     @Mock private Context mContext;
     @Mock private Resources mResources;
 
+    private PipDisplayLayoutState mPipDisplayLayoutState;
     private PipSizeSpecHandler mPipSizeSpecHandler;
 
     /**
@@ -137,7 +139,6 @@
     @Before
     public void setUp() {
         initExpectedSizes();
-        setUpStaticSystemPropertiesSession();
 
         when(mResources.getDimensionPixelSize(anyInt())).thenReturn(DEFAULT_MIN_EDGE_SIZE);
         when(mResources.getFloat(anyInt())).thenReturn(OPTIMIZED_ASPECT_RATIO);
@@ -148,11 +149,6 @@
         // set up the mock context for spec handler specifically
         when(mContext.getResources()).thenReturn(mResources);
 
-        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
-
-        // no overridden min edge size by default
-        mPipSizeSpecHandler.setOverrideMinSize(null);
-
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = DISPLAY_EDGE_SIZE;
         displayInfo.logicalHeight = DISPLAY_EDGE_SIZE;
@@ -161,7 +157,14 @@
         // this is done to avoid unnecessary mocking while allowing for custom display dimensions
         DisplayLayout displayLayout = new DisplayLayout(displayInfo, getContext().getResources(),
                 false, false);
-        mPipSizeSpecHandler.setDisplayLayout(displayLayout);
+        mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+        mPipDisplayLayoutState.setDisplayLayout(displayLayout);
+
+        setUpStaticSystemPropertiesSession();
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+
+        // no overridden min edge size by default
+        mPipSizeSpecHandler.setOverrideMinSize(null);
     }
 
     @After
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 5c4863f..d36060f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -35,6 +35,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -92,6 +93,7 @@
     private PipMotionHelper mMotionHelper;
     private PipResizeGestureHandler mPipResizeGestureHandler;
     private PipSizeSpecHandler mPipSizeSpecHandler;
+    private PipDisplayLayoutState mPipDisplayLayoutState;
 
     private DisplayLayout mDisplayLayout;
     private Rect mInsetBounds;
@@ -105,8 +107,9 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
-        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
+        mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState);
         mPipSnapAlgorithm = new PipSnapAlgorithm();
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
                 new PipKeepClearAlgorithmInterface() {}, mPipSizeSpecHandler);
@@ -124,8 +127,7 @@
         mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
 
         mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
-        mPipBoundsState.setDisplayLayout(mDisplayLayout);
-        mPipSizeSpecHandler.setDisplayLayout(mDisplayLayout);
+        mPipDisplayLayoutState.setDisplayLayout(mDisplayLayout);
         mInsetBounds = new Rect(mPipBoundsState.getDisplayBounds().left + INSET,
                 mPipBoundsState.getDisplayBounds().top + INSET,
                 mPipBoundsState.getDisplayBounds().right - INSET,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 30096cb..f9b7723 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -26,6 +26,7 @@
 import android.view.Gravity;
 
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 
@@ -47,6 +48,7 @@
     private TvPipBoundsState mTvPipBoundsState;
     private TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
     private PipSizeSpecHandler mPipSizeSpecHandler;
+    private PipDisplayLayoutState mPipDisplayLayoutState;
 
     @Before
     public void setUp() {
@@ -54,8 +56,10 @@
             return;
         }
         MockitoAnnotations.initMocks(this);
-        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
-        mTvPipBoundsState = new TvPipBoundsState(mContext, mPipSizeSpecHandler);
+        mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState);
+        mTvPipBoundsState = new TvPipBoundsState(mContext, mPipSizeSpecHandler,
+                mPipDisplayLayoutState);
         mTvPipBoundsAlgorithm = new TvPipBoundsAlgorithm(mContext, mTvPipBoundsState,
                 mMockPipSnapAlgorithm, mPipSizeSpecHandler);
 
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 8f66f4e..94c064b 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
@@ -5,13 +5,16 @@
 import android.graphics.Rect
 import android.os.IBinder
 import android.testing.AndroidTestingRunner
+import android.view.Display
 import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_BOTTOM
 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
@@ -19,10 +22,11 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+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
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 /**
@@ -51,6 +55,8 @@
     private lateinit var mockDisplayController: DisplayController
     @Mock
     private lateinit var mockDisplayLayout: DisplayLayout
+    @Mock
+    private lateinit var mockDisplay: Display
 
     private lateinit var taskPositioner: TaskPositioner
 
@@ -68,6 +74,9 @@
         `when`(taskToken.asBinder()).thenReturn(taskBinder)
         `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
         `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
+        `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+        }
 
         mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
             taskId = TASK_ID
@@ -78,6 +87,8 @@
             displayId = DISPLAY_ID
             configuration.windowConfiguration.bounds = STARTING_BOUNDS
         }
+        mockWindowDecoration.mDisplay = mockDisplay
+        `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
     }
 
     @Test
@@ -451,6 +462,72 @@
         })
     }
 
+    fun testDragResize_toDisallowedBounds_freezesAtLimit() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.bottom.toFloat()
+        )
+
+        // Resize the task by 10px to the right and bottom, a valid destination
+        val newBounds = Rect(
+                STARTING_BOUNDS.left,
+                STARTING_BOUNDS.top,
+                STARTING_BOUNDS.right + 10,
+                STARTING_BOUNDS.bottom + 10)
+        taskPositioner.onDragPositioningMove(
+                newBounds.right.toFloat(),
+                newBounds.bottom.toFloat()
+        )
+
+        // Resize the task by another 10px to the right (allowed) and to just in the disallowed
+        // area of the Y coordinate.
+        val newBounds2 = Rect(
+                newBounds.left,
+                newBounds.top,
+                newBounds.right + 10,
+                DISALLOWED_RESIZE_AREA.top
+        )
+        taskPositioner.onDragPositioningMove(
+                newBounds2.right.toFloat(),
+                newBounds2.bottom.toFloat()
+        )
+
+        taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat())
+
+        // The first resize falls in the allowed area, verify there's a change for it.
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder && change.ofBounds(newBounds)
+            }
+        })
+        // The second resize falls in the disallowed area, verify there's no change for it.
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder && change.ofBounds(newBounds2)
+            }
+        })
+        // Instead, there should be a change for its allowed portion (the X movement) with the Y
+        // staying frozen in the last valid resize position.
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder && change.ofBounds(
+                        Rect(
+                                newBounds2.left,
+                                newBounds2.top,
+                                newBounds2.right,
+                                newBounds.bottom // Stayed at the first resize destination.
+                        )
+                )
+            }
+        })
+    }
+
+    private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean {
+        return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) &&
+                bounds == configuration.windowConfiguration.bounds
+    }
+
     companion object {
         private const val TASK_ID = 5
         private const val MIN_WIDTH = 10
@@ -458,6 +535,19 @@
         private const val DENSITY_DPI = 20
         private const val DEFAULT_MIN = 40
         private const val DISPLAY_ID = 1
+        private const val NAVBAR_HEIGHT = 50
+        private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
         private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+        private val DISALLOWED_RESIZE_AREA = Rect(
+                DISPLAY_BOUNDS.left,
+                DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+                DISPLAY_BOUNDS.right,
+                DISPLAY_BOUNDS.bottom)
+        private val STABLE_BOUNDS = Rect(
+                DISPLAY_BOUNDS.left,
+                DISPLAY_BOUNDS.top,
+                DISPLAY_BOUNDS.right,
+                DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
+        )
     }
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4da4c7f..f9b6ce0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -9564,7 +9564,10 @@
      * A stream type is considered independent when the volume changes of that type do not
      * affect any other independent volume control stream type.
      * An independent stream type is its own alias when using {@link #getStreamTypeAlias(int)}.
-     * @return list of independent stream types.
+     * @return list of independent stream types, where each value can be one of
+     *     {@link #STREAM_VOICE_CALL}, {@link #STREAM_SYSTEM}, {@link #STREAM_RING},
+     *     {@link #STREAM_MUSIC}, {@link #STREAM_ALARM}, {@link #STREAM_NOTIFICATION},
+     *     {@link #STREAM_DTMF} and {@link #STREAM_ACCESSIBILITY}.
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index c52cd59..0f8a00a 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -55,7 +55,7 @@
     void onCueingMessageAvailability(boolean available, int seq);
     void onTimeShiftMode(int mode, int seq);
     void onAvailableSpeeds(in float[] speeds, int seq);
-    void onTvMessage(in String type, in Bundle data, int seq);
+    void onTvMessage(int type, in Bundle data, int seq);
 
     void onTuned(in Uri channelUri, int seq);
     // For the recording session
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 113c858..14c020e 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -116,10 +116,10 @@
 
     // For ad request
     void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
-    void notifyAdBuffer(in IBinder sessionToken, in AdBuffer buffer, int userId);
+    void notifyAdBufferReady(in IBinder sessionToken, in AdBuffer buffer, int userId);
 
     // For TV Message
-    void notifyTvMessage(in IBinder sessionToken, in String type, in Bundle data, int userId);
+    void notifyTvMessage(in IBinder sessionToken, int type, in Bundle data, int userId);
 
     // For TV input hardware binding
     List<TvInputHardwareInfo> getHardwareList();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 165a9dd..a7bd8d3 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -75,8 +75,8 @@
 
     // For ad request
     void requestAd(in AdRequest request);
-    void notifyAdBuffer(in AdBuffer buffer);
+    void notifyAdBufferReady(in AdBuffer buffer);
 
     // For TV messages
-    void notifyTvMessage(in String type, in Bundle data);
+    void notifyTvMessage(int type, in Bundle data);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 449c2d6..a52e9a5 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -66,5 +66,5 @@
     void onAdBufferConsumed(in AdBuffer buffer);
 
     // For messages sent from the TV input
-    void onTvMessage(in String type, in Bundle data);
+    void onTvMessage(int type, in Bundle data);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 8389706..7946baee 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -267,7 +267,7 @@
             }
             case DO_SET_TV_MESSAGE_ENABLED: {
                 SomeArgs args = (SomeArgs) msg.obj;
-                mTvInputSessionImpl.setTvMessageEnabled((String) args.arg1, (Boolean) args.arg2);
+                mTvInputSessionImpl.setTvMessageEnabled((Integer) args.arg1, (Boolean) args.arg2);
                 break;
             }
             case DO_REQUEST_AD: {
@@ -275,7 +275,7 @@
                 break;
             }
             case DO_NOTIFY_AD_BUFFER: {
-                mTvInputSessionImpl.notifyAdBuffer((AdBuffer) msg.obj);
+                mTvInputSessionImpl.notifyAdBufferReady((AdBuffer) msg.obj);
                 break;
             }
             case DO_NOTIFY_TV_MESSAGE: {
@@ -465,12 +465,12 @@
     }
 
     @Override
-    public void notifyAdBuffer(AdBuffer buffer) {
+    public void notifyAdBufferReady(AdBuffer buffer) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER, buffer));
     }
 
     @Override
-    public void notifyTvMessage(String type, Bundle data) {
+    public void notifyTvMessage(int type, Bundle data) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
     }
 
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index fb4e99c..c4fc26e 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -64,7 +64,10 @@
      * @param tableUri The URI of the table in the database.
      * @param version The version number of requested table.
      * @param size The Size number of table in bytes.
+     *
+     * @deprecated use {@link Builder} instead.
      */
+    @Deprecated
     public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
             @Nullable Uri tableUri, int version, int size) {
         super(RESPONSE_TYPE, requestId, sequence, responseResult);
@@ -76,30 +79,7 @@
     }
 
     /**
-     * Constructs a TableResponse with a table URI.
-     *
-     * @param requestId The ID is used to associate the response with the request.
-     * @param sequence The sequence number which indicates the order of related responses.
-     * @param responseResult The result for the response. It's one of {@link #RESPONSE_RESULT_OK},
-     *                       {@link #RESPONSE_RESULT_CANCEL}, {@link #RESPONSE_RESULT_ERROR}.
-     * @param tableByteArray The byte array which stores the table in bytes. The structure and
-     *                       syntax of the table depends on the table name in
-     *                       {@link TableRequest#getTableName()} and the corresponding standard.
-     * @param version The version number of requested table.
-     * @param size The Size number of table in bytes.
-     */
-    public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
-            @NonNull byte[] tableByteArray, int version, int size) {
-        super(RESPONSE_TYPE, requestId, sequence, responseResult);
-        mVersion = version;
-        mSize = size;
-        mTableUri = null;
-        mTableByteArray = tableByteArray;
-        mTableSharedMemory = null;
-    }
-
-    /**
-     * Constructs a TableResponse with a table URI.
+     * Constructs a TableResponse.
      *
      * @param requestId The ID is used to associate the response with the request.
      * @param sequence The sequence number which indicates the order of related responses.
@@ -112,17 +92,128 @@
      *                          {@link TableRequest#getTableName()} and the corresponding standard.
      * @param version The version number of requested table.
      * @param size The Size number of table in bytes.
+     * @param tableUri The URI of the table in the database.
+     * @param tableByteArray The byte array which stores the table in bytes. The structure and
+     *                       syntax of the table depends on the table name in
+     * @param tableSharedMemory The shared memory which stores the table. The table size can be
+     *                          large so using a shared memory optimizes the data
+     *                          communication between the table data source and the receiver. The
+     *                          structure syntax of the table depends on the table name in
+     *                          {@link TableRequest#getTableName()} and the corresponding standard.
      */
-    public TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
-            @NonNull SharedMemory tableSharedMemory, int version, int size) {
+    private TableResponse(int requestId, int sequence, @ResponseResult int responseResult,
+            int version, int size, Uri tableUri, byte[] tableByteArray,
+            SharedMemory tableSharedMemory) {
         super(RESPONSE_TYPE, requestId, sequence, responseResult);
         mVersion = version;
         mSize = size;
-        mTableUri = null;
-        mTableByteArray = null;
+        mTableUri = tableUri;
+        mTableByteArray = tableByteArray;
         mTableSharedMemory = tableSharedMemory;
     }
 
+    /**
+     * Builder for {@link TableResponse}.
+     */
+    public static final class Builder {
+        private final int mRequestId;
+        private final int mSequence;
+        @ResponseResult
+        private final int mResponseResult;
+        private final int mVersion;
+        private final int mSize;
+        private Uri mTableUri;
+        private byte[] mTableByteArray;
+        private SharedMemory mTableSharedMemory;
+
+        /**
+         * Constructs a Builder object of {@link TableResponse}.
+         *
+         * @param requestId The ID is used to associate the response with the request.
+         * @param sequence The sequence number which indicates the order of related responses.
+         * @param responseResult The result for the response. It's one of
+         *                       {@link #RESPONSE_RESULT_OK}, {@link #RESPONSE_RESULT_CANCEL},
+         *                       {@link #RESPONSE_RESULT_ERROR}.
+         * @param version The version number of requested table.
+         * @param size The Size number of table in bytes.
+         */
+        public Builder(int requestId, int sequence, @ResponseResult int responseResult, int version,
+                int size) {
+            mRequestId = requestId;
+            mSequence = sequence;
+            mResponseResult = responseResult;
+            mVersion = version;
+            mSize = size;
+        }
+
+        /**
+         * Sets table URI.
+         *
+         * <p>For a single builder instance, at most one of table URI, table byte array, and table
+         * shared memory can be set. If more than one are set, only the last call takes precedence
+         * and others are reset to {@code null}.
+         *
+         * @param uri The URI of the table.
+         */
+        @NonNull
+        public Builder setTableUri(@NonNull Uri uri) {
+            mTableUri = uri;
+            mTableByteArray = null;
+            mTableSharedMemory = null;
+            return this;
+        }
+
+        /**
+         * Sets table byte array.
+         *
+         * <p>For a single builder instance, at most one of table URI, table byte array, and table
+         * shared memory can be set. If more than one are set, only the last call takes precedence
+         * and others are reset to {@code null}.
+         *
+         * @param bytes The byte array which stores the table in bytes. The structure and
+         *              syntax of the table depends on the table name in
+         *              {@link TableRequest#getTableName()} and the corresponding standard.
+         */
+        @NonNull
+        public Builder setTableByteArray(@NonNull byte[] bytes) {
+            mTableByteArray = bytes;
+            mTableUri = null;
+            mTableSharedMemory = null;
+            return this;
+        }
+
+
+        /**
+         * Sets table shared memory.
+         *
+         * <p>For a single builder instance, at most one of table URI, table byte array, and table
+         * shared memory can be set. If more than one are set, only the last call takes precedence
+         * and others are reset to {@code null}.
+         *
+         * @param sharedMemory The shared memory which stores the table. The table size can be
+         *                     large so using a shared memory optimizes the data
+         *                     communication between the table data source and the receiver. The
+         *                     structure syntax of the table depends on the table name in
+         *                     {@link TableRequest#getTableName()} and the corresponding standard.
+         */
+        @NonNull
+        public Builder setTableSharedMemory(@NonNull SharedMemory sharedMemory) {
+            mTableSharedMemory = sharedMemory;
+            mTableUri = null;
+            mTableByteArray = null;
+            return this;
+        }
+
+        /**
+         * Builds a {@link TableResponse} object.
+         */
+        @NonNull
+        public TableResponse build() {
+            return new TableResponse(mRequestId, mSequence, mResponseResult, mVersion, mSize,
+                    mTableUri, mTableByteArray, mTableSharedMemory);
+        }
+    }
+
     TableResponse(Parcel source) {
         super(RESPONSE_TYPE, source);
         String uriString = source.readString();
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 55a753f..7d6a8b4 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -134,14 +133,17 @@
     public @interface VideoUnavailableReason {}
 
     /** Indicates that this TV message contains watermarking data */
-    public static final String TV_MESSAGE_TYPE_WATERMARK = "Watermark";
+    public static final int TV_MESSAGE_TYPE_WATERMARK = 1;
 
     /** Indicates that this TV message contains Closed Captioning data */
-    public static final String TV_MESSAGE_TYPE_CLOSED_CAPTION = "CC";
+    public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2;
+
+    /** Indicates that this TV message contains other data */
+    public static final int TV_MESSAGE_TYPE_OTHER = 1000;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @StringDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION})
+    @IntDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION, TV_MESSAGE_TYPE_OTHER})
     public @interface TvMessageType {}
 
     /**
@@ -802,7 +804,7 @@
          * @param type The type of message received, such as {@link #TV_MESSAGE_TYPE_WATERMARK}
          * @param data The raw data of the message
          */
-        public void onTvMessage(Session session, @TvInputManager.TvMessageType String type,
+        public void onTvMessage(Session session, @TvInputManager.TvMessageType int type,
                 Bundle data) {
         }
 
@@ -1081,7 +1083,7 @@
             });
         }
 
-        void postTvMessage(String type, Bundle data) {
+        void postTvMessage(int type, Bundle data) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -1620,7 +1622,7 @@
             }
 
             @Override
-            public void onTvMessage(String type, Bundle data, int seq) {
+            public void onTvMessage(int type, Bundle data, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
@@ -3230,7 +3232,7 @@
         /**
          * Sends TV messages to the service for testing purposes
          */
-        public void notifyTvMessage(@NonNull @TvMessageType String type, @NonNull Bundle data) {
+        public void notifyTvMessage(@NonNull @TvMessageType int type, @NonNull Bundle data) {
             try {
                 mService.notifyTvMessage(mToken, type, data, mUserId);
             } catch (RemoteException e) {
@@ -3642,13 +3644,13 @@
         /**
          * Notifies when the advertisement buffer is filled and ready to be read.
          */
-        public void notifyAdBuffer(AdBuffer buffer) {
+        public void notifyAdBufferReady(AdBuffer buffer) {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.notifyAdBuffer(mToken, buffer, mUserId);
+                mService.notifyAdBufferReady(mToken, buffer, mUserId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 9f40d70..3c6ed91 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1032,7 +1032,7 @@
          * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
          * @param data The data sent with the message.
          */
-        public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+        public void notifyTvMessage(@TvInputManager.TvMessageType int type,
                 @NonNull Bundle data) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
@@ -1351,7 +1351,7 @@
          *
          * @param buffer The {@link AdBuffer} that became ready for playback.
          */
-        public void onAdBuffer(@NonNull AdBuffer buffer) {
+        public void onAdBufferReady(@NonNull AdBuffer buffer) {
         }
 
         /**
@@ -1486,11 +1486,12 @@
         /**
          * Called when the application enables or disables the detection of the specified message
          * type.
-         * @param type The {@link TvInputManager.TvMessageType} of message that was sent.
+         * @param type The type of message received, such as
+         *             {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
          * @param enabled {@code true} if TV message detection is enabled,
          *                {@code false} otherwise.
          */
-        public void onSetTvMessageEnabled(@NonNull @TvInputManager.TvMessageType String type,
+        public void onSetTvMessageEnabled(@TvInputManager.TvMessageType int type,
                 boolean enabled) {
         }
 
@@ -1856,9 +1857,9 @@
         }
 
         /**
-         * Calls {@link #onSetTvMessageEnabled(String, boolean)}.
+         * Calls {@link #onSetTvMessageEnabled(int, boolean)}.
          */
-        void setTvMessageEnabled(String type, boolean enabled) {
+        void setTvMessageEnabled(int type, boolean enabled) {
             onSetTvMessageEnabled(type, enabled);
         }
 
@@ -2050,8 +2051,8 @@
             onRequestAd(request);
         }
 
-        void notifyAdBuffer(AdBuffer buffer) {
-            onAdBuffer(buffer);
+        void notifyAdBufferReady(AdBuffer buffer) {
+            onAdBufferReady(buffer);
         }
 
         void onTvMessageReceived(String type, Bundle data) {
diff --git a/media/java/android/media/tv/TvRecordingInfo.java b/media/java/android/media/tv/TvRecordingInfo.java
index 60ceb83..59915fc 100644
--- a/media/java/android/media/tv/TvRecordingInfo.java
+++ b/media/java/android/media/tv/TvRecordingInfo.java
@@ -131,7 +131,6 @@
      * cause the recording to start later than the specified time. This should cause the actual
      * duration of the recording to decrease.
      */
-    @NonNull
     public long getStartPaddingMillis() {
         return mStartPaddingMillis;
     }
@@ -144,7 +143,6 @@
      * cause the recording to end earlier than the specified time. This should cause the actual
      * duration of the recording to decrease.
      */
-    @NonNull
     public long getEndPaddingMillis() {
         return mEndPaddingMillis;
     }
@@ -176,7 +174,6 @@
      * https://www.oipf.tv/docs/OIPF-T1-R2_Specification-Volume-5-Declarative-Application-Environment-v2_3-2014-01-24.pdf
      * ">Open IPTV Forum Release 2 Specification</a>. It is described in Volume 5, section 7.10.1.1.
      */
-    @NonNull
     @DaysOfWeek
     public int getRepeatDays() {
         return mRepeatDays;
@@ -228,7 +225,6 @@
      * Returns the scheduled start time of the recording in milliseconds since the epoch.
      */
     @IntRange(from = 0)
-    @NonNull
     public long getScheduledStartTimeMillis() {
         return mScheduledStartTimeMillis;
     }
@@ -237,7 +233,6 @@
      * Returns the scheduled duration of the recording in milliseconds since the epoch.
      */
     @IntRange(from = 0)
-    @NonNull
     public long getScheduledDurationMillis() {
         return mScheduledDurationMillis;
     }
@@ -292,7 +287,6 @@
      * <p> Returns -1 for recordings that have not yet started.
      */
     @IntRange(from = -1)
-    @NonNull
     public long getRecordingStartTimeMillis() {
         return mRecordingStartTimeMillis;
     }
@@ -306,7 +300,6 @@
      * <p> Returns -1 for recordings that have not yet started.
      */
     @IntRange(from = -1)
-    @NonNull
     public long getRecordingDurationMillis() {
         return mRecordingDurationMillis;
     }
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 5aeed1f..19a2e5d 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -648,7 +648,7 @@
      * @hide
      */
     @TestApi
-    public void notifyTvMessage(@TvInputManager.TvMessageType @NonNull String type,
+    public void notifyTvMessage(@TvInputManager.TvMessageType int type,
             @NonNull Bundle data) {
         if (mSession != null) {
             mSession.notifyTvMessage(type, data);
@@ -738,11 +738,12 @@
     /**
      * Enables or disables TV message detection in the stream of the bound TV input.
      *
-     * @param type The type of {@link android.media.tv.TvInputManager.TvMessageType}
+     * @param type The type of message received, such as
+     *             {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
      * @param enabled {@code true} if you want to enable TV message detection
      *                {@code false} otherwise.
      */
-    public void setTvMessageEnabled(@NonNull @TvInputManager.TvMessageType String type,
+    public void setTvMessageEnabled(@TvInputManager.TvMessageType int type,
             boolean enabled) {
     }
 
@@ -1251,11 +1252,12 @@
          * This is called when a new TV Message has been received.
          *
          * @param inputId The ID of the TV input bound to this view.
-         * @param type The type of {@link android.media.tv.TvInputManager.TvMessageType}
+         * @param type The type of message received, such as
+         *             {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
          * @param data The raw data of the message
          */
         public void onTvMessage(@NonNull String inputId,
-                @NonNull @TvInputManager.TvMessageType String type, @NonNull Bundle data) {
+                @TvInputManager.TvMessageType int type, @NonNull Bundle data) {
         }
     }
 
@@ -1670,7 +1672,7 @@
         }
 
         @Override
-        public void onTvMessage(Session session, String type, Bundle data) {
+        public void onTvMessage(Session session, int type, Bundle data) {
             if (DEBUG) {
                 Log.d(TAG, "onTvMessage(type=" + type + ", data=" + data + ")");
             }
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 36954ad..7739184 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -39,7 +39,7 @@
     void onSessionStateChanged(int state, int err, int seq);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
     void onTeletextAppStateChanged(int state, int seq);
-    void onAdBuffer(in AdBuffer buffer, int seq);
+    void onAdBufferReady(in AdBuffer buffer, int seq);
     void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
     void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters, int seq);
     void onSetVideoBounds(in Rect rect, int seq);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index 89847a7..41cbe4a 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -94,7 +94,7 @@
     void notifyRecordingStarted(in IBinder sessionToken, in String recordingId, String requestId,
             int userId);
     void notifyRecordingStopped(in IBinder sessionToken, in String recordingId, int userId);
-    void notifyTvMessage(in IBinder sessionToken, in String type, in Bundle data, int userId);
+    void notifyTvMessage(in IBinder sessionToken, in int type, in Bundle data, int userId);
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
     void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
             int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index f17d1b7..052bc3d 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -72,7 +72,7 @@
     void notifySignalStrength(int strength);
     void notifyRecordingStarted(in String recordingId, in String requestId);
     void notifyRecordingStopped(in String recordingId);
-    void notifyTvMessage(in String type, in Bundle data);
+    void notifyTvMessage(int type, in Bundle data);
     void setSurface(in Surface surface);
     void dispatchSurfaceChanged(int format, int width, int height);
     void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 7db8604..9e43e79 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -38,7 +38,7 @@
     void onSessionStateChanged(int state, int err);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
     void onTeletextAppStateChanged(int state);
-    void onAdBuffer(in AdBuffer buffer);
+    void onAdBufferReady(in AdBuffer buffer);
     void onCommandRequest(in String cmdType, in Bundle parameters);
     void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters);
     void onSetVideoBounds(in Rect rect);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index 6eed483..d3f598a 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -243,7 +243,7 @@
             }
             case DO_NOTIFY_TV_MESSAGE: {
                 SomeArgs args = (SomeArgs) msg.obj;
-                mSessionImpl.notifyTvMessage((String) args.arg1, (Bundle) args.arg2);
+                mSessionImpl.notifyTvMessage((Integer) args.arg1, (Bundle) args.arg2);
                 args.recycle();
                 break;
             }
@@ -520,7 +520,7 @@
     }
 
     @Override
-    public void notifyTvMessage(String type, Bundle data) {
+    public void notifyTvMessage(int type, Bundle data) {
         mCaller.executeOrSendMessage(
                 mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
     }
@@ -558,7 +558,7 @@
     @Override
     public void notifyRecordingStarted(String recordingId, String requestId) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
-                DO_NOTIFY_RECORDING_STARTED, recordingId, recordingId));
+                DO_NOTIFY_RECORDING_STARTED, recordingId, requestId));
     }
 
     @Override
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 3e31bce3..fc8fe5c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -681,14 +681,14 @@
             }
 
             @Override
-            public void onAdBuffer(AdBuffer buffer, int seq) {
+            public void onAdBufferReady(AdBuffer buffer, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
                         Log.e(TAG, "Callback not found for seq " + seq);
                         return;
                     }
-                    record.postAdBuffer(buffer);
+                    record.postAdBufferReady(buffer);
                 }
             }
         };
@@ -1751,7 +1751,7 @@
         /**
          * Notifies Interactive APP session when a new TV message is received.
          */
-        public void notifyTvMessage(String type, Bundle data) {
+        public void notifyTvMessage(int type, Bundle data) {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
@@ -2245,12 +2245,12 @@
             });
         }
 
-        void postAdBuffer(AdBuffer buffer) {
+        void postAdBufferReady(AdBuffer buffer) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     if (mSession.getInputSession() != null) {
-                        mSession.getInputSession().notifyAdBuffer(buffer);
+                        mSession.getInputSession().notifyAdBufferReady(buffer);
                     }
                 }
             });
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 1ae82f4..7dfe16a 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -38,6 +38,7 @@
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvInputManager;
 import android.media.tv.TvRecordingInfo;
@@ -630,6 +631,8 @@
          *                  {@link #requestStartRecording(String, Uri)} is called.
          *                  {@code null} if the recording is not triggered by a
          *                  {@link #requestStartRecording(String, Uri)} request.
+         *                  This ID should be created by the {@link TvInteractiveAppService} and
+         *                  can be any string.
          * @see #onRecordingStopped(String)
          */
         public void onRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
@@ -708,6 +711,8 @@
          * @param requestId The ID of the request when
          *                  {@link #requestScheduleRecording}  is called.
          *                  {@code null} if the recording is not triggered by a request.
+         *                  This ID should be created by the {@link TvInteractiveAppService} and
+         *                  can be any string.
          */
         public void onRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) {
         }
@@ -916,7 +921,7 @@
          * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
          * @param data The raw data of the message
          */
-        public void onTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+        public void onTvMessage(@TvInputManager.TvMessageType int type,
                 @NonNull Bundle data) {
         }
 
@@ -1338,8 +1343,12 @@
          * @param requestId The ID of this request which is used to match the corresponding
          *                  response. The request ID in
          *                  {@link #onRecordingStarted(String, String)} for this request is the
-         *                  same as the ID sent here.
-         * @param programUri The URI for the TV program to record.
+         *                  same as the ID sent here. This should be defined by the
+         *                  {@link TvInteractiveAppService} and can be any string.
+         *                  Should this API be called with the same requestId twice, both 
+         *                  requests should be handled regardless by the TV application.
+         * @param programUri The URI for the TV program to record, built by
+         *            {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
          * @see android.media.tv.TvRecordingClient#startRecording(Uri)
          */
         @CallSuper
@@ -1390,7 +1399,10 @@
          * @param requestId The ID of this request which is used to match the corresponding
          *                  response. The request ID in
          *                  {@link #onRecordingScheduled(String, String)} for this request is the
-         *                  same as the ID sent here.
+         *                  same as the ID sent here. This should be defined by the
+         *                  {@link TvInteractiveAppService} and can be any string.
+         *                  Should this API be called with the same requestId twice, both requests
+         *                  should be handled regardless by the TV application.
          * @param inputId The ID of the TV input for the given channel.
          * @param channelUri The URI of a channel to be recorded.
          * @param programUri The URI of the TV program to be recorded.
@@ -1424,7 +1436,10 @@
          * @param requestId The ID of this request which is used to match the corresponding
          *                  response. The request ID in
          *                  {@link #onRecordingScheduled(String, String)} for this request is the
-         *                  same as the ID sent here.
+         *                  same as the ID sent here. This should be defined by the
+         *                  {@link TvInteractiveAppService} and can be any string. Should this API
+         *                  be called with the same requestId twice, both requests should be handled
+         *                  regardless by the TV application.
          * @param inputId The ID of the TV input for the given channel.
          * @param channelUri The URI of a channel to be recorded.
          * @param startTime The start time of the recording in milliseconds since epoch.
@@ -1507,8 +1522,7 @@
          * @param type The type of recording to retrieve.
          */
         @CallSuper
-        public void requestTvRecordingInfoList(@NonNull @TvRecordingInfo.TvRecordingListType
-                int type) {
+        public void requestTvRecordingInfoList(@TvRecordingInfo.TvRecordingListType int type) {
             executeOrPostRunnableOnMainThread(() -> {
                 try {
                     if (DEBUG) {
@@ -1753,7 +1767,7 @@
             onAdResponse(response);
         }
 
-        void notifyTvMessage(String type, Bundle data) {
+        void notifyTvMessage(int type, Bundle data) {
             if (DEBUG) {
                 Log.d(TAG, "notifyTvMessage (type=" + type + ", data= " + data + ")");
             }
@@ -1942,7 +1956,7 @@
          * @param buffer The {@link AdBuffer} to be received
          */
         @CallSuper
-        public void notifyAdBuffer(@NonNull AdBuffer buffer) {
+        public void notifyAdBufferReady(@NonNull AdBuffer buffer) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -1950,10 +1964,10 @@
                     try {
                         if (DEBUG) {
                             Log.d(TAG,
-                                    "notifyAdBuffer(buffer=" + buffer + ")");
+                                    "notifyAdBufferReady(buffer=" + buffer + ")");
                         }
                         if (mSessionCallback != null) {
-                            mSessionCallback.onAdBuffer(buffer);
+                            mSessionCallback.onAdBufferReady(buffer);
                         }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyAdBuffer", e);
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 0a8de12..1a0319b 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -675,6 +675,8 @@
      * @param requestId The ID of the request when
      *                  {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)}
      *                  is called. {@code null} if the recording is not triggered by a request.
+     *                  This ID should be created by the {@link TvInteractiveAppService} and
+     *                  can be any string.
      * @see TvInteractiveAppView#notifyRecordingStopped(String)
      */
     public void notifyRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
@@ -682,7 +684,7 @@
             Log.d(TAG, "notifyRecordingStarted");
         }
         if (mSession != null) {
-            mSession.notifyRecordingStarted(recordingId, recordingId);
+            mSession.notifyRecordingStarted(recordingId, requestId);
         }
     }
 
@@ -922,6 +924,8 @@
      * @param requestId The ID of the request when
      *                  {@link TvInteractiveAppService.Session#requestScheduleRecording} is called.
      *                  {@code null} if the recording is not triggered by a request.
+     *                  This ID should be created by the {@link TvInteractiveAppService} and
+     *                  can be any string.
      */
     public void notifyRecordingScheduled(
             @NonNull String recordingId, @Nullable String requestId) {
@@ -942,7 +946,7 @@
      * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
      * @param data The raw data of the message
      */
-    public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+    public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType int type,
             @NonNull Bundle data) {
         if (DEBUG) {
             Log.d(TAG, "notifyTvMessage type=" + type
@@ -1214,14 +1218,17 @@
         }
 
         /**
-         * This is called when {@link TvInteractiveAppService.Session#requestStartRecording(Uri)}
-         * is called.
+         * This is called when
+         * {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)} is called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param requestId The ID of this request which is used to match the corresponding
          *                  response. The request ID in
-         *                  {@link #notifyRecordingStarted(String, String)}
-         *                  for this request should be the same as the ID received here.
+         *                  {@link #notifyRecordingStarted(String, String)}  for this request is the
+         *                  same as the ID sent here. This should be defined by the
+         *                  TIAS and can be any string. Should this API be called with the
+         *                  same requestId twice, both requests should be handled regardless
+         *                  by the TV application.
          * @param programUri The URI of the program to record
          *
          */
@@ -1252,8 +1259,11 @@
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param requestId The ID of this request which is used to match the corresponding
          *                  response. The request ID in
-         *                  {@link #notifyRecordingScheduled(String, String)} for this request
-         *                  should be the same as the ID received here.
+         *                  {@link #notifyRecordingScheduled(String, String)} for this request is
+         *                  the same as the ID sent here. This should be defined by the
+         *                  TIAS and can be any string. Should this API be called with the
+         *                  same requestId twice, both requests should be handled regardless
+         *                  by the TV application.
          * @param inputId The ID of the TV input for the given channel.
          * @param channelUri The URI of a channel to be recorded.
          * @param programUri The URI of the TV program to be recorded.
@@ -1276,8 +1286,11 @@
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param requestId The ID of this request which is used to match the corresponding
          *                  response. The request ID in
-         *                  {@link #notifyRecordingScheduled(String, String)} for this request
-         *                  should be the same as the ID received here.
+         *                  {@link #notifyRecordingScheduled(String, String)} for this request is
+         *                  the same as the ID sent here. This should be defined by the
+         *                  TIAS and can be any string. Should this API be called with the
+         *                  same requestId twice, both requests should be handled regardless
+         *                  by the TV application.
          * @param inputId The ID of the TV input for the given channel.
          * @param channelUri The URI of a channel to be recorded.
          * @param startTime The start time of the recording in milliseconds since epoch.
@@ -1350,7 +1363,7 @@
          */
         public void onRequestTvRecordingInfoList(
                 @NonNull String iAppServiceId,
-                @NonNull @TvRecordingInfo.TvRecordingListType int type) {
+                @TvRecordingInfo.TvRecordingListType int type) {
         }
     }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 16827da..6fd579b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -446,6 +446,7 @@
             BodyMediumText(text = stringResource(
                 R.string.use_provider_for_all_description, entryInfo.userProviderDisplayName))
         }
+        item { Divider(thickness = 24.dp, color = Color.Transparent) }
         item {
             CtaButtonRow(
                 leftButton = {
@@ -559,7 +560,11 @@
                     modifier = Modifier.padding(vertical = 16.dp)
                 )
             }
-            item { BodySmallText(text = createOptionInfo.footerDescription) }
+            item {
+                Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+                    BodySmallText(text = createOptionInfo.footerDescription)
+                }
+            }
         }
     }
     onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_CREATION_OPTION_SELECTION)
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index 643af75..2071489 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -24,7 +24,7 @@
     }
 }
 plugins {
-    id 'com.android.application' version '8.0.0-beta01' apply false
-    id 'com.android.library' version '8.0.0-beta01' apply false
+    id 'com.android.application' version '8.0.0-beta03' apply false
+    id 'com.android.library' version '8.0.0-beta03' apply false
     id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
 }
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 53b24b0..c3d5431 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,7 +16,7 @@
 
 #Thu Jul 14 10:36:06 CST 2022
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-2-bin.zip
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index db6cc1a..7f3b0ff 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -16,7 +16,6 @@
 
 package com.android.providers.settings;
 
-import android.annotation.NonNull;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.ArrayMap;
@@ -60,10 +59,6 @@
     // Maximum size of an individual backing store
     static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize();
 
-    // Use an empty string to track the generation number of all non-predefined, unset settings
-    // The generation number is only increased when a new non-predefined setting is inserted
-    private static final String DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS = "";
-
     public GenerationRegistry(Object lock) {
         mLock = lock;
     }
@@ -77,10 +72,6 @@
                 (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
         // Only store the prefix if the mutated setting is a config
         final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name;
-        incrementGenerationInternal(key, indexMapKey);
-    }
-
-    private void incrementGenerationInternal(int key, @NonNull String indexMapKey) {
         synchronized (mLock) {
             final MemoryIntArray backingStore = getBackingStoreLocked(key,
                     /* createIfNotExist= */ false);
@@ -96,8 +87,7 @@
                 final int generation = backingStore.get(index) + 1;
                 backingStore.set(index, generation);
                 if (DEBUG) {
-                    Slog.i(LOG_TAG, "Incremented generation for "
-                            + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
+                    Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey
                             + " key:" + SettingsState.keyToString(key)
                             + " at index:" + index);
                 }
@@ -108,18 +98,6 @@
         }
     }
 
-    // A new, non-predefined setting has been inserted, increment the tracking number for all unset
-    // settings
-    public void incrementGenerationForUnsetSettings(int key) {
-        final boolean isConfig =
-                (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
-        if (isConfig) {
-            // No need to track new settings for configs
-            return;
-        }
-        incrementGenerationInternal(key, DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
-    }
-
     /**
      *  Return the backing store's reference, the index and the current generation number
      *  of a cached setting. If it was not in the backing store, first create the entry in it before
@@ -146,8 +124,8 @@
                 bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
                         backingStore.get(index));
                 if (DEBUG) {
-                    Slog.i(LOG_TAG, "Exported index:" + index + " for "
-                            + (indexMapKey.isEmpty() ? "unset settings" : "setting:" + indexMapKey)
+                    Slog.i(LOG_TAG, "Exported index:" + index
+                            + " for setting:" + indexMapKey
                             + " key:" + SettingsState.keyToString(key));
                 }
             } catch (IOException e) {
@@ -157,10 +135,6 @@
         }
     }
 
-    public void addGenerationDataForUnsetSettings(Bundle bundle, int key) {
-        addGenerationData(bundle, key, /* indexMapKey= */ DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS);
-    }
-
     public void onUserRemoved(int userId) {
         final int secureKey = SettingsState.makeKey(
                 SettingsState.SETTINGS_TYPE_SECURE, userId);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 27c8cdf..832c1b9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1173,7 +1173,7 @@
             Slog.v(LOG_TAG, "setAllConfigSettings for prefix: " + prefix);
         }
 
-        enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+        enforceDeviceConfigWritePermission(getContext(), keyValues.keySet());
         final String callingPackage = resolveCallingPackage();
 
         synchronized (mLock) {
@@ -1192,7 +1192,8 @@
             Slog.v(LOG_TAG, "setSyncDisabledModeConfig(" + syncDisabledMode + ")");
         }
 
-        enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+        enforceHasAtLeastOnePermission(Manifest.permission.WRITE_DEVICE_CONFIG,
+                Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG);
 
         synchronized (mLock) {
             setSyncDisabledModeConfigLocked(syncDisabledMode);
@@ -1204,7 +1205,8 @@
             Slog.v(LOG_TAG, "getSyncDisabledModeConfig");
         }
 
-        enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
+        enforceHasAtLeastOnePermission(Manifest.permission.WRITE_DEVICE_CONFIG,
+                Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG);
 
         synchronized (mLock) {
             return getSyncDisabledModeConfigLocked();
@@ -1289,13 +1291,13 @@
 
     private boolean mutateConfigSetting(String name, String value, String prefix,
             boolean makeDefault, int operation, int mode) {
-        enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
         final String callingPackage = resolveCallingPackage();
 
         // Perform the mutation.
         synchronized (mLock) {
             switch (operation) {
                 case MUTATION_OPERATION_INSERT: {
+                    enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
                             callingPackage, false, null,
@@ -1303,11 +1305,14 @@
                 }
 
                 case MUTATION_OPERATION_DELETE: {
+                    enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name));
                     return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, name, false, null);
                 }
 
                 case MUTATION_OPERATION_RESET: {
+                    enforceDeviceConfigWritePermission(getContext(),
+                            getAllConfigFlags(prefix).keySet());
                     mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix);
                 } return true;
@@ -1464,7 +1469,7 @@
             boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
             int mode, boolean overrideableByRestore) {
         // Make sure the caller can change the settings - treated as secure.
-        enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+        enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
 
         // Resolve the userId on whose behalf the call is made.
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -1752,7 +1757,7 @@
             boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
             int mode, boolean overrideableByRestore) {
         // Make sure the caller can change the settings.
-        enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+        enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
 
         // Resolve the userId on whose behalf the call is made.
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
@@ -2277,11 +2282,57 @@
         }
     }
 
-    private void enforceWritePermission(String permission) {
-        if (getContext().checkCallingOrSelfPermission(permission)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Permission denial: writing to settings requires:"
-                    + permission);
+    private void enforceHasAtLeastOnePermission(String ...permissions) {
+        for (String permission : permissions) {
+            if (getContext().checkCallingOrSelfPermission(permission)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
+        }
+        throw new SecurityException("Permission denial, must have one of: "
+            + Arrays.toString(permissions));
+    }
+
+    /**
+     * Throws an exception if write permissions are not granted for {@code flags}.
+     * <p>
+     * Write permissions are granted if the calling UID is root, or the
+     * WRITE_DEVICE_CONFIG permission is granted, or the WRITE_DEVICE_CONFIG_ALLOWLIST
+     * permission is granted and each flag in {@code flags} is allowlisted in {@code
+     * WRITABLE_FLAG_ALLOWLIST_FLAG}.
+     *
+     * @param context the {@link Context} this is called in
+     * @param flags a list of flags to check, each one of the form 'namespace/flagName'
+     *
+     * @throws SecurityException if the above criteria are not met.
+     * @hide
+     */
+    private void enforceDeviceConfigWritePermission(
+            @NonNull Context context,
+            @NonNull Set<String> flags) {
+        boolean hasAllowlistPermission =
+                context.checkCallingOrSelfPermission(
+                Manifest.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG)
+                == PackageManager.PERMISSION_GRANTED;
+        boolean hasWritePermission =
+                context.checkCallingOrSelfPermission(
+                Manifest.permission.WRITE_DEVICE_CONFIG)
+                == PackageManager.PERMISSION_GRANTED;
+        boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID;
+
+        if (isRoot || hasWritePermission) {
+            return;
+        } else if (hasAllowlistPermission) {
+            for (String flag : flags) {
+                if (!DeviceConfig.getAdbWritableFlags().contains(flag)) {
+                    throw new SecurityException("Permission denial for flag '"
+                        + flag
+                        + "'; allowlist permission granted, but must add flag to the allowlist.");
+                }
+            }
+        } else {
+            throw new SecurityException("Permission denial to mutate flag, must have root, "
+                + "WRITE_DEVICE_CONFIG, or ALLOWLISTED_WRITE_DEVICE_CONFIG");
         }
     }
 
@@ -2327,15 +2378,11 @@
         result.putString(Settings.NameValueTable.VALUE,
                 (setting != null && !setting.isNull()) ? setting.getValue() : null);
 
-        synchronized (mLock) {
-            if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
-                // Individual generation tracking for predefined settings even if they are unset
+        if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) {
+            // Don't track generation for non-existent settings unless the name is predefined
+            synchronized (mLock) {
                 mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
                         SettingsState.makeKey(type, userId), name);
-            } else {
-                // All non-predefined, unset settings are tracked using the same generation number
-                mSettingsRegistry.mGenerationRegistry.addGenerationDataForUnsetSettings(result,
-                        SettingsState.makeKey(type, userId));
             }
         }
         return result;
@@ -2349,8 +2396,7 @@
         } else if (type == SETTINGS_TYPE_SYSTEM) {
             return sAllSystemSettings.contains(name);
         } else {
-            // Consider all config settings predefined because they are used by system apps only
-            return type == SETTINGS_TYPE_CONFIG;
+            return false;
         }
     }
 
@@ -2359,13 +2405,14 @@
         Bundle result = new Bundle();
         result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
         if (trackingGeneration) {
+            // Track generation even if the namespace is empty because this is for system apps
             synchronized (mLock) {
-                // Track generation even if namespace is empty because this is for system apps only
                 mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
-                        SettingsState.makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM),
-                        prefix);
+                        mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG,
+                                UserHandle.USER_SYSTEM).mKey, prefix);
             }
         }
+
         return result;
     }
 
@@ -3056,15 +3103,10 @@
             final int key = makeKey(type, userId);
 
             boolean success = false;
-            boolean wasUnsetNonPredefinedSetting = false;
             SettingsState settingsState = peekSettingsStateLocked(key);
             if (settingsState != null) {
-                if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) {
-                    wasUnsetNonPredefinedSetting = true;
-                }
                 success = settingsState.insertSettingLocked(name, value,
-                        tag, makeDefault, forceNonSystemPackage, packageName,
-                        overrideableByRestore);
+                        tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore);
             }
 
             if (success && criticalSettings != null && criticalSettings.contains(name)) {
@@ -3073,11 +3115,6 @@
 
             if (forceNotify || success) {
                 notifyForSettingsChange(key, name);
-                if (wasUnsetNonPredefinedSetting) {
-                    // Increment the generation number for all non-predefined, unset settings,
-                    // because a new non-predefined setting has been inserted
-                    mGenerationRegistry.incrementGenerationForUnsetSettings(key);
-                }
             }
             if (success) {
                 logSettingChanged(userId, name, type, CHANGE_TYPE_INSERT);
@@ -3709,7 +3746,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 214;
+            private static final int SETTINGS_VERSION = 215;
 
             private final int mUserId;
 
@@ -5664,6 +5701,49 @@
                     currentVersion = 214;
                 }
 
+                if (currentVersion == 214) {
+                    // Version 214: Set a default value for Credential Manager service.
+
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting currentSetting = secureSettings
+                            .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
+                    if (currentSetting.isNull()) {
+                        final int resourceId =
+                            com.android.internal.R.string.config_defaultCredentialProviderService;
+                        final Resources resources = getContext().getResources();
+                        // If the config has not be defined we might get an exception. We also get
+                        // values from both the string array type and the single string in case the
+                        // OEM uses the wrong one.
+                        final List<String> providers = new ArrayList<>();
+                        try {
+                            providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
+                        } catch (Resources.NotFoundException e) {
+                            Slog.w(LOG_TAG,
+                                "Get default array Cred Provider not found: " + e.toString());
+                        }
+                        try {
+                            final String storedValue = resources.getString(resourceId);
+                            if (!TextUtils.isEmpty(storedValue)) {
+                                providers.add(storedValue);
+                            }
+                        } catch (Resources.NotFoundException e) {
+                            Slog.w(LOG_TAG,
+                                "Get default Cred Provider not found: " + e.toString());
+                        }
+
+                        if (!providers.isEmpty()) {
+                            final String defaultValue = String.join(":", providers);
+                            Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as CredMan Service "
+                                    + "for user " + userId);
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
+                                    Settings.Secure.CREDENTIAL_SERVICE, defaultValue, null, true,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+
+                    currentVersion = 215;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4d8705f..c388826 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -759,12 +759,6 @@
         mPackageToMemoryUsage.put(packageName, newSize);
     }
 
-    public boolean hasSetting(String name) {
-        synchronized (mLock) {
-            return hasSettingLocked(name);
-        }
-    }
-
     @GuardedBy("mLock")
     private boolean hasSettingLocked(String name) {
         return mSettings.indexOfKey(name) >= 0;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index 6ec8146..d34fe694 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -151,26 +151,6 @@
         checkBundle(b, 0, 1, false);
     }
 
-    @Test
-    public void testUnsetSettings() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
-        final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
-        final String testSecureSetting = "test_secure_setting";
-        Bundle b = new Bundle();
-        generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
-        checkBundle(b, 0, 1, false);
-        generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
-        checkBundle(b, 1, 1, false);
-        generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
-        // Test that unset settings always have the same index
-        checkBundle(b, 1, 1, false);
-        generationRegistry.incrementGenerationForUnsetSettings(secureKey);
-        // Test that the generation number of the unset settings have increased
-        generationRegistry.addGenerationDataForUnsetSettings(b, secureKey);
-        checkBundle(b, 1, 2, false);
-    }
-
-
     private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull)
             throws IOException {
         final MemoryIntArray array = getArray(b);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d02e569..8cbf5f8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -148,6 +148,8 @@
     <uses-permission android:name="android.permission.LOCATION_BYPASS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" />
     <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6176c61..dc24dce 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -967,6 +967,7 @@
             android:enabled="false"
             android:exported="true"
             android:excludeFromRecents="true"
+            android:resizeableActivity="false"
             android:theme="@android:style/Theme.NoDisplay"
             android:label="@string/note_task_button_label"
             android:icon="@drawable/ic_note_task_shortcut_widget">
@@ -981,6 +982,7 @@
             android:name=".notetask.shortcut.LaunchNoteTaskActivity"
             android:exported="true"
             android:excludeFromRecents="true"
+            android:resizeableActivity="false"
             android:theme="@android:style/Theme.NoDisplay" />
         <!-- endregion -->
 
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
index 92c9527..401ac73 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
@@ -14,8 +14,8 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
+    android:width="32dp"
+    android:height="32dp"
     android:viewportWidth="48"
     android:viewportHeight="48"
     android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
index 209681f..9a850e8 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
@@ -14,8 +14,8 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
+    android:width="32dp"
+    android:height="32dp"
     android:viewportWidth="48"
     android:viewportHeight="48"
     android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
index a3dc816..54322c5 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
@@ -14,8 +14,8 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
+    android:width="32dp"
+    android:height="32dp"
     android:viewportWidth="48"
     android:viewportHeight="48"
     android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/drawable/ic_open_in_full.xml b/packages/SystemUI/res/drawable/ic_open_in_full.xml
index c7f3236..7081c4b 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_full.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_full.xml
@@ -14,8 +14,8 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
+    android:width="24dp"
+    android:height="24dp"
     android:viewportWidth="48"
     android:viewportHeight="48"
     android:tint="?attr/colorControlNormal">
diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
index ae0f8f4..3d0741c 100644
--- a/packages/SystemUI/res/layout/window_magnification_settings_view.xml
+++ b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
@@ -35,7 +35,7 @@
             android:text="@string/accessibility_magnifier_size"
             android:textAppearance="@style/TextAppearance.MagnificationSetting.Title"
             android:focusable="true"
-            android:layout_gravity="center_vertical|left" />
+            android:layout_gravity="center_vertical" />
 
         <Button
             android:id="@+id/magnifier_edit_button"
@@ -45,25 +45,22 @@
             android:text="@string/accessibility_magnifier_edit"
             android:textAppearance="@style/TextAppearance.MagnificationSetting.EditButton"
             android:focusable="true"
-            android:layout_gravity="right" />
+            android:layout_gravity="center_vertical" />
     </LinearLayout>
 
     <LinearLayout
         android:background="@drawable/accessibility_magnification_setting_view_image_btn_layout_bg"
-        android:layout_width="@dimen/magnification_setting_image_button_background_width"
+        android:layout_width="match_parent"
         android:layout_height="@dimen/magnification_setting_image_button_height"
+        android:minWidth="@dimen/magnification_setting_image_button_background_width"
         android:orientation="horizontal">
         <ImageButton
             android:id="@+id/magnifier_small_button"
             android:layout_width="0dp"
             android:layout_height="@dimen/magnification_setting_image_button_height"
             android:layout_weight="1"
-            android:scaleType="fitCenter"
+            android:scaleType="center"
             android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
-            android:paddingLeft="@dimen/magnification_setting_image_button_padding_horizontal"
-            android:paddingRight="@dimen/magnification_setting_image_button_padding_horizontal"
-            android:paddingTop="@dimen/magnification_setting_image_button_padding_vertical"
-            android:paddingBottom="@dimen/magnification_setting_image_button_padding_vertical"
             android:src="@drawable/ic_magnification_menu_small"
             android:tint="@color/accessibility_magnification_image_button_tint"
             android:tintMode="src_atop" />
@@ -73,12 +70,8 @@
             android:layout_width="0dp"
             android:layout_height="@dimen/magnification_setting_image_button_height"
             android:layout_weight="1"
-            android:scaleType="fitCenter"
+            android:scaleType="center"
             android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
-            android:paddingLeft="@dimen/magnification_setting_image_button_padding_horizontal"
-            android:paddingRight="@dimen/magnification_setting_image_button_padding_horizontal"
-            android:paddingTop="@dimen/magnification_setting_image_button_padding_vertical"
-            android:paddingBottom="@dimen/magnification_setting_image_button_padding_vertical"
             android:src="@drawable/ic_magnification_menu_medium"
             android:tint="@color/accessibility_magnification_image_button_tint"
             android:tintMode="src_atop" />
@@ -88,12 +81,8 @@
             android:layout_width="0dp"
             android:layout_height="@dimen/magnification_setting_image_button_height"
             android:layout_weight="1"
-            android:scaleType="fitCenter"
+            android:scaleType="center"
             android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
-            android:paddingLeft="@dimen/magnification_setting_image_button_padding_horizontal"
-            android:paddingRight="@dimen/magnification_setting_image_button_padding_horizontal"
-            android:paddingTop="@dimen/magnification_setting_image_button_padding_vertical"
-            android:paddingBottom="@dimen/magnification_setting_image_button_padding_vertical"
             android:src="@drawable/ic_magnification_menu_large"
             android:tint="@color/accessibility_magnification_image_button_tint"
             android:tintMode="src_atop" />
@@ -103,16 +92,8 @@
             android:layout_width="0dp"
             android:layout_height="@dimen/magnification_setting_image_button_height"
             android:layout_weight="1"
-            android:scaleType="fitCenter"
+            android:scaleType="center"
             android:background="@drawable/accessibility_magnification_setting_view_image_btn_bg"
-            android:paddingLeft
-                ="@dimen/magnification_setting_image_button_open_in_full_padding_horizontal"
-            android:paddingRight
-                ="@dimen/magnification_setting_image_button_open_in_full_padding_horizontal"
-            android:paddingTop
-                ="@dimen/magnification_setting_image_button_open_in_full_padding_vertical"
-            android:paddingBottom
-                ="@dimen/magnification_setting_image_button_open_in_full_padding_vertical"
             android:src="@drawable/ic_open_in_full"
             android:tint="@color/accessibility_magnification_image_button_tint"
             android:tintMode="src_atop" />
@@ -130,7 +111,6 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:singleLine="true"
             android:text="@string/accessibility_allow_diagonal_scrolling"
             android:textAppearance="@style/TextAppearance.MagnificationSetting.Title"
             android:layout_gravity="center_vertical" />
@@ -166,16 +146,11 @@
     <Button
         android:id="@+id/magnifier_done_button"
         android:background="@drawable/accessibility_window_magnification_button_done_bg"
-        android:minHeight="@dimen/magnification_setting_button_done_height"
-        android:layout_width="@dimen/magnification_setting_button_done_width"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/accessibility_magnification_done"
         android:textAppearance="@style/TextAppearance.MagnificationSetting.DoneButton"
         android:focusable="true"
         android:layout_gravity="center_horizontal"
-        android:layout_marginTop="@dimen/magnification_setting_view_margin"
-        android:paddingLeft="@dimen/magnification_setting_button_done_padding_horizontal"
-        android:paddingRight="@dimen/magnification_setting_button_done_padding_horizontal"
-        android:paddingTop="@dimen/magnification_setting_button_done_padding_vertical"
-        android:paddingBottom="@dimen/magnification_setting_button_done_padding_vertical" />
+        android:layout_marginTop="@dimen/magnification_setting_view_margin"/>
 </LinearLayout>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2cd2173..6596ed2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1358,6 +1358,7 @@
         <item name="android:fontFamily">google-sans</item>
         <item name="android:textColor">?androidprv:attr/textColorPrimary</item>
         <item name="android:textSize">@dimen/magnification_setting_text_size</item>
+        <item name="android:singleLine">true</item>
     </style>
 
     <style name="TextAppearance.MagnificationSetting.EditButton">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 8690b36..0c2341f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -33,8 +33,6 @@
      */
     public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
 
-    // Contains the portion of the thumbnail that is unclipped when fullscreen progress = 1.
-    private final RectF mClippedInsets = new RectF();
     private final Matrix mMatrix = new Matrix();
     private boolean mIsOrientationChanged;
     private SplitBounds mSplitBounds;
@@ -70,33 +68,6 @@
         int thumbnailRotation = thumbnailData.rotation;
         int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
         RectF thumbnailClipHint = new RectF();
-
-        float scaledTaskbarSize;
-        float canvasScreenRatio;
-        if (mSplitBounds != null) {
-            if (mSplitBounds.appsStackedVertically) {
-                if (mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
-                    // Top app isn't cropped at all by taskbar
-                    canvasScreenRatio = 0;
-                } else {
-                    // Same as fullscreen ratio
-                    canvasScreenRatio = (float) canvasWidth / screenWidthPx;
-                }
-            } else {
-                // For landscape, scale the width
-                float taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
-                        ? mSplitBounds.leftTaskPercent
-                        : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
-                // Scale landscape width to that of actual screen
-                float fullscreenTaskWidth = screenWidthPx * taskPercent;
-                canvasScreenRatio = canvasWidth / fullscreenTaskWidth;
-            }
-        } else {
-            canvasScreenRatio = (float) canvasWidth / screenWidthPx;
-        }
-        scaledTaskbarSize = taskbarSize * canvasScreenRatio;
-        thumbnailClipHint.bottom = isLargeScreen ? scaledTaskbarSize : 0;
-
         float scale = thumbnailData.scale;
         final float thumbnailScale;
 
@@ -116,10 +87,8 @@
 
             float surfaceWidth = thumbnailBounds.width() / scale;
             float surfaceHeight = thumbnailBounds.height() / scale;
-            float availableWidth = surfaceWidth
-                    - (thumbnailClipHint.left + thumbnailClipHint.right);
-            float availableHeight = surfaceHeight
-                    - (thumbnailClipHint.top + thumbnailClipHint.bottom);
+            float availableWidth = surfaceWidth;
+            float availableHeight = surfaceHeight;
 
             float canvasAspect = canvasWidth / (float) canvasHeight;
             float availableAspect = isRotated
@@ -210,8 +179,6 @@
             setThumbnailRotation(deltaRotate, thumbnailBounds);
         }
 
-        mClippedInsets.set(0, 0, 0, scaledTaskbarSize);
-
         mMatrix.postScale(thumbnailScale, thumbnailScale);
         mIsOrientationChanged = isOrientationDifferent;
     }
@@ -250,8 +217,4 @@
         }
         mMatrix.postTranslate(translateX, translateY);
     }
-
-    public RectF getClippedInsets() {
-        return mClippedInsets;
-    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index dd60647..0d36d04 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -116,6 +116,27 @@
     public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
     // Device dreaming state
     public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
+    // Whether the screen is currently on. Note that the screen is considered on while turning on,
+    // but not while turning off.
+    public static final int SYSUI_STATE_SCREEN_ON = 1 << 28;
+    // Whether the screen is currently transitioning into the state indicated by
+    // SYSUI_STATE_SCREEN_ON.
+    public static final int SYSUI_STATE_SCREEN_TRANSITION = 1 << 29;
+
+    // Mask for SystemUiStateFlags to isolate SYSUI_STATE_SCREEN_ON and
+    // SYSUI_STATE_SCREEN_TRANSITION, to match SCREEN_STATE_*
+    public static final int SYSUI_STATE_SCREEN_STATE_MASK =
+            SYSUI_STATE_SCREEN_ON | SYSUI_STATE_SCREEN_TRANSITION;
+    // Screen is off.
+    public static final int SCREEN_STATE_OFF = 0;
+    // Screen is on.
+    public static final int SCREEN_STATE_ON = SYSUI_STATE_SCREEN_ON;
+    // Screen is still on, but transitioning to turn off.
+    public static final int SCREEN_STATE_TURNING_OFF = SYSUI_STATE_SCREEN_TRANSITION;
+    // Screen was off and is now turning on.
+    public static final int SCREEN_STATE_TURNING_ON =
+            SYSUI_STATE_SCREEN_TRANSITION | SYSUI_STATE_SCREEN_ON;
+
     // Whether the back gesture is allowed (or ignored) by the Shade
     public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
             "persist.wm.debug.shade_allow_back_gesture", false);
@@ -148,7 +169,9 @@
             SYSUI_STATE_IMMERSIVE_MODE,
             SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
             SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
-            SYSUI_STATE_DEVICE_DREAMING
+            SYSUI_STATE_DEVICE_DREAMING,
+            SYSUI_STATE_SCREEN_ON,
+            SYSUI_STATE_SCREEN_TRANSITION,
     })
     public @interface SystemUiStateFlags {}
 
@@ -187,6 +210,9 @@
         str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0
                 ? "freeform_active_in_desktop_mode" : "");
         str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : "");
+        str.add("screen_"
+                + ((flags & SYSUI_STATE_SCREEN_TRANSITION) != 0 ? "turning_" : "")
+                + ((flags & SYSUI_STATE_SCREEN_ON) != 0 ? "on" : "off"));
 
         return str.toString();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 64fe645..aaf6307 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2459,8 +2459,10 @@
         Boolean isFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
                 && mBiometricEnabledForUser.get(userId)
                 && mAuthController.isFaceAuthEnrolled(userId);
+        if (mIsFaceEnrolled != isFaceEnrolled) {
+            mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
+        }
         mIsFaceEnrolled = isFaceEnrolled;
-        mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
     }
 
     public boolean isFaceSupported() {
@@ -3132,13 +3134,14 @@
     @VisibleForTesting
     boolean isUnlockWithFingerprintPossible(int userId) {
         // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
-        boolean fpEnrolled = mFpm != null
+        boolean newFpEnrolled = mFpm != null
                 && !mFingerprintSensorProperties.isEmpty()
                 && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
-        mLogger.logFpEnrolledUpdated(userId,
-                mIsUnlockWithFingerprintPossible.getOrDefault(userId, false),
-                fpEnrolled);
-        mIsUnlockWithFingerprintPossible.put(userId, fpEnrolled);
+        Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
+        if (oldFpEnrolled != newFpEnrolled) {
+            mLogger.logFpEnrolledUpdated(userId, oldFpEnrolled, newFpEnrolled);
+        }
+        mIsUnlockWithFingerprintPossible.put(userId, newFpEnrolled);
         return mIsUnlockWithFingerprintPossible.get(userId);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index bfd99d3..1ae380e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -51,6 +51,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
+import com.android.settingslib.udfps.UdfpsOverlayParams;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.biometrics.AuthController;
@@ -778,7 +779,7 @@
         }
 
         @Override
-        public void onUdfpsLocationChanged() {
+        public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {
             updateUdfpsConfig();
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index d811d30..57c9918 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -403,15 +403,21 @@
         mAnimationController.deleteWindowMagnification(animationCallback);
     }
 
+    void deleteWindowMagnification() {
+        deleteWindowMagnification(/* closeSettingPanel= */ true);
+    }
+
     /**
      * Deletes the magnification window.
      */
-    void deleteWindowMagnification() {
+    void deleteWindowMagnification(boolean closeSettingPanel) {
         if (!isWindowVisible()) {
             return;
         }
 
-        closeMagnificationSettings();
+        if (closeSettingPanel) {
+            closeMagnificationSettings();
+        }
 
         if (mMirrorSurface != null) {
             mTransaction.remove(mMirrorSurface).apply();
@@ -489,7 +495,7 @@
         // Recreate the window again to correct the window appearance due to density or
         // window size changed not caused by rotation.
         if (isWindowVisible() && reCreateWindow) {
-            deleteWindowMagnification();
+            deleteWindowMagnification(/* closeSettingPanel= */ false);
             enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 9ad64e29..d9f5544 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -230,14 +230,6 @@
         }
     };
 
-    private void applyResourcesValuesWithDensityChanged() {
-        if (mIsVisible) {
-            // Reset button to make its window layer always above the mirror window.
-            hideSettingPanel();
-            showSettingPanel(false);
-        }
-    }
-
     private boolean onTouch(View v, MotionEvent event) {
         if (!mIsVisible) {
             return false;
@@ -354,10 +346,6 @@
             }
 
             mWindowManager.addView(mSettingView, mParams);
-            if (resetPosition) {
-                // Request focus on the settings panel when position of the panel is reset.
-                mSettingView.requestFocus();
-            }
 
             // Exclude magnification switch button from system gesture area.
             setSystemGestureExclusion();
@@ -445,7 +433,15 @@
 
     void onConfigurationChanged(int configDiff) {
         if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0
-                || (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
+                || (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0
+                || (configDiff & ActivityInfo.CONFIG_FONT_SCALE) != 0
+                || (configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
+            // We listen to following config changes to trigger layout inflation:
+            // CONFIG_UI_MODE: theme change
+            // CONFIG_ASSETS_PATHS: wallpaper change
+            // CONFIG_FONT_SCALE: font size change
+            // CONFIG_DENSITY: display size change
+
             boolean showSettingPanelAfterThemeChange = mIsVisible;
             hideSettingPanel(/* resetPosition= */ false);
             inflateView();
@@ -454,6 +450,7 @@
             }
             return;
         }
+
         if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
             final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds);
             mDraggableWindowBounds.set(getDraggableWindowBounds());
@@ -465,10 +462,7 @@
                     + mDraggableWindowBounds.top;
             return;
         }
-        if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
-            applyResourcesValuesWithDensityChanged();
-            return;
-        }
+
         if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
             updateAccessibilityWindowTitle();
             return;
@@ -536,7 +530,7 @@
                 LayoutParams.WRAP_CONTENT,
                 LayoutParams.WRAP_CONTENT,
                 LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
-                /* _flags= */ 0,
+                LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSPARENT);
         params.gravity = Gravity.TOP | Gravity.START;
         params.accessibilityTitle = getAccessibilityWindowTitle(context);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 08efd89..c31d45f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -148,6 +148,7 @@
     @NonNull private final WindowManager mWindowManager;
     @NonNull private final DisplayManager mDisplayManager;
     @Nullable private UdfpsController mUdfpsController;
+    @Nullable private UdfpsOverlayParams mUdfpsOverlayParams;
     @Nullable private IUdfpsRefreshRateRequestCallback mUdfpsRefreshRateRequestCallback;
     @Nullable private SideFpsController mSideFpsController;
     @Nullable private UdfpsLogger mUdfpsLogger;
@@ -806,6 +807,8 @@
             final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
 
             final Rect previousUdfpsBounds = mUdfpsBounds;
+            final UdfpsOverlayParams previousUdfpsOverlayParams = mUdfpsOverlayParams;
+
             mUdfpsBounds = udfpsProp.getLocation().getRect();
             mUdfpsBounds.scale(mScaleFactor);
 
@@ -815,7 +818,7 @@
                     mCachedDisplayInfo.getNaturalWidth(), /* right */
                     mCachedDisplayInfo.getNaturalHeight() /* botom */);
 
-            final UdfpsOverlayParams overlayParams = new UdfpsOverlayParams(
+            mUdfpsOverlayParams = new UdfpsOverlayParams(
                     mUdfpsBounds,
                     overlayBounds,
                     mCachedDisplayInfo.getNaturalWidth(),
@@ -823,10 +826,11 @@
                     mScaleFactor,
                     mCachedDisplayInfo.rotation);
 
-            mUdfpsController.updateOverlayParams(udfpsProp, overlayParams);
-            if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) {
+            mUdfpsController.updateOverlayParams(udfpsProp, mUdfpsOverlayParams);
+            if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds) || !Objects.equals(
+                    previousUdfpsOverlayParams, mUdfpsOverlayParams)) {
                 for (Callback cb : mCallbacks) {
-                    cb.onUdfpsLocationChanged();
+                    cb.onUdfpsLocationChanged(mUdfpsOverlayParams);
                 }
             }
         }
@@ -1336,7 +1340,7 @@
          * On devices with UDFPS, this is always called alongside
          * {@link #onFingerprintLocationChanged}.
          */
-        default void onUdfpsLocationChanged() {}
+        default void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {}
 
         /**
          * Called when the location of the face unlock sensor (typically the front facing camera)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 93b57dc..1719ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -28,6 +28,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.keyguard.logging.KeyguardLogger
 import com.android.settingslib.Utils
+import com.android.settingslib.udfps.UdfpsOverlayParams
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.flags.FeatureFlags
@@ -326,7 +327,7 @@
                 updateUdfpsDependentParams()
             }
 
-            override fun onUdfpsLocationChanged() {
+            override fun onUdfpsLocationChanged(udfpsOverlayParams: UdfpsOverlayParams) {
                 updateUdfpsDependentParams()
             }
         }
@@ -397,7 +398,7 @@
     }
 
     companion object {
-        const val RIPPLE_ANIMATION_DURATION: Long = 1533
+        const val RIPPLE_ANIMATION_DURATION: Long = 800
         const val TAG = "AuthRippleController"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 4b32759..8409462 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.surfaceeffects.ripple.RippleShader
 
-private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
+private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
 
 /**
  * Handles two ripple effects: dwell ripple and unlocked ripple
@@ -75,8 +75,8 @@
         }
     private var radius: Float = 0f
         set(value) {
-            rippleShader.rippleSize.setMaxSize(value * 2f, value * 2f)
-            field = value
+            field = value * .9f
+            rippleShader.rippleSize.setMaxSize(field * 2f, field * 2f)
         }
     private var origin: Point = Point()
         set(value) {
@@ -87,8 +87,9 @@
     init {
         rippleShader.color = 0xffffffff.toInt() // default color
         rippleShader.rawProgress = 0f
+        rippleShader.pixelDensity = resources.displayMetrics.density
         rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
-        setupRippleFadeParams()
+        updateRippleFadeParams()
         ripplePaint.shader = rippleShader
 
         dwellShader.color = 0xffffffff.toInt() // default color
@@ -266,7 +267,6 @@
         unlockedRippleAnimator?.cancel()
 
         val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
-            interpolator = Interpolators.LINEAR_OUT_SLOW_IN
             duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
             addUpdateListener { animator ->
                 val now = animator.currentPlayTime
@@ -277,7 +277,7 @@
             }
         }
 
-        val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
+        val alphaInAnimator = ValueAnimator.ofInt(0, 62).apply {
             duration = alphaInDuration
             addUpdateListener { animator ->
                 rippleShader.color = ColorUtils.setAlphaComponent(
@@ -339,15 +339,17 @@
         )
     }
 
-    private fun setupRippleFadeParams() {
+    private fun updateRippleFadeParams() {
         with(rippleShader) {
-            baseRingFadeParams.fadeOutStart = RippleShader.DEFAULT_BASE_RING_FADE_OUT_START
-            baseRingFadeParams.fadeOutEnd = RippleShader.DEFAULT_FADE_OUT_END
+            baseRingFadeParams.fadeInStart = 0f
+            baseRingFadeParams.fadeInEnd = .2f
+            baseRingFadeParams.fadeOutStart = .2f
+            baseRingFadeParams.fadeOutEnd = 1f
 
-            centerFillFadeParams.fadeInStart = RippleShader.DEFAULT_FADE_IN_START
-            centerFillFadeParams.fadeInEnd = RippleShader.DEFAULT_CENTER_FILL_FADE_IN_END
-            centerFillFadeParams.fadeOutStart = RippleShader.DEFAULT_CENTER_FILL_FADE_OUT_START
-            centerFillFadeParams.fadeOutEnd = RippleShader.DEFAULT_CENTER_FILL_FADE_OUT_END
+            centerFillFadeParams.fadeInStart = 0f
+            centerFillFadeParams.fadeInEnd = .15f
+            centerFillFadeParams.fadeOutStart = .15f
+            centerFillFadeParams.fadeOutEnd = .56f
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index f7d87fc..b62c729 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -64,6 +64,7 @@
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.traceSection
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -215,7 +216,9 @@
         requests.add(request)
         mainExecutor.execute {
             if (overlayView == null) {
-                createOverlayForDisplay(reason)
+                traceSection("SideFpsController#show(request=${request.name}, reason=$reason") {
+                    createOverlayForDisplay(reason)
+                }
             } else {
                 Log.v(TAG, "overlay already shown")
             }
@@ -227,7 +230,7 @@
         requests.remove(request)
         mainExecutor.execute {
             if (requests.isEmpty()) {
-                overlayView = null
+                traceSection("SideFpsController#hide(${request.name}") { overlayView = null }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9e83264..bb35355 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -100,6 +100,8 @@
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.SystemClock;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -110,8 +112,6 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import kotlin.Unit;
-
 /**
  * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
  * and toggles the UDFPS display mode.
@@ -598,14 +598,20 @@
                 mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
                 break;
 
+            case UNCHANGED:
+                if (!isWithinSensorArea(mOverlay.getOverlayView(), event.getX(), event.getY(),
+                        true) && mActivePointerId == MotionEvent.INVALID_POINTER_ID
+                        && event.getActionMasked() == MotionEvent.ACTION_DOWN
+                        && mAlternateBouncerInteractor.isVisibleState()) {
+                    // No pointer on sensor, forward to keyguard if alternateBouncer is visible
+                    mKeyguardViewManager.onTouch(event);
+                }
+
             default:
                 break;
         }
         logBiometricTouch(processedTouch.getEvent(), data);
 
-        // We should only consume touches that are within the sensor. By returning "false" for
-        // touches outside of the sensor, we let other UI components consume these events and act on
-        // them appropriately.
         return processedTouch.getTouchData().isWithinSensor(mOverlayParams.getNativeSensorBounds());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
new file mode 100644
index 0000000..92a7094
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.domain.interactor
+
+import android.view.MotionEvent
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.settingslib.udfps.UdfpsOverlayParams
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Encapsulates business logic for interacting with the UDFPS overlay. */
+@SysUISingleton
+class UdfpsOverlayInteractor
+@Inject
+constructor(private val authController: AuthController, @Application scope: CoroutineScope) {
+
+    /** Whether a touch should be intercepted or allowed to pass to the UdfpsOverlay */
+    fun canInterceptTouchInUdfpsBounds(ev: MotionEvent): Boolean {
+        val isUdfpsEnrolled = authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
+        val isWithinUdfpsOverlayBounds =
+            udfpsOverlayParams.value.overlayBounds.contains(ev.rawX.toInt(), ev.rawY.toInt())
+        return !isUdfpsEnrolled || !isWithinUdfpsOverlayBounds
+    }
+
+    /** Returns the current udfpsOverlayParams */
+    val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
+        ConflatedCallbackFlow.conflatedCallbackFlow {
+                val callback =
+                    object : AuthController.Callback {
+                        override fun onUdfpsLocationChanged(
+                            udfpsOverlayParams: UdfpsOverlayParams
+                        ) {
+                            trySendWithFailureLogging(
+                                udfpsOverlayParams,
+                                TAG,
+                                "update udfpsOverlayParams"
+                            )
+                        }
+                    }
+                authController.addCallback(callback)
+                awaitClose { authController.removeCallback(callback) }
+            }
+            .stateIn(scope, started = SharingStarted.Eagerly, initialValue = UdfpsOverlayParams())
+
+    companion object {
+        private const val TAG = "UdfpsOverlayInteractor"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index b9f1666..cf5ccc5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.controls.management
 
+import android.annotation.WorkerThread
 import android.content.ComponentName
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.util.UserAwareController
@@ -33,6 +34,9 @@
      */
     fun getCurrentServices(): List<ControlsServiceInfo>
 
+    @WorkerThread
+    fun forceReload()
+
     /**
      * Get the app label for a given component.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index c81a2c7..8ba060e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -16,8 +16,11 @@
 
 package com.android.systemui.controls.management
 
+import android.annotation.WorkerThread
 import android.content.ComponentName
 import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
 import android.os.UserHandle
 import android.service.controls.ControlsProviderService
 import android.util.Log
@@ -65,7 +68,7 @@
     private val serviceListingBuilder: (Context) -> ServiceListing,
     private val userTracker: UserTracker,
     dumpManager: DumpManager,
-    featureFlags: FeatureFlags
+    private val featureFlags: FeatureFlags
 ) : ControlsListingController, Dumpable {
 
     @Inject
@@ -97,18 +100,7 @@
         // After here, `list` is not captured, so we don't risk modifying it outside of the callback
         backgroundExecutor.execute {
             if (userChangeInProgress.get() > 0) return@execute
-            if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
-                val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
-                newServices.forEach {
-                    it.resolvePanelActivity(allowAllApps) }
-            }
-
-            if (newServices != availableServices) {
-                availableServices = newServices
-                callbacks.forEach {
-                    it.onServicesUpdated(getCurrentServices())
-                }
-            }
+            updateServices(newServices)
         }
     }
 
@@ -120,6 +112,21 @@
         serviceListing.reload()
     }
 
+    private fun updateServices(newServices: List<ControlsServiceInfo>) {
+        if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
+            val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
+            newServices.forEach {
+                it.resolvePanelActivity(allowAllApps) }
+        }
+
+        if (newServices != availableServices) {
+            availableServices = newServices
+            callbacks.forEach {
+                it.onServicesUpdated(getCurrentServices())
+            }
+        }
+    }
+
     override fun changeUser(newUser: UserHandle) {
         userChangeInProgress.incrementAndGet()
         serviceListing.setListening(false)
@@ -178,6 +185,23 @@
     override fun getCurrentServices(): List<ControlsServiceInfo> =
             availableServices.map(ControlsServiceInfo::copy)
 
+    @WorkerThread
+    override fun forceReload() {
+        val packageManager = context.packageManager
+        val intent = Intent(ControlsProviderService.SERVICE_CONTROLS)
+        val user = userTracker.userHandle
+        val flags = PackageManager.GET_SERVICES or
+                PackageManager.GET_META_DATA or
+                PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
+                PackageManager.MATCH_DIRECT_BOOT_AWARE
+        val services = packageManager.queryIntentServicesAsUser(
+                intent,
+                PackageManager.ResolveInfoFlags.of(flags.toLong()),
+                user
+        ).map { ControlsServiceInfo(userTracker.userContext, it.serviceInfo) }
+        updateServices(services)
+    }
+
     /**
      * Get the localized label for the component.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
index 3a4a00c..461cacc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
@@ -19,6 +19,7 @@
 
 import android.content.Context
 import android.os.UserHandle
+import androidx.annotation.WorkerThread
 import com.android.systemui.CoreStartable
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
@@ -75,11 +76,13 @@
             // Controls is disabled, we don't need this anymore
             return
         }
-        startForUser()
+        executor.execute(this::startForUser)
         userTracker.addCallback(userTrackerCallback, executor)
     }
 
+    @WorkerThread
     private fun startForUser() {
+        controlsListingController.forceReload()
         selectDefaultPanelIfNecessary()
         bindToPanel()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index 24e90f0..aad2090 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -61,9 +61,6 @@
     @VisibleForTesting
     boolean mIsAnimationEnabled;
 
-    // Whether dream entry animations are finished.
-    private boolean mEntryAnimationsFinished = false;
-
     @Inject
     protected ComplicationHostViewController(
             @Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout view,
@@ -78,14 +75,6 @@
         mComplicationCollectionViewModel = viewModel;
         mDreamOverlayStateController = dreamOverlayStateController;
 
-        mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
-            @Override
-            public void onStateChanged() {
-                mEntryAnimationsFinished =
-                        mDreamOverlayStateController.areEntryAnimationsFinished();
-            }
-        });
-
         // Whether animations are enabled.
         mIsAnimationEnabled = secureSettings.getFloatForUser(
                 Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, UserHandle.USER_CURRENT) != 0.0f;
@@ -159,7 +148,8 @@
 
                     // Complications to be added before dream entry animations are finished are set
                     // to invisible and are animated in.
-                    if (!mEntryAnimationsFinished && mIsAnimationEnabled) {
+                    if (!mDreamOverlayStateController.areEntryAnimationsFinished()
+                            && mIsAnimationEnabled) {
                         view.setVisibility(View.INVISIBLE);
                     }
                     mComplications.put(id, viewHolder);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b6edcf5..265a9903 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -137,7 +137,7 @@
      * the digits when the clock moves.
      */
     @JvmField
-    val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation", teamfood = true)
+    val STEP_CLOCK_ANIMATION = releasedFlag(212, "step_clock_animation")
 
     /**
      * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
@@ -220,6 +220,11 @@
             "lock_screen_long_press_enabled"
         )
 
+    /** Whether to inflate the bouncer view on a background thread. */
+    // TODO(b/272091103): Tracking Bug
+    @JvmField
+    val ASYNC_INFLATE_BOUNCER = unreleasedFlag(229, "async_inflate_bouncer", teamfood = true)
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -485,6 +490,13 @@
     val ENABLE_PIP_APP_ICON_OVERLAY =
         sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = true)
 
+    // TODO(b/272110828): Tracking bug
+    @Keep
+    @JvmField
+    val ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP =
+        sysPropBooleanFlag(
+            1116, "persist.wm.debug.enable_move_floating_window_in_tabletop", default = false)
+
     // 1200 - predictive back
     @Keep
     @JvmField
@@ -616,14 +628,12 @@
     @JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection")
 
     // 2300 - stylus
+    @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used")
     @JvmField
-    val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used", teamfood = true)
-    @JvmField
-    val ENABLE_STYLUS_CHARGING_UI =
-        unreleasedFlag(2301, "enable_stylus_charging_ui", teamfood = true)
+    val ENABLE_STYLUS_CHARGING_UI = releasedFlag(2301, "enable_stylus_charging_ui")
     @JvmField
     val ENABLE_USI_BATTERY_NOTIFICATIONS =
-        unreleasedFlag(2302, "enable_usi_battery_notifications", teamfood = true)
+        releasedFlag(2302, "enable_usi_battery_notifications")
     @JvmField val ENABLE_STYLUS_EDUCATION = unreleasedFlag(2303, "enable_stylus_education")
 
     // 2400 - performance tools and debugging info
@@ -663,5 +673,5 @@
     // TODO(b/272036292): Tracking Bug
     @JvmField
     val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION =
-            unreleasedFlag(2602, "large_shade_granular_alpha_interpolation")
+            unreleasedFlag(2602, "large_shade_granular_alpha_interpolation", teamfood = true)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 57c4b36..3e52ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -380,10 +380,7 @@
                 // If the launcher is underneath, but we're about to launch an activity, don't do
                 // the animations since they won't be visible.
                 !notificationShadeWindowController.isLaunchingActivity &&
-                launcherUnlockController != null &&
-                // Temporarily disable for foldables since foldable launcher has two first pages,
-                // which breaks the in-window animation.
-                !isFoldable(context)
+                launcherUnlockController != null
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 911861d..28cc697 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -64,7 +64,11 @@
                 .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isAbleToDream, lastStartedTransition) = pair
-                    if (isAbleToDream && lastStartedTransition.to == KeyguardState.LOCKSCREEN) {
+                    if (
+                        isAbleToDream &&
+                            lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
+                            lastStartedTransition.from != KeyguardState.AOD
+                    ) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 name,
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
index 4880f80..b73ddc5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
@@ -59,6 +59,17 @@
         int = value
     }
 
+    /** Updates this to store the same value as [change]. */
+    fun updateTo(change: TableChange) {
+        reset(change.timestamp, change.columnPrefix, change.columnName)
+        when (change.type) {
+            DataType.STRING -> set(change.str)
+            DataType.INT -> set(change.int)
+            DataType.BOOLEAN -> set(change.bool)
+            DataType.EMPTY -> {}
+        }
+    }
+
     /** Returns true if this object has a change. */
     fun hasData(): Boolean {
         return columnName.isNotBlank() && type != DataType.EMPTY
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 29f273a..a0f1c95 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -16,13 +16,13 @@
 
 package com.android.systemui.log.table
 
+import android.os.Trace
 import com.android.systemui.Dumpable
 import com.android.systemui.plugins.util.RingBuffer
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import java.text.SimpleDateFormat
 import java.util.Locale
-import kotlinx.coroutines.flow.Flow
 
 /**
  * A logger that logs changes in table format.
@@ -82,6 +82,19 @@
 
     private val buffer = RingBuffer(maxSize) { TableChange() }
 
+    // Stores the most recently evicted value for each column name (indexed on column name).
+    //
+    // Why it's necessary: Because we use a RingBuffer of a fixed size, it's possible that a column
+    // that's logged infrequently will eventually get pushed out by a different column that's
+    // logged more frequently. Then, that infrequently-logged column isn't present in the RingBuffer
+    // at all and we have no logs that the column ever existed. This is a problem because the
+    // column's information is still relevant, valid, and may be critical to debugging issues.
+    //
+    // Fix: When a change is being evicted from the RingBuffer, we store it in this map (based on
+    // its [TableChange.getName()]. This ensures that we always have at least one value for every
+    // column ever logged. See b/272016422 for more details.
+    private val lastEvictedValues = mutableMapOf<String, TableChange>()
+
     // A [TableRowLogger] object, re-used each time [logDiffs] is called.
     // (Re-used to avoid object allocation.)
     private val tempRow = TableRowLoggerImpl(0, columnPrefix = "", this)
@@ -138,18 +151,24 @@
     // timestamps.)
 
     private fun logChange(timestamp: Long, prefix: String, columnName: String, value: String?) {
+        Trace.beginSection("TableLogBuffer#logChange(string)")
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
+        Trace.endSection()
     }
 
     private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Boolean) {
+        Trace.beginSection("TableLogBuffer#logChange(boolean)")
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
+        Trace.endSection()
     }
 
     private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int?) {
+        Trace.beginSection("TableLogBuffer#logChange(int)")
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
+        Trace.endSection()
     }
 
     // TODO(b/259454430): Add additional change types here.
@@ -158,6 +177,9 @@
     private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
         verifyValidName(prefix, columnName)
         val tableChange = buffer.advance()
+        if (tableChange.hasData()) {
+            saveEvictedValue(tableChange)
+        }
         tableChange.reset(timestamp, prefix, columnName)
         return tableChange
     }
@@ -173,10 +195,23 @@
         }
     }
 
+    private fun saveEvictedValue(change: TableChange) {
+        Trace.beginSection("TableLogBuffer#saveEvictedValue")
+        val name = change.getName()
+        val previouslyEvicted =
+            lastEvictedValues[name] ?: TableChange().also { lastEvictedValues[name] = it }
+        // For recycling purposes, update the existing object in the map with the new information
+        // instead of creating a new object.
+        previouslyEvicted.updateTo(change)
+        Trace.endSection()
+    }
+
     @Synchronized
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println(HEADER_PREFIX + name)
         pw.println("version $VERSION")
+
+        lastEvictedValues.values.sortedBy { it.timestamp }.forEach { it.dump(pw) }
         for (i in 0 until buffer.size) {
             buffer[i].dump(pw)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 1c90154..9bccb7d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
 import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
+import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -61,8 +62,15 @@
         val width = windowMetrics.bounds.width()
         var height = maximumWindowHeight
 
+        // TODO(b/271410803): Read isTransientTaskbar from Launcher
         val isLargeScreen = isLargeScreen(context)
-        if (isLargeScreen) {
+        val isTransientTaskbar =
+            QuickStepContract.isGesturalMode(
+                context.resources.getInteger(
+                    com.android.internal.R.integer.config_navBarInteractionMode
+                )
+            )
+        if (isLargeScreen && !isTransientTaskbar) {
             val taskbarSize =
                 context.resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
             height -= taskbarSize
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index e4351d2..df70f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -839,6 +839,7 @@
         }
         mFrame = null;
         mOrientationHandle = null;
+        notifyNavigationBarSurface();
     }
 
     // TODO: Remove this when we update nav bar recreation
@@ -1003,7 +1004,8 @@
 
     private void notifyNavigationBarSurface() {
         ViewRootImpl viewRoot = mView.getViewRootImpl();
-        SurfaceControl surface = viewRoot != null
+        SurfaceControl surface = mView.getParent() != null 
+                && viewRoot != null
                 && viewRoot.getSurfaceControl() != null
                 && viewRoot.getSurfaceControl().isValid()
                         ? viewRoot.getSurfaceControl()
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 6cd04c8..ac22b7c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -89,22 +89,16 @@
     /**
      * Shows a note task. How the task is shown will depend on when the method is invoked.
      *
-     * If in multi-window mode, notes will open as a full screen experience. That is particularly
-     * important for Large screen devices. These devices may support a taskbar that let users to
-     * drag and drop a shortcut into multi-window mode, and notes should comply with this behaviour.
-     *
      * If the keyguard is locked, notes will open as a full screen experience. A locked device has
      * no contextual information which let us use the whole screen space available.
      *
-     * If not in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
-     * collapsed if the notes bubble is already opened.
+     * If the keyguard is unlocked, notes will open as a bubble OR it will be collapsed if the notes
+     * bubble is already opened.
      *
      * That will let users open other apps in full screen, and take contextual notes.
      */
-    @JvmOverloads
     fun showNoteTask(
         entryPoint: NoteTaskEntryPoint,
-        isInMultiWindowMode: Boolean = false,
     ) {
         if (!isEnabled) return
 
@@ -125,13 +119,7 @@
             return
         }
 
-        val info =
-            resolver.resolveInfo(
-                entryPoint = entryPoint,
-                isInMultiWindowMode = isInMultiWindowMode,
-                isKeyguardLocked = isKeyguardLocked,
-            )
-                ?: return
+        val info = resolver.resolveInfo(entryPoint, isKeyguardLocked) ?: return
 
         infoReference.set(info)
 
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
index 28d7647..2b9f0af 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -20,12 +20,11 @@
     val packageName: String,
     val uid: Int,
     val entryPoint: NoteTaskEntryPoint? = null,
-    val isInMultiWindowMode: Boolean = false,
     val isKeyguardLocked: Boolean = false,
 ) {
 
     val launchMode: NoteTaskLaunchMode =
-        if (isInMultiWindowMode || isKeyguardLocked) {
+        if (isKeyguardLocked) {
             NoteTaskLaunchMode.Activity
         } else {
             NoteTaskLaunchMode.AppBubble
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
index b98a0fd..8ecf081 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -34,7 +34,6 @@
 
     fun resolveInfo(
         entryPoint: NoteTaskEntryPoint? = null,
-        isInMultiWindowMode: Boolean = false,
         isKeyguardLocked: Boolean = false,
     ): NoteTaskInfo? {
         // TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
@@ -48,7 +47,6 @@
             packageName = packageName,
             uid = packageManager.getUidOf(packageName, user),
             entryPoint = entryPoint,
-            isInMultiWindowMode = isInMultiWindowMode,
             isKeyguardLocked = isKeyguardLocked,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 30660c4..8aed995 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.notetask.quickaffordance
 
 import android.content.Context
+import android.hardware.input.InputSettings
 import com.android.systemui.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
@@ -26,36 +27,52 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.notetask.NoteTaskController
 import com.android.systemui.notetask.NoteTaskEnabledKey
 import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.stylus.StylusManager
+import dagger.Lazy
 import javax.inject.Inject
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.trySendBlocking
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
 
 class NoteTaskQuickAffordanceConfig
 @Inject
 constructor(
     context: Context,
-    private val noteTaskController: NoteTaskController,
+    private val controller: NoteTaskController,
+    private val stylusManager: StylusManager,
+    private val lazyRepository: Lazy<KeyguardQuickAffordanceRepository>,
     @NoteTaskEnabledKey private val isEnabled: Boolean,
 ) : KeyguardQuickAffordanceConfig {
 
     override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
 
-    override val pickerName: String = context.getString(R.string.note_task_button_label)
+    private val pickerNameResourceId = R.string.note_task_button_label
+
+    override val pickerName: String = context.getString(pickerNameResourceId)
 
     override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
 
-    override val lockScreenState = flowOf(getLockScreenState())
-
-    // TODO(b/265949213)
-    private fun getLockScreenState() =
-        if (isEnabled) {
-            val icon = Icon.Resource(pickerIconResourceId, ContentDescription.Loaded(pickerName))
-            LockScreenState.Visible(icon)
-        } else {
-            LockScreenState.Hidden
+    // Due to a dependency cycle with KeyguardQuickAffordanceRepository, we need to lazily access
+    // the repository when lockScreenState is accessed for the first time.
+    override val lockScreenState by lazy {
+        val stylusEverUsedFlow = createStylusEverUsedFlow(context, stylusManager)
+        val configSelectedFlow = createConfigSelectedFlow(lazyRepository.get(), key)
+        combine(configSelectedFlow, stylusEverUsedFlow) { isSelected, isStylusEverUsed ->
+            if (isEnabled && (isSelected || isStylusEverUsed)) {
+                val contentDescription = ContentDescription.Resource(pickerNameResourceId)
+                val icon = Icon.Resource(pickerIconResourceId, contentDescription)
+                LockScreenState.Visible(icon)
+            } else {
+                LockScreenState.Hidden
+            }
         }
+    }
 
     override suspend fun getPickerScreenState() =
         if (isEnabled) {
@@ -65,9 +82,27 @@
         }
 
     override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
-        noteTaskController.showNoteTask(
+        controller.showNoteTask(
             entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE,
         )
         return OnTriggeredResult.Handled
     }
 }
+
+private fun createStylusEverUsedFlow(context: Context, stylusManager: StylusManager) =
+    callbackFlow {
+        trySendBlocking(InputSettings.isStylusEverUsed(context))
+        val callback =
+            object : StylusManager.StylusCallback {
+                override fun onStylusFirstUsed() {
+                    trySendBlocking(InputSettings.isStylusEverUsed(context))
+                }
+            }
+        stylusManager.registerCallback(callback)
+        awaitClose { stylusManager.unregisterCallback(callback) }
+    }
+
+private fun createConfigSelectedFlow(repository: KeyguardQuickAffordanceRepository, key: String) =
+    repository.selections.map { selected ->
+        selected.values.flatten().any { selectedConfig -> selectedConfig.key == key }
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index 80fce6a..14b0779 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -28,17 +28,12 @@
 class LaunchNoteTaskActivity
 @Inject
 constructor(
-    private val noteTaskController: NoteTaskController,
+    private val controller: NoteTaskController,
 ) : ComponentActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
-        noteTaskController.showNoteTask(
-            entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
-            isInMultiWindowMode = isInMultiWindowMode,
-        )
-
+        controller.showNoteTask(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT)
         finish()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 019ca52..8a3ecc6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -31,6 +31,8 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_TRANSITION;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -121,7 +123,8 @@
 public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
         NavigationModeController.ModeChangedListener, Dumpable {
 
-    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
+    @VisibleForTesting
+    static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
 
     public static final String TAG_OPS = "OverviewProxyService";
     private static final long BACKOFF_MILLIS = 1000;
@@ -548,6 +551,7 @@
         mUiEventLogger = uiEventLogger;
         mDisplayTracker = displayTracker;
         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
+        mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
 
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
 
@@ -596,7 +600,6 @@
         // Connect to the service
         updateEnabledState();
         startConnectionToCurrentUser();
-        mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
 
         // Listen for assistant changes
         assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
@@ -726,10 +729,8 @@
             return;
         }
         mHandler.removeCallbacks(mConnectionRunnable);
-        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
-                .setPackage(mRecentsComponentName.getPackageName());
         try {
-            mBound = mContext.bindServiceAsUser(launcherServiceIntent,
+            mBound = mContext.bindServiceAsUser(mQuickStepIntent,
                     mOverviewServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                     UserHandle.of(mUserTracker.getUserId()));
@@ -862,6 +863,11 @@
          */
         @Override
         public void onScreenTurnedOn() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, true)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
+                .commitUpdate(mContext.getDisplayId());
+
             try {
                 if (mOverviewProxy != null) {
                     mOverviewProxy.onScreenTurnedOn();
@@ -874,10 +880,26 @@
         }
 
         /**
+         * Notifies the Launcher that screen turned off.
+         */
+        @Override
+        public void onScreenTurnedOff() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, false)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
+                .commitUpdate(mContext.getDisplayId());
+        }
+
+        /**
          * Notifies the Launcher that screen is starting to turn on.
          */
         @Override
         public void onScreenTurningOff() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, false)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
+                .commitUpdate(mContext.getDisplayId());
+
             try {
                 if (mOverviewProxy != null) {
                     mOverviewProxy.onScreenTurningOff();
@@ -894,6 +916,11 @@
          */
         @Override
         public void onScreenTurningOn() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, true)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
+                .commitUpdate(mContext.getDisplayId());
+
             try {
                 if (mOverviewProxy != null) {
                     mOverviewProxy.onScreenTurningOn();
@@ -1005,4 +1032,21 @@
         default void onAssistantGestureCompletion(float velocity) {}
         default void startAssistant(Bundle bundle) {}
     }
+
+    /**
+     * Shuts down this service at the end of a testcase.
+     * <p>
+     * The in-production service is never shuts down, and it was not designed with testing in mind.
+     * This unregisters the mechanisms by which the service will be revived after a testcase.
+     * <p>
+     * NOTE: This is a stop-gap introduced when first added some tests to this class. It should
+     * probably be replaced by proper lifecycle management on this class.
+     */
+    @VisibleForTesting()
+    void shutdownForTest() {
+        mContext.unregisterReceiver(mLauncherStateChangedReceiver);
+        mIsEnabled = false;
+        mHandler.removeCallbacks(mConnectionRunnable);
+        disconnectFromLauncherService();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index c130b39..c09524b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -36,6 +36,7 @@
 import com.android.keyguard.LockIconViewController;
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.R;
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -86,7 +87,7 @@
     private final PulsingGestureListener mPulsingGestureListener;
     private final NotificationInsetsController mNotificationInsetsController;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
-
+    private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
     private GestureDetector mPulsingWakeupGestureHandler;
     private View mBrightnessMirror;
     private boolean mTouchActive;
@@ -134,6 +135,7 @@
             KeyguardBouncerViewModel keyguardBouncerViewModel,
             KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
             AlternateBouncerInteractor alternateBouncerInteractor,
+            UdfpsOverlayInteractor udfpsOverlayInteractor,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
             PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
     ) {
@@ -156,6 +158,7 @@
         mPulsingGestureListener = pulsingGestureListener;
         mNotificationInsetsController = notificationInsetsController;
         mAlternateBouncerInteractor = alternateBouncerInteractor;
+        mUdfpsOverlayInteractor = udfpsOverlayInteractor;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -240,7 +243,6 @@
 
                 mFalsingCollector.onTouchEvent(ev);
                 mPulsingWakeupGestureHandler.onTouchEvent(ev);
-                mStatusBarKeyguardViewManager.onTouch(ev);
                 if (mBrightnessMirror != null
                         && mBrightnessMirror.getVisibility() == View.VISIBLE) {
                     // Disallow new pointers while the brightness mirror is visible. This is so that
@@ -316,8 +318,8 @@
                 }
 
                 if (mAlternateBouncerInteractor.isVisibleState()) {
-                    // capture all touches if the alt auth bouncer is showing
-                    return true;
+                    // If using UDFPS, don't intercept touches that are within its overlay bounds
+                    return mUdfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(ev);
                 }
 
                 if (mLockIconViewController.onInterceptTouchEvent(ev)) {
@@ -355,6 +357,7 @@
 
                 if (mAlternateBouncerInteractor.isVisibleState()) {
                     // eat the touch
+                    mStatusBarKeyguardViewManager.onTouch(ev);
                     handled = true;
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 6857f4c..df8ae50 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -1072,7 +1072,11 @@
     private void setClippingBounds() {
         float qsExpansionFraction = computeExpansionFraction();
         final int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
-        final boolean qsVisible = (qsExpansionFraction > 0 || qsPanelBottomY > 0);
+        // Split shade has no QQS
+        final boolean qqsVisible =
+                !mSplitShadeEnabled && qsExpansionFraction == 0 && qsPanelBottomY > 0;
+        final boolean qsVisible = qsExpansionFraction > 0;
+        final boolean qsOrQqsVisible = qqsVisible || qsVisible;
         checkCorrectScrimVisibility(qsExpansionFraction);
 
         int top = calculateTopClippingBound(qsPanelBottomY);
@@ -1081,7 +1085,7 @@
         int right = calculateRightClippingBound();
         // top should never be lower than bottom, otherwise it will be invisible.
         top = Math.min(top, bottom);
-        applyClippingBounds(left, top, right, bottom, qsVisible);
+        applyClippingBounds(left, top, right, bottom, qsOrQqsVisible);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 664d61a..00519b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3623,7 +3623,8 @@
         boolean goingToSleepWithoutAnimation = isGoingToSleep()
                 && !mDozeParameters.shouldControlScreenOff();
         boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
-                || goingToSleepWithoutAnimation;
+                || goingToSleepWithoutAnimation
+                || mDeviceProvisionedController.isFrpActive();
         mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
         mNotificationIconAreaController.setAnimationsEnabled(!disabled);
     }
@@ -3797,10 +3798,10 @@
 
         boolean launchingAffordanceWithPreview = mLaunchingAffordance;
         mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
-
         if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
-                    || mTransitionToFullShadeProgress > 0f) {
+            if ((!isOccluded() || isPanelExpanded())
+                    && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                    || mTransitionToFullShadeProgress > 0f)) {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
             } else {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 66f5b65..aa71b51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -733,7 +733,9 @@
             } else {
                 showBouncerOrKeyguard(hideBouncerWhenShowing);
             }
-            hideAlternateBouncer(false);
+            if (hideBouncerWhenShowing) {
+                hideAlternateBouncer(false);
+            }
             mKeyguardUpdateManager.sendKeyguardReset();
             updateStates();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
index 3944c8c..0d09fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -21,7 +21,8 @@
 /**
  * Controller to cache in process the state of the device provisioning.
  * <p>
- * This controller keeps track of the values of device provisioning and user setup complete
+ * This controller keeps track of the values of device provisioning, user setup complete, and
+ * whether Factory Reset Protection is active.
  */
 public interface DeviceProvisionedController extends CallbackController<DeviceProvisionedListener> {
 
@@ -49,6 +50,9 @@
      */
     boolean isCurrentUserSetup();
 
+    /** Returns true when Factory Reset Protection is locking the device. */
+    boolean isFrpActive();
+
     /**
      * Interface to provide calls when the values tracked change
      */
@@ -69,5 +73,10 @@
          * Call when some user changes from not provisioned to provisioned
          */
         default void onUserSetupChanged() { }
+
+        /**
+         * Called when the state of FRP changes.
+         */
+        default void onFrpActiveChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index a6b7d9c5..32c64f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -60,9 +60,11 @@
     }
 
     private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
+    private val frpActiveUri = secureSettings.getUriFor(Settings.Secure.SECURE_FRP_MODE)
     private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
 
     private val deviceProvisioned = AtomicBoolean(false)
+    private val frpActive = AtomicBoolean(false)
     @GuardedBy("lock")
     private val userSetupComplete = SparseBooleanArray()
     @GuardedBy("lock")
@@ -89,11 +91,15 @@
             userId: Int
         ) {
             val updateDeviceProvisioned = deviceProvisionedUri in uris
+            val updateFrp = frpActiveUri in uris
             val updateUser = if (userSetupUri in uris) userId else NO_USERS
-            updateValues(updateDeviceProvisioned, updateUser)
+            updateValues(updateDeviceProvisioned, updateFrp, updateUser)
             if (updateDeviceProvisioned) {
                 onDeviceProvisionedChanged()
             }
+            if (updateFrp) {
+                onFrpActiveChanged()
+            }
             if (updateUser != NO_USERS) {
                 onUserSetupChanged()
             }
@@ -103,7 +109,7 @@
     private val userChangedCallback = object : UserTracker.Callback {
         @WorkerThread
         override fun onUserChanged(newUser: Int, userContext: Context) {
-            updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+            updateValues(updateDeviceProvisioned = false, updateFrp = false, updateUser = newUser)
             onUserSwitched()
         }
 
@@ -125,19 +131,27 @@
         updateValues()
         userTracker.addCallback(userChangedCallback, backgroundExecutor)
         globalSettings.registerContentObserver(deviceProvisionedUri, observer)
+        globalSettings.registerContentObserver(frpActiveUri, observer)
         secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
     }
 
     @WorkerThread
-    private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
+    private fun updateValues(
+        updateDeviceProvisioned: Boolean = true,
+        updateFrp: Boolean = true,
+        updateUser: Int = ALL_USERS
+    ) {
         if (updateDeviceProvisioned) {
             deviceProvisioned
                     .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
         }
+        if (updateFrp) {
+            frpActive.set(globalSettings.getInt(Settings.Secure.SECURE_FRP_MODE, 0) != 0)
+        }
         synchronized(lock) {
             if (updateUser == ALL_USERS) {
-                val N = userSetupComplete.size()
-                for (i in 0 until N) {
+                val n = userSetupComplete.size()
+                for (i in 0 until n) {
                     val user = userSetupComplete.keyAt(i)
                     val value = secureSettings
                             .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
@@ -172,6 +186,10 @@
         return deviceProvisioned.get()
     }
 
+    override fun isFrpActive(): Boolean {
+        return frpActive.get()
+    }
+
     override fun isUserSetup(user: Int): Boolean {
         val index = synchronized(lock) {
             userSetupComplete.indexOfKey(user)
@@ -196,7 +214,13 @@
 
     override fun onDeviceProvisionedChanged() {
         dispatchChange(
-                DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+            DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+        )
+    }
+
+    override fun onFrpActiveChanged() {
+        dispatchChange(
+            DeviceProvisionedController.DeviceProvisionedListener::onFrpActiveChanged
         )
     }
 
@@ -221,6 +245,7 @@
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println("Device provisioned: ${deviceProvisioned.get()}")
+        pw.println("Factory Reset Protection active: ${frpActive.get()}")
         synchronized(lock) {
             pw.println("User setup complete: $userSetupComplete")
             pw.println("Listeners: $listeners")
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index 064c224..c6da55c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -119,11 +119,11 @@
 
     /**
      * Default constructor for {@link ObservableServiceConnection}.
-     * @param context The context from which the service will be bound with.
+     * @param context       The context from which the service will be bound with.
      * @param serviceIntent The intent to  bind service with.
-     * @param executor The executor for connection callbacks to be delivered on
-     * @param transformer A {@link ServiceTransformer} for transforming the resulting service
-     *                    into a desired type.
+     * @param executor      The executor for connection callbacks to be delivered on
+     * @param transformer   A {@link ServiceTransformer} for transforming the resulting service
+     *                      into a desired type.
      */
     @Inject
     public ObservableServiceConnection(Context context, Intent serviceIntent,
@@ -143,7 +143,13 @@
      * @return {@code true} if initiating binding succeed, {@code false} otherwise.
      */
     public boolean bind() {
-        final boolean bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+        boolean bindResult = false;
+        try {
+            bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+        } catch (SecurityException e) {
+            Log.d(TAG, "Could not bind to service", e);
+            mContext.unbindService(this);
+        }
         mBoundCalled = true;
         if (DEBUG) {
             Log.d(TAG, "bind. bound:" + bindResult);
@@ -197,7 +203,7 @@
             Log.d(TAG, "removeCallback:" + callback);
         }
 
-        mExecutor.execute(()-> mCallbacks.removeIf(el -> el.get() == callback));
+        mExecutor.execute(() -> mCallbacks.removeIf(el-> el.get() == callback));
     }
 
     private void onDisconnected(@DisconnectReason int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 95cc12a..3c7d092 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1416,7 +1416,11 @@
             @Override
             public void onAnimationCancel(@NonNull Animator animation) {
                 mInteractionJankMonitor.cancel(CUJ_VOLUME_CONTROL);
-                Log.d(TAG, "onAnimationCancel");
+                Log.i(TAG, "onAnimationCancel");
+
+                // We can only have one animation listener for cancel, so the jank listener should
+                // also call for cleanup.
+                finishDismiss();
             }
 
             @Override
@@ -1525,12 +1529,7 @@
                 .setDuration(mDialogHideAnimationDurationMs)
                 .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
                 .withEndAction(() -> mHandler.postDelayed(() -> {
-                    mController.notifyVisible(false);
-                    mDialog.dismiss();
-                    tryToRemoveCaptionsTooltip();
-                    mIsAnimatingDismiss = false;
-
-                    hideRingerDrawer();
+                    finishDismiss();
                 }, 50));
         if (!shouldSlideInVolumeTray()) {
             animator.translationX(
@@ -1548,6 +1547,18 @@
         Trace.endSection();
     }
 
+    /**
+     * Clean up and hide volume dialog. Called when animation is finished/cancelled.
+     */
+    private void finishDismiss() {
+        mController.notifyVisible(false);
+        mDialog.dismiss();
+        tryToRemoveCaptionsTooltip();
+        mIsAnimatingDismiss = false;
+
+        hideRingerDrawer();
+    }
+
     private boolean showActiveStreamOnly() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
                 || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index f370ad6..33f0ae5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -36,6 +36,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.udfps.UdfpsOverlayParams;
 import com.android.systemui.doze.util.BurnInHelperKt;
 
 import org.junit.Test;
@@ -107,7 +108,7 @@
         Pair<Float, Point> udfps = setupUdfps();
 
         // WHEN udfps location changes
-        mAuthControllerCallback.onUdfpsLocationChanged();
+        mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams());
         mDelayableExecutor.runAllReady();
 
         // THEN lock icon view location is updated with the same coordinates as auth controller vals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 52a70ee..47c9191 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -201,13 +201,6 @@
         assertThat(magnifierMediumButton.isSelected()).isTrue();
     }
 
-    @Test
-    public void showSettingPanel_focusOnThePanel() {
-        mWindowMagnificationSettings.showSettingPanel();
-
-        assertThat(mSettingView.isFocused()).isTrue();
-    }
-
     private <T extends View> T getInternalView(@IdRes int idRes) {
         T view = mSettingView.findViewById(idRes);
         assertNotNull(view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 05266f1..9a73898 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1224,6 +1224,44 @@
     }
 
     @Test
+    public void onTouch_WithNewTouchDetection_forwardToKeyguard() throws RemoteException {
+        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+                0L);
+        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
+                InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData);
+
+        // Enable new touch detection.
+        when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+        // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+        initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+        // Configure UdfpsView to accept the ACTION_DOWN event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
+
+        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+        // WHEN ACTION_DOWN is received
+        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+                processorResultDown);
+        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+        mBiometricExecutor.runAllReady();
+
+        // THEN the touch is forwarded to Keyguard
+        verify(mStatusBarKeyguardViewManager).onTouch(downEvent);
+        downEvent.recycle();
+    }
+
+    @Test
     public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
         // GIVEN UDFPS overlay is showing
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
new file mode 100644
index 0000000..87d5ae6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.domain.interactor
+
+import android.graphics.Rect
+import android.test.suitebuilder.annotation.SmallTest
+import android.view.MotionEvent
+import android.view.Surface
+import com.android.settingslib.udfps.UdfpsOverlayParams
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UdfpsOverlayInteractorTest : SysuiTestCase() {
+
+    @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+    private lateinit var testScope: TestScope
+
+    @Mock private lateinit var authController: AuthController
+    @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
+
+    @Mock private lateinit var udfpsOverlayParams: UdfpsOverlayParams
+    @Mock private lateinit var overlayBounds: Rect
+
+    private lateinit var underTest: UdfpsOverlayInteractor
+
+    @Before
+    fun setUp() {
+        testScope = TestScope(StandardTestDispatcher())
+    }
+
+    @Test
+    fun testShouldInterceptTouch() =
+        testScope.runTest {
+            createUdpfsOverlayInteractor()
+
+            // When fingerprint enrolled and touch is within bounds
+            verify(authController).addCallback(authControllerCallback.capture())
+            authControllerCallback.value.onUdfpsLocationChanged(udfpsOverlayParams)
+            whenever(authController.isUdfpsEnrolled(anyInt())).thenReturn(true)
+            whenever(udfpsOverlayParams.overlayBounds).thenReturn(overlayBounds)
+            whenever(overlayBounds.contains(downEv.x.toInt(), downEv.y.toInt())).thenReturn(true)
+
+            runCurrent()
+
+            // Then touch should not be intercepted
+            val canInterceptTrue = underTest.canInterceptTouchInUdfpsBounds(downEv)
+            assertThat(canInterceptTrue).isFalse()
+
+            // When touch is outside of bounds
+            whenever(overlayBounds.contains(downEv.x.toInt(), downEv.y.toInt())).thenReturn(false)
+
+            // Then touch should be intercepted
+            val canInterceptFalse = underTest.canInterceptTouchInUdfpsBounds(downEv)
+            assertThat(canInterceptFalse).isTrue()
+        }
+
+    @Test
+    fun testUdfpsOverlayParamsChange() =
+        testScope.runTest {
+            createUdpfsOverlayInteractor()
+            val udfpsOverlayParams = collectLastValue(underTest.udfpsOverlayParams)
+            runCurrent()
+
+            verify(authController).addCallback(authControllerCallback.capture())
+
+            // When udfpsLocationChanges in authcontroller
+            authControllerCallback.value.onUdfpsLocationChanged(firstParams)
+
+            // Then the value in the interactor should be updated
+            assertThat(udfpsOverlayParams()).isEqualTo(firstParams)
+        }
+
+    private fun createUdpfsOverlayInteractor() {
+        underTest = UdfpsOverlayInteractor(authController, testScope.backgroundScope)
+        testScope.runCurrent()
+    }
+}
+
+private val firstParams =
+    UdfpsOverlayParams(Rect(0, 0, 10, 10), Rect(0, 0, 10, 10), 1, 1, 1f, Surface.ROTATION_0)
+private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 35cd3d2..10bfc1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -45,6 +45,8 @@
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
@@ -62,7 +64,6 @@
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -106,6 +107,7 @@
         MockitoAnnotations.initMocks(this)
 
         `when`(userTracker.userId).thenReturn(user)
+        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
         `when`(userTracker.userContext).thenReturn(context)
         // Return disabled by default
         `when`(packageManager.getComponentEnabledSetting(any()))
@@ -564,6 +566,79 @@
         assertTrue(controller.getCurrentServices().isEmpty())
     }
 
+    @Test
+    fun testForceReloadQueriesPackageManager() {
+        val user = 10
+        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
+
+        controller.forceReload()
+        verify(packageManager).queryIntentServicesAsUser(
+                argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+                argThat(FlagsMatcher(
+                        PackageManager.GET_META_DATA.toLong() or
+                                PackageManager.GET_SERVICES.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+                )),
+                eq(UserHandle.of(user))
+        )
+    }
+
+    @Test
+    fun testForceReloadUpdatesList() {
+        val resolveInfo = ResolveInfo()
+        resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+        `when`(packageManager.queryIntentServicesAsUser(
+                argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+                argThat(FlagsMatcher(
+                        PackageManager.GET_META_DATA.toLong() or
+                                PackageManager.GET_SERVICES.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+                )),
+                any<UserHandle>()
+        )).thenReturn(listOf(resolveInfo))
+
+        controller.forceReload()
+
+        val services = controller.getCurrentServices()
+        assertThat(services.size).isEqualTo(1)
+        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+    }
+
+    @Test
+    fun testForceReloadCallsListeners() {
+        controller.addCallback(mockCallback)
+        executor.runAllReady()
+
+        @Suppress("unchecked_cast")
+        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
+                ArgumentCaptor.forClass(List::class.java)
+                        as ArgumentCaptor<List<ControlsServiceInfo>>
+
+        val resolveInfo = ResolveInfo()
+        resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+        `when`(packageManager.queryIntentServicesAsUser(
+                any(),
+                any<PackageManager.ResolveInfoFlags>(),
+                any<UserHandle>()
+        )).thenReturn(listOf(resolveInfo))
+
+        reset(mockCallback)
+        controller.forceReload()
+
+        verify(mockCallback).onServicesUpdated(capture(captor))
+
+        val services = captor.value
+
+        assertThat(services.size).isEqualTo(1)
+        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+    }
+
+
+
     private fun ServiceInfo(
             componentName: ComponentName,
             panelActivityComponentName: ComponentName? = null
@@ -600,7 +675,7 @@
     private fun setUpQueryResult(infos: List<ActivityInfo>) {
         `when`(
                 packageManager.queryIntentActivitiesAsUser(
-                        argThat(IntentMatcher(activityName)),
+                        argThat(IntentMatcherComponent(activityName)),
                         argThat(FlagsMatcher(FLAGS)),
                         eq(UserHandle.of(user))
                 )
@@ -609,7 +684,7 @@
         })
     }
 
-    private class IntentMatcher(
+    private class IntentMatcherComponent(
             private val componentName: ComponentName
     ) : ArgumentMatcher<Intent> {
         override fun matches(argument: Intent?): Boolean {
@@ -617,6 +692,14 @@
         }
     }
 
+    private class IntentMatcherAction(
+            private val action: String
+    ) : ArgumentMatcher<Intent> {
+        override fun matches(argument: Intent?): Boolean {
+            return argument?.action == action
+        }
+    }
+
     private class FlagsMatcher(
             private val flags: Long
     ) : ArgumentMatcher<PackageManager.ResolveInfoFlags> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index 9d8084d..bd7e98e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -42,6 +42,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
@@ -80,9 +82,10 @@
     fun testNoPreferredPackagesNoDefaultSelected_noNewSelection() {
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -92,9 +95,10 @@
         whenever(authorizedPanelsRepository.getPreferredPackages())
             .thenReturn(setOf(TEST_PACKAGE_PANEL))
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
-        `when`(controlsListingController.getCurrentServices()).thenReturn(emptyList())
+        setUpControlsListingControls(emptyList())
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -105,9 +109,10 @@
             .thenReturn(setOf(TEST_PACKAGE_PANEL))
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "not panel", hasPanel = false))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -119,9 +124,10 @@
         `when`(controlsController.getPreferredSelection())
             .thenReturn(mock<SelectedItem.PanelItem>())
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -132,9 +138,10 @@
             .thenReturn(setOf(TEST_PACKAGE_PANEL))
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
     }
@@ -149,9 +156,10 @@
                 ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true),
                 ControlsServiceInfo(ComponentName("other_package", "cls"), "non panel", false)
             )
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
     }
@@ -166,9 +174,10 @@
                 ControlsServiceInfo(ComponentName("other_package", "cls"), "panel", true),
                 ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)
             )
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).setPreferredSelection(listings[1].toPanelItem())
     }
@@ -176,10 +185,11 @@
     @Test
     fun testPreferredSelectionIsPanel_bindOnStart() {
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
         `when`(controlsController.getPreferredSelection()).thenReturn(listings[0].toPanelItem())
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).bindComponentForPanel(TEST_COMPONENT_PANEL)
     }
@@ -187,11 +197,12 @@
     @Test
     fun testPreferredSelectionPanel_listingNoPanel_notBind() {
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
         `when`(controlsController.getPreferredSelection())
             .thenReturn(SelectedItem.PanelItem("panel", TEST_COMPONENT_PANEL))
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).bindComponentForPanel(any())
     }
@@ -199,10 +210,11 @@
     @Test
     fun testNotPanelSelection_noBind() {
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).bindComponentForPanel(any())
     }
@@ -221,6 +233,12 @@
         verify(controlsController, never()).setPreferredSelection(any())
     }
 
+    private fun setUpControlsListingControls(listings: List<ControlsServiceInfo>) {
+        doAnswer { doReturn(listings).`when`(controlsListingController).getCurrentServices() }
+            .`when`(controlsListingController)
+            .forceReload()
+    }
+
     private fun createStartable(enabled: Boolean): ControlsStartable {
         val component: ControlsComponent =
             mock() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
index 068852d..95c6897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
@@ -92,9 +92,6 @@
     @Captor
     private ArgumentCaptor<Observer<Collection<ComplicationViewModel>>> mObserverCaptor;
 
-    @Captor
-    private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
-
     @Complication.Category
     static final int COMPLICATION_CATEGORY = Complication.CATEGORY_SYSTEM;
 
@@ -189,8 +186,6 @@
 
         // Dream entry animations finished.
         when(mDreamOverlayStateController.areEntryAnimationsFinished()).thenReturn(true);
-        final DreamOverlayStateController.Callback stateCallback = captureOverlayStateCallback();
-        stateCallback.onStateChanged();
 
         // Add a complication after entry animations are finished.
         final HashSet<ComplicationViewModel> complications = new HashSet<>(
@@ -223,9 +218,4 @@
                 mObserverCaptor.capture());
         return mObserverCaptor.getValue();
     }
-
-    private DreamOverlayStateController.Callback captureOverlayStateCallback() {
-        verify(mDreamOverlayStateController).addCallback(mCallbackCaptor.capture());
-        return mCallbackCaptor.getValue();
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index fc3a638..5cd24e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -358,6 +358,50 @@
         }
 
     @Test
+    fun `LOCKSCREEN to DREAMING`() =
+        testScope.runTest {
+            // GIVEN a device that is not dreaming or dozing
+            keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            reset(mockTransitionRepository)
+
+            // WHEN the device begins to dream
+            keyguardRepository.setDreamingWithOverlay(true)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DREAMING should occur
+            assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun `LOCKSCREEN to DOZING`() =
         testScope.runTest {
             // GIVEN a device with AOD not available
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
index c7f3fa0..fb20bac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
@@ -121,4 +121,92 @@
         assertThat(underTest.getName()).doesNotContain("original")
         assertThat(underTest.getVal()).isEqualTo("8900")
     }
+
+    @Test
+    fun updateTo_emptyToString_isString() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set("newString")
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("newString")
+    }
+
+    @Test
+    fun updateTo_intToEmpty_isEmpty() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(42)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isFalse()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("null")
+    }
+
+    @Test
+    fun updateTo_stringToBool_isBool() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set("oldString")
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set(true)
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("true")
+    }
+
+    @Test
+    fun updateTo_intToString_isString() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(43)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set("newString")
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("newString")
+    }
+
+    @Test
+    fun updateTo_boolToInt_isInt() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(false)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set(44)
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("44")
+    }
+
+    @Test
+    fun updateTo_boolToNewBool_isNewBool() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(false)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set(true)
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("true")
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 2c8d7ab..949fa1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -435,11 +435,236 @@
 
         assertThat(dumpedString).doesNotContain("testString[0]")
         assertThat(dumpedString).doesNotContain("testString[1]")
-        assertThat(dumpedString).doesNotContain("testString[2]")
+        // The buffer should contain [MAX_SIZE + 1] entries since we also save the most recently
+        // evicted value.
+        assertThat(dumpedString).contains("testString[2]")
         assertThat(dumpedString).contains("testString[3]")
         assertThat(dumpedString).contains("testString[${MAX_SIZE + 2}]")
     }
 
+    @Test
+    fun columnEvicted_lastKnownColumnValueInDump() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvicted", value = "evictedValue")
+
+        // Exactly fill the buffer so that "willBeEvicted" is evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have both the evicted column entry...
+        val evictedColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "willBeEvicted" +
+                SEPARATOR +
+                "evictedValue"
+        assertThat(dumpedString).contains(evictedColumnLog)
+
+        // ... *and* all of the fillingColumn entries.
+        val firstFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[0]"
+        val lastFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(1100L) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[9]"
+        assertThat(dumpedString).contains(firstFillingColumnLog)
+        assertThat(dumpedString).contains(lastFillingColumnLog)
+    }
+
+    @Test
+    fun multipleColumnsEvicted_allColumnsInDump() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedString", value = "evictedValue")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedInt", value = 45)
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedBool", value = true)
+
+        // Exactly fill the buffer so that all the above columns will be evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have all the evicted column entries...
+        val evictedColumnLogString =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "willBeEvictedString" +
+                SEPARATOR +
+                "evictedValue"
+        val evictedColumnLogInt =
+            TABLE_LOG_DATE_FORMAT.format(200L) + SEPARATOR + "willBeEvictedInt" + SEPARATOR + "45"
+        val evictedColumnLogBool =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "willBeEvictedBool" +
+                SEPARATOR +
+                "true"
+        assertThat(dumpedString).contains(evictedColumnLogString)
+        assertThat(dumpedString).contains(evictedColumnLogInt)
+        assertThat(dumpedString).contains(evictedColumnLogBool)
+
+        // ... *and* all of the fillingColumn entries.
+        val firstFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(400) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[0]"
+        val lastFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(1300) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[9]"
+        assertThat(dumpedString).contains(firstFillingColumnLog)
+        assertThat(dumpedString).contains(lastFillingColumnLog)
+    }
+
+    @Test
+    fun multipleColumnsEvicted_differentPrefixSameName_allColumnsInDump() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "prefix1", columnName = "sameName", value = "value1")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "prefix2", columnName = "sameName", value = "value2")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "prefix3", columnName = "sameName", value = "value3")
+
+        // Exactly fill the buffer so that all the above columns will be evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have all the evicted column entries
+        val evictedColumn1 =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "prefix1.sameName" +
+                SEPARATOR +
+                "value1"
+        val evictedColumn2 =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "prefix2.sameName" +
+                SEPARATOR +
+                "value2"
+        val evictedColumn3 =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "prefix3.sameName" +
+                SEPARATOR +
+                "value3"
+        assertThat(dumpedString).contains(evictedColumn1)
+        assertThat(dumpedString).contains(evictedColumn2)
+        assertThat(dumpedString).contains(evictedColumn3)
+    }
+
+    @Test
+    fun multipleColumnsEvicted_dumpSortedByTimestamp() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedFirst", value = "evictedValue")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedSecond", value = 45)
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedThird", value = true)
+
+        // Exactly fill the buffer with so that all the above columns will be evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have all the evicted column entries in timestamp order
+        val firstEvictedLog =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "willBeEvictedFirst" +
+                SEPARATOR +
+                "evictedValue"
+        val secondEvictedLog =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "willBeEvictedSecond" +
+                SEPARATOR +
+                "45"
+        val thirdEvictedLog =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "willBeEvictedThird" +
+                SEPARATOR +
+                "true"
+        assertThat(dumpedString).contains(firstEvictedLog)
+        val stringAfterFirst = dumpedString.substringAfter(firstEvictedLog)
+        assertThat(stringAfterFirst).contains(secondEvictedLog)
+        val stringAfterSecond = stringAfterFirst.substringAfter(secondEvictedLog)
+        assertThat(stringAfterSecond).contains(thirdEvictedLog)
+    }
+
+    @Test
+    fun sameColumnEvictedMultipleTimes_onlyLastEvictionInDump() {
+        systemClock.setCurrentTimeMillis(0L)
+
+        for (i in 1 until 4) {
+            systemClock.advanceTime(100L)
+            val dumpString = "evicted[$i]"
+            underTest.logChange(prefix = "", columnName = "evictedColumn", value = dumpString)
+        }
+
+        // Exactly fill the buffer so that all the entries for "evictedColumn" will be evicted.
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we only have the most recent evicted column entry
+        val evictedColumnLog1 =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "evictedColumn" +
+                SEPARATOR +
+                "evicted[1]"
+        val evictedColumnLog2 =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "evictedColumn" +
+                SEPARATOR +
+                "evicted[2]"
+        val evictedColumnLog3 =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "evictedColumn" +
+                SEPARATOR +
+                "evicted[3]"
+        assertThat(dumpedString).doesNotContain(evictedColumnLog1)
+        assertThat(dumpedString).doesNotContain(evictedColumnLog2)
+        assertThat(dumpedString).contains(evictedColumnLog3)
+    }
+
     private fun dumpChanges(): String {
         underTest.dump(PrintWriter(outputWriter), arrayOf())
         return outputWriter.toString()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 608d809..3f940d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -72,7 +72,7 @@
         MockitoAnnotations.initMocks(this)
 
         whenever(context.packageManager).thenReturn(packageManager)
-        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(noteTaskInfo)
+        whenever(resolver.resolveInfo(any(), any())).thenReturn(noteTaskInfo)
         whenever(userManager.isUserUnlocked).thenReturn(true)
         whenever(
                 devicePolicyManager.getKeyguardDisabledFeatures(
@@ -102,7 +102,7 @@
     // region onBubbleExpandChanged
     @Test
     fun onBubbleExpandChanged_expanding_logNoteTaskOpened() {
-        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false)
 
         createNoteTaskController()
             .apply { infoReference.set(expectedInfo) }
@@ -117,7 +117,7 @@
 
     @Test
     fun onBubbleExpandChanged_collapsing_logNoteTaskClosed() {
-        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false)
 
         createNoteTaskController()
             .apply { infoReference.set(expectedInfo) }
@@ -132,7 +132,7 @@
 
     @Test
     fun onBubbleExpandChanged_expandingAndKeyguardLocked_doNothing() {
-        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
+        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
 
         createNoteTaskController()
             .apply { infoReference.set(expectedInfo) }
@@ -146,35 +146,7 @@
 
     @Test
     fun onBubbleExpandChanged_notExpandingAndKeyguardLocked_doNothing() {
-        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
-
-        createNoteTaskController()
-            .apply { infoReference.set(expectedInfo) }
-            .onBubbleExpandChanged(
-                isExpanding = false,
-                key = Bubble.KEY_APP_BUBBLE,
-            )
-
-        verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
-    }
-
-    @Test
-    fun onBubbleExpandChanged_expandingAndInMultiWindowMode_doNothing() {
-        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
-
-        createNoteTaskController()
-            .apply { infoReference.set(expectedInfo) }
-            .onBubbleExpandChanged(
-                isExpanding = true,
-                key = Bubble.KEY_APP_BUBBLE,
-            )
-
-        verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
-    }
-
-    @Test
-    fun onBubbleExpandChanged_notExpandingAndInMultiWindowMode_doNothing() {
-        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true)
+        val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
 
         createNoteTaskController()
             .apply { infoReference.set(expectedInfo) }
@@ -215,16 +187,14 @@
         val expectedInfo =
             noteTaskInfo.copy(
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
                 isKeyguardLocked = true,
             )
         whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
 
         createNoteTaskController()
             .showNoteTask(
                 entryPoint = expectedInfo.entryPoint!!,
-                isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
             )
 
         val intentCaptor = argumentCaptor<Intent>()
@@ -250,16 +220,14 @@
         val expectedInfo =
             noteTaskInfo.copy(
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
                 isKeyguardLocked = false,
             )
-        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
+        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
         whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
 
         createNoteTaskController()
             .showNoteTask(
                 entryPoint = expectedInfo.entryPoint!!,
-                isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
             )
 
         verifyZeroInteractions(context)
@@ -275,49 +243,10 @@
     }
 
     @Test
-    fun showNoteTask_isInMultiWindowMode_shouldStartActivityAndLogUiEvent() {
-        val expectedInfo =
-            noteTaskInfo.copy(
-                entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
-                isInMultiWindowMode = true,
-                isKeyguardLocked = false,
-            )
-        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
-        whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
-
-        createNoteTaskController()
-            .showNoteTask(
-                entryPoint = expectedInfo.entryPoint!!,
-                isInMultiWindowMode = expectedInfo.isInMultiWindowMode,
-            )
-
-        val intentCaptor = argumentCaptor<Intent>()
-        val userCaptor = argumentCaptor<UserHandle>()
-        verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
-
-        (intentCaptor.value.flags and FLAG_ACTIVITY_NEW_TASK) == FLAG_ACTIVITY_NEW_TASK
-
-        intentCaptor.value.let { intent ->
-            assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
-            assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
-            assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
-            assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
-                .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
-            assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
-                .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
-            assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
-        }
-        assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
-        verify(eventLogger).logNoteTaskOpened(expectedInfo)
-        verifyZeroInteractions(bubbles)
-    }
-
-    @Test
     fun showNoteTask_bubblesIsNull_shouldDoNothing() {
         createNoteTaskController(bubbles = null)
             .showNoteTask(
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
             )
 
         verifyZeroInteractions(context, bubbles, eventLogger)
@@ -325,12 +254,11 @@
 
     @Test
     fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
-        whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null)
+        whenever(resolver.resolveInfo(any(), any())).thenReturn(null)
 
         createNoteTaskController()
             .showNoteTask(
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
             )
 
         verifyZeroInteractions(context, bubbles, eventLogger)
@@ -341,7 +269,6 @@
         createNoteTaskController(isEnabled = false)
             .showNoteTask(
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
             )
 
         verifyZeroInteractions(context, bubbles, eventLogger)
@@ -354,7 +281,6 @@
         createNoteTaskController()
             .showNoteTask(
                 entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
             )
 
         verifyZeroInteractions(context, bubbles, eventLogger)
@@ -405,11 +331,7 @@
             )
             .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
 
-        createNoteTaskController()
-            .showNoteTask(
-                isInMultiWindowMode = false,
-                entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
-            )
+        createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
 
         verifyZeroInteractions(context, bubbles, eventLogger)
     }
@@ -425,11 +347,7 @@
             )
             .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
 
-        createNoteTaskController()
-            .showNoteTask(
-                isInMultiWindowMode = false,
-                entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
-            )
+        createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
 
         verifyZeroInteractions(context, bubbles, eventLogger)
     }
@@ -445,11 +363,7 @@
             )
             .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
 
-        createNoteTaskController()
-            .showNoteTask(
-                isInMultiWindowMode = false,
-                entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
-            )
+        createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
 
         val intentCaptor = argumentCaptor<Intent>()
         verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
@@ -472,11 +386,7 @@
             )
             .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
 
-        createNoteTaskController()
-            .showNoteTask(
-                isInMultiWindowMode = false,
-                entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE
-            )
+        createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
 
         val intentCaptor = argumentCaptor<Intent>()
         verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 7e975b6..91cd6ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -31,41 +31,19 @@
         NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
 
     @Test
-    fun launchMode_notInMultiWindowModeAndKeyguardUnlocked_launchModeAppBubble() {
-        val underTest =
-            createNoteTaskInfo()
-                .copy(
-                    isKeyguardLocked = false,
-                    isInMultiWindowMode = false,
-                )
+    fun launchMode_keyguardLocked_launchModeActivity() {
+        val underTest = createNoteTaskInfo().copy(isKeyguardLocked = true)
+
+        assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
+    }
+
+    @Test
+    fun launchMode_keyguardUnlocked_launchModeActivity() {
+        val underTest = createNoteTaskInfo().copy(isKeyguardLocked = false)
 
         assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
     }
 
-    @Test
-    fun launchMode_inMultiWindowMode_launchModeActivity() {
-        val underTest =
-            createNoteTaskInfo()
-                .copy(
-                    isKeyguardLocked = false,
-                    isInMultiWindowMode = true,
-                )
-
-        assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
-    }
-
-    @Test
-    fun launchMode_keyguardLocked_launchModeActivity() {
-        val underTest =
-            createNoteTaskInfo()
-                .copy(
-                    isKeyguardLocked = true,
-                    isInMultiWindowMode = false,
-                )
-
-        assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
-    }
-
     private companion object {
         const val NOTES_PACKAGE_NAME = "com.android.note.app"
         const val NOTES_UID = 123456
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index e57d0d9..d44012f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -18,75 +18,139 @@
 
 package com.android.systemui.notetask.quickaffordance
 
+import android.hardware.input.InputSettings
 import android.test.suitebuilder.annotation.SmallTest
-import androidx.test.runner.AndroidJUnit4
+import android.testing.AndroidTestingRunner
+import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.notetask.NoteTaskController
 import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.stylus.StylusManager
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runTest
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
 
-/**
- * Tests for [NoteTaskQuickAffordanceConfig].
- *
- * Build/Install/Run:
- * - atest SystemUITests:NoteTaskQuickAffordanceConfigTest
- */
+/** atest SystemUITests:NoteTaskQuickAffordanceConfigTest */
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(AndroidTestingRunner::class)
 internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
 
-    @Mock lateinit var noteTaskController: NoteTaskController
+    @Mock lateinit var controller: NoteTaskController
+    @Mock lateinit var stylusManager: StylusManager
+    @Mock lateinit var repository: KeyguardQuickAffordanceRepository
+
+    private lateinit var mockitoSession: MockitoSession
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
+        mockitoSession =
+            ExtendedMockito.mockitoSession()
+                .initMocks(this)
+                .mockStatic(InputSettings::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+
+        whenever(InputSettings.isStylusEverUsed(mContext)).then { true }
+        whenever(repository.selections).then {
+            val map = mapOf("" to listOf(createUnderTest()))
+            MutableStateFlow(map)
+        }
     }
 
-    private fun createUnderTest(isEnabled: Boolean) =
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
+    private fun createUnderTest(isEnabled: Boolean = true): KeyguardQuickAffordanceConfig =
         NoteTaskQuickAffordanceConfig(
             context = context,
-            noteTaskController = noteTaskController,
+            controller = controller,
+            stylusManager = stylusManager,
+            lazyRepository = { repository },
             isEnabled = isEnabled,
         )
 
+    private fun createLockScreenStateVisible(): LockScreenState =
+        LockScreenState.Visible(
+            icon =
+                Icon.Resource(
+                    res = R.drawable.ic_note_task_shortcut_keyguard,
+                    contentDescription =
+                        ContentDescription.Resource(R.string.note_task_button_label),
+                )
+        )
+
+    @Test
+    fun lockScreenState_stylusUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
+        val underTest = createUnderTest()
+
+        val actual by collectLastValue(underTest.lockScreenState)
+
+        assertThat(actual).isEqualTo(createLockScreenStateVisible())
+    }
+
+    @Test
+    fun lockScreenState_noStylusEverUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
+        whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
+        val underTest = createUnderTest()
+
+        val actual by collectLastValue(underTest.lockScreenState)
+
+        assertThat(actual).isEqualTo(createLockScreenStateVisible())
+    }
+
+    @Test
+    fun lockScreenState_stylusUsed_customShortcutSelected_shouldEmitVisible() = runTest {
+        whenever(repository.selections).then {
+            val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
+            MutableStateFlow(map)
+        }
+        val underTest = createUnderTest()
+
+        val actual by collectLastValue(underTest.lockScreenState)
+
+        assertThat(actual).isEqualTo(createLockScreenStateVisible())
+    }
+
+    @Test
+    fun lockScreenState_noIsStylusEverUsed_noCustomShortcutSelected_shouldEmitHidden() = runTest {
+        whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
+        whenever(repository.selections).then {
+            val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
+            MutableStateFlow(map)
+        }
+        val underTest = createUnderTest()
+
+        val actual by collectLastValue(underTest.lockScreenState)
+
+        assertThat(actual).isEqualTo(LockScreenState.Hidden)
+    }
+
     @Test
     fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
         val underTest = createUnderTest(isEnabled = false)
 
-        val actual = collectLastValue(underTest.lockScreenState)
+        val actual by collectLastValue(underTest.lockScreenState)
 
-        assertThat(actual()).isEqualTo(LockScreenState.Hidden)
-    }
-
-    @Test
-    fun lockScreenState_isEnabled_shouldEmitVisible() = runTest {
-        val stringResult = "Notetaking"
-        val underTest = createUnderTest(isEnabled = true)
-
-        val actual = collectLastValue(underTest.lockScreenState)
-
-        val expected =
-            LockScreenState.Visible(
-                icon =
-                    Icon.Resource(
-                        res = R.drawable.ic_note_task_shortcut_keyguard,
-                        contentDescription = ContentDescription.Loaded(stringResult),
-                    )
-            )
-        assertThat(actual()).isEqualTo(expected)
+        assertThat(actual).isEqualTo(LockScreenState.Hidden)
     }
 
     @Test
@@ -95,6 +159,6 @@
 
         underTest.onTriggered(expandable = null)
 
-        verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+        verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
new file mode 100644
index 0000000..eb7b481
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.app.AssistUtils
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.model.SysUiState
+import com.android.systemui.navigationbar.NavigationBarController
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.recents.IOverviewProxy
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_OFF
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_ON
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_OFF
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_ON
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.sysui.ShellInterface
+import com.google.common.util.concurrent.MoreExecutors
+import dagger.Lazy
+import java.util.Optional
+import java.util.concurrent.Executor
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.intThat
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class OverviewProxyServiceTest : SysuiTestCase() {
+
+    @Main private val executor: Executor = MoreExecutors.directExecutor()
+
+    private lateinit var subject: OverviewProxyService
+    private val dumpManager = DumpManager()
+    private val displayTracker = FakeDisplayTracker(mContext)
+    private val sysUiState = SysUiState(displayTracker)
+    private val screenLifecycle = ScreenLifecycle(dumpManager)
+
+    @Mock private lateinit var overviewProxy: IOverviewProxy.Stub
+    @Mock private lateinit var packageManager: PackageManager
+
+    // The following mocks belong to not-yet-tested parts of OverviewProxyService.
+    @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var shellInterface: ShellInterface
+    @Mock private lateinit var navBarController: NavigationBarController
+    @Mock private lateinit var centralSurfaces: CentralSurfaces
+    @Mock private lateinit var navModeController: NavigationModeController
+    @Mock private lateinit var statusBarWinController: NotificationShadeWindowController
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController
+    @Mock private lateinit var assistUtils: AssistUtils
+    @Mock
+    private lateinit var unfoldTransitionProgressForwarder:
+        Optional<UnfoldTransitionProgressForwarder>
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        val serviceComponent = ComponentName("test_package", "service_provider")
+        context.addMockService(serviceComponent, overviewProxy)
+        context.addMockServiceResolver(
+            TestableContext.MockServiceResolver {
+                if (it.action == ACTION_QUICKSTEP) serviceComponent else null
+            }
+        )
+        whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString()))
+            .thenReturn(overviewProxy)
+        whenever(overviewProxy.asBinder()).thenReturn(overviewProxy)
+
+        // packageManager.resolveServiceAsUser has to return non-null for
+        // OverviewProxyService#isEnabled to become true.
+        context.setMockPackageManager(packageManager)
+        whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
+            .thenReturn(mock(ResolveInfo::class.java))
+
+        subject =
+            OverviewProxyService(
+                context,
+                executor,
+                commandQueue,
+                shellInterface,
+                Lazy { navBarController },
+                Lazy { Optional.of(centralSurfaces) },
+                navModeController,
+                statusBarWinController,
+                sysUiState,
+                userTracker,
+                screenLifecycle,
+                uiEventLogger,
+                displayTracker,
+                sysuiUnlockAnimationController,
+                assistUtils,
+                dumpManager,
+                unfoldTransitionProgressForwarder
+            )
+    }
+
+    @After
+    fun tearDown() {
+        subject.shutdownForTest()
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurnedOn triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurnedOn()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_ON }
+            )
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurningOn triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurningOn()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_ON }
+            )
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurnedOff triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurnedOff()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_OFF }
+            )
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurningOff triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurningOff()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_OFF }
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 82a5743..51492eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -26,6 +26,7 @@
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.dock.DockManager
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
@@ -86,6 +87,7 @@
     @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
     @Mock private lateinit var notificationInsetsController: NotificationInsetsController
     @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    @Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
     @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
     @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
     @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@@ -133,6 +135,7 @@
                 keyguardBouncerViewModel,
                 keyguardBouncerComponentFactory,
                 alternateBouncerInteractor,
+                udfpsOverlayInteractor,
                 keyguardTransitionInteractor,
                 primaryBouncerToGoneTransitionViewModel,
             )
@@ -265,6 +268,17 @@
     }
 
     @Test
+    fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() {
+        // Down event within udfpsOverlay bounds while alternateBouncer is showing
+        whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(downEv)).thenReturn(false)
+        whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
+
+        // Then touch should not be intercepted
+        val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(downEv)
+        assertThat(shouldIntercept).isFalse()
+    }
+
+    @Test
     fun testGetBouncerContainer() {
         Mockito.clearInvocations(view)
         underTest.bouncerContainer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index faa6221..2f528a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -40,6 +40,7 @@
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -101,6 +102,7 @@
     @Mock private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
     @Mock private NotificationInsetsController mNotificationInsetsController;
     @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
+    @Mock private UdfpsOverlayInteractor mUdfpsOverlayInteractor;
     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
 
@@ -152,6 +154,7 @@
                 mKeyguardBouncerViewModel,
                 mKeyguardBouncerComponentFactory,
                 mAlternateBouncerInteractor,
+                mUdfpsOverlayInteractor,
                 mKeyguardTransitionInteractor,
                 mPrimaryBouncerToGoneTransitionViewModel
         );
@@ -177,6 +180,7 @@
         // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
         when(mStatusBarStateController.isDozing()).thenReturn(false);
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+        when(mUdfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(any())).thenReturn(true);
         when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
 
         // THEN we should intercept touch
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index e3a3678..15b8423 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -77,7 +78,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
@@ -91,9 +91,12 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 import dagger.Lazy;
 
 @SmallTest
@@ -101,12 +104,14 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class QuickSettingsControllerTest extends SysuiTestCase {
 
-    private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
     private static final float QS_FRAME_START_X = 0f;
     private static final int QS_FRAME_WIDTH = 1000;
     private static final int QS_FRAME_TOP = 0;
     private static final int QS_FRAME_BOTTOM = 1000;
-
+    private static final int DEFAULT_HEIGHT = 1000;
+    // In split shade min = max
+    private static final int DEFAULT_MIN_HEIGHT_SPLIT_SHADE = DEFAULT_HEIGHT;
+    private static final int DEFAULT_MIN_HEIGHT = 300;
 
     private QuickSettingsController mQsController;
 
@@ -115,7 +120,6 @@
     @Mock private KeyguardStatusBarView mKeyguardStatusBar;
     @Mock private QS mQs;
     @Mock private QSFragment mQSFragment;
-
     @Mock private Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private NotificationPanelView mPanelView;
@@ -147,10 +151,7 @@
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private InteractionJankMonitor mInteractionJankMonitor;
     @Mock private ShadeLogger mShadeLogger;
-
     @Mock private DumpManager mDumpManager;
-
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
     @Mock private UiEventLogger mUiEventLogger;
 
     private SysuiStatusBarStateController mStatusBarStateController;
@@ -173,6 +174,8 @@
         KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
         keyguardStatusView.setId(R.id.keyguard_status_view);
 
+        when(mResources.getDimensionPixelSize(
+                R.dimen.lockscreen_shade_qs_transition_distance)).thenReturn(DEFAULT_HEIGHT);
         when(mPanelView.getResources()).thenReturn(mResources);
         when(mPanelView.getContext()).thenReturn(getContext());
         when(mPanelView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
@@ -529,6 +532,88 @@
         assertThat(mQsController.isOpenQsEvent(event)).isTrue();
     }
 
+    @Test
+    public void shadeClosed_onLockscreen_inSplitShade_setsQsNotVisible() {
+        mQsController.setQs(mQs);
+        enableSplitShade(true);
+        lockScreen();
+
+        closeLockedQS();
+
+        assertQsVisible(false);
+    }
+
+    @Test
+    public void shadeOpened_onLockscreen_inSplitShade_setsQsVisible() {
+        mQsController.setQs(mQs);
+        enableSplitShade(true);
+        lockScreen();
+
+        openLockedQS();
+
+        assertQsVisible(true);
+    }
+
+    @Test
+    public void shadeClosed_onLockscreen_inSingleShade_setsQsNotVisible() {
+        mQsController.setQs(mQs);
+        enableSplitShade(false);
+        lockScreen();
+
+        closeLockedQS();
+
+        verify(mQs).setQsVisible(false);
+    }
+
+    @Test
+    public void shadeOpened_onLockscreen_inSingleShade_setsQsVisible() {
+        mQsController.setQs(mQs);
+        enableSplitShade(false);
+        lockScreen();
+
+        openLockedQS();
+
+        verify(mQs).setQsVisible(true);
+    }
+
+    private void lockScreen() {
+        mQsController.setBarState(KEYGUARD);
+    }
+
+    private void openLockedQS() {
+        when(mLockscreenShadeTransitionController.getQSDragProgress())
+                .thenReturn((float) DEFAULT_HEIGHT);
+        mLockscreenShadeTransitionCallback.setTransitionToFullShadeAmount(
+                /* pxAmount= */ DEFAULT_HEIGHT,
+                /* animate=*/ false,
+                /* delay= */ 0
+        );
+    }
+
+    private void closeLockedQS() {
+        when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(0f);
+        mLockscreenShadeTransitionCallback.setTransitionToFullShadeAmount(
+                /* pxAmount= */ 0,
+                /* animate=*/ false,
+                /* delay= */ 0
+        );
+    }
+
+    private void setSplitShadeHeightProperties() {
+        // In split shade, min = max
+        when(mQs.getQsMinExpansionHeight()).thenReturn(DEFAULT_MIN_HEIGHT_SPLIT_SHADE);
+        when(mQs.getDesiredHeight()).thenReturn(DEFAULT_HEIGHT);
+        mQsController.updateMinHeight();
+        mQsController.onHeightChanged();
+    }
+
+    private void setDefaultHeightProperties() {
+        when(mQs.getQsMinExpansionHeight()).thenReturn(DEFAULT_MIN_HEIGHT);
+        when(mQs.getDesiredHeight()).thenReturn(DEFAULT_HEIGHT);
+        mQsController.updateMinHeight();
+        mQsController.onHeightChanged();
+    }
+
     private static MotionEvent createMotionEvent(int x, int y, int action) {
         return MotionEvent.obtain(0, 0, action, x, y, 0);
     }
@@ -549,6 +634,11 @@
     private void enableSplitShade(boolean enabled) {
         when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
         mQsController.updateResources();
+        if (enabled) {
+            setSplitShadeHeightProperties();
+        } else {
+            setDefaultHeightProperties();
+        }
     }
 
     private void setIsFullWidth(boolean fullWidth) {
@@ -561,5 +651,11 @@
         mQsController.handleShadeLayoutChanged(oldMaxHeight);
     }
 
-
+    private void assertQsVisible(boolean visible) {
+        ArgumentCaptor<Boolean> visibilityCaptor = ArgumentCaptor.forClass(Boolean.class);
+        verify(mQs, atLeastOnce()).setQsVisible(visibilityCaptor.capture());
+        List<Boolean> allVisibilities = visibilityCaptor.getAllValues();
+        boolean lastVisibility = allVisibilities.get(allVisibilities.size() - 1);
+        assertThat(lastVisibility).isEqualTo(visible);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c4ee326..031c17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -1091,6 +1091,34 @@
     }
 
     @Test
+    public void testOccludingQSNotExpanded_transitionToAuthScrimmed() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and panel is NOT expanded
+        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+        mCentralSurfaces.mPanelExpanded = false;
+
+        mCentralSurfaces.updateScrimController();
+
+        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED));
+    }
+
+    @Test
+    public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and qs IS expanded
+        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+        mCentralSurfaces.mPanelExpanded = true;
+
+        mCentralSurfaces.updateScrimController();
+
+        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
+    }
+
+    @Test
     public void testShowKeyguardImplementation_setsState() {
         when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
 
@@ -1314,6 +1342,15 @@
         verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString());
     }
 
+    @Test
+    public void frpLockedDevice_shadeDisabled() {
+        when(mDeviceProvisionedController.isFrpActive()).thenReturn(true);
+        when(mDozeServiceHost.isPulsing()).thenReturn(true);
+        mCentralSurfaces.updateNotificationPanelTouchState();
+
+        verify(mNotificationPanelViewController).setTouchAndAnimationDisabled(true);
+    }
+
     /**
      * Configures the appropriate mocks and then calls {@link CentralSurfacesImpl#updateIsKeyguard}
      * to reconfigure the keyguard to reflect the requested showing/occluded states.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 158e9ad..e2019b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -683,4 +683,30 @@
         // the following call before registering centralSurfaces should NOT throw a NPE:
         mStatusBarKeyguardViewManager.hideAlternateBouncer(true);
     }
+
+    @Test
+    public void testResetHideBouncerWhenShowing_alternateBouncerHides() {
+        // GIVEN the keyguard is showing
+        reset(mAlternateBouncerInteractor);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+        // WHEN SBKV is reset with hideBouncerWhenShowing=true
+        mStatusBarKeyguardViewManager.reset(true);
+
+        // THEN alternate bouncer is hidden
+        verify(mAlternateBouncerInteractor).hide();
+    }
+
+    @Test
+    public void testResetHideBouncerWhenShowingIsFalse_alternateBouncerHides() {
+        // GIVEN the keyguard is showing
+        reset(mAlternateBouncerInteractor);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+        // WHEN SBKV is reset with hideBouncerWhenShowing=false
+        mStatusBarKeyguardViewManager.reset(false);
+
+        // THEN alternate bouncer is NOT hidden
+        verify(mAlternateBouncerInteractor, never()).hide();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
index 5129f85..6980a0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -90,6 +90,12 @@
     }
 
     @Test
+    fun testFrpNotActiveByDefault() {
+        init()
+        assertThat(controller.isFrpActive).isFalse()
+    }
+
+    @Test
     fun testNotUserSetupByDefault() {
         init()
         assertThat(controller.isUserSetup(START_USER)).isFalse()
@@ -104,6 +110,14 @@
     }
 
     @Test
+    fun testFrpActiveWhenCreated() {
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        init()
+
+        assertThat(controller.isFrpActive).isTrue()
+    }
+
+    @Test
     fun testUserSetupWhenCreated() {
         settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
         init()
@@ -122,6 +136,16 @@
     }
 
     @Test
+    fun testFrpActiveChange() {
+        init()
+
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        testableLooper.processAllMessages() // background observer
+
+        assertThat(controller.isFrpActive).isTrue()
+    }
+
+    @Test
     fun testUserSetupChange() {
         init()
 
@@ -164,6 +188,7 @@
         mainExecutor.runAllReady()
 
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onUserSwitched()
     }
@@ -181,6 +206,7 @@
         verify(listener).onUserSwitched()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
     }
 
     @Test
@@ -195,6 +221,7 @@
         verify(listener, never()).onUserSwitched()
         verify(listener).onUserSetupChanged()
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
     }
 
     @Test
@@ -208,10 +235,26 @@
 
         verify(listener, never()).onUserSwitched()
         verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener).onDeviceProvisionedChanged()
     }
 
     @Test
+    fun testListenerCalledOnFrpActiveChanged() {
+        init()
+        controller.addCallback(listener)
+
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        testableLooper.processAllMessages()
+        mainExecutor.runAllReady()
+
+        verify(listener, never()).onUserSwitched()
+        verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener).onFrpActiveChanged()
+    }
+
+    @Test
     fun testRemoveListener() {
         init()
         controller.addCallback(listener)
@@ -220,11 +263,13 @@
         switchUser(10)
         settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
         settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
 
         testableLooper.processAllMessages()
         mainExecutor.runAllReady()
 
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onUserSwitched()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 046ad12..f9bfafc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.util.service;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -169,4 +171,19 @@
         verify(mCallback).onDisconnected(eq(connection),
                 eq(ObservableServiceConnection.DISCONNECT_REASON_UNBIND));
     }
+
+    @Test
+    public void testBindServiceThrowsError() {
+        ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
+                mIntent, mExecutor, mTransformer);
+        connection.addCallback(mCallback);
+
+        when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection)))
+                .thenThrow(new SecurityException());
+
+        // Verify that the exception was caught and that bind returns false, and we properly
+        // unbind.
+        assertThat(connection.bind()).isFalse();
+        verify(mContext).unbindService(connection);
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b54dbbf..7f6ad43 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -742,6 +742,9 @@
             return Collections.EMPTY_LIST;
         }
         final String typeHints = mService.getMaster().getPccProviderHints();
+        if (sVerbose) {
+            Slog.v(TAG, "TypeHints flag:" + typeHints);
+        }
         if (TextUtils.isEmpty(typeHints)) {
             return new ArrayList<>();
         }
@@ -757,7 +760,7 @@
         @GuardedBy("mLock")
         void maybeRequestFieldClassificationFromServiceLocked() {
             if (mClassificationState.mPendingFieldClassificationRequest == null) {
-                Log.w(TAG, "Received AssistData without pending classification request");
+                Slog.w(TAG, "Received AssistData without pending classification request");
                 return;
             }
 
@@ -791,7 +794,8 @@
             final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID);
 
             if (sVerbose) {
-                Slog.v(TAG, "New structure for requestId " + requestId + ": " + structure);
+                Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": "
+                        + structure);
             }
 
             synchronized (mLock) {
@@ -1125,6 +1129,13 @@
         // structure is taken. This causes only one fill request per burst of focus changes.
         cancelCurrentRequestLocked();
 
+        if (mClassificationState.mHintsToAutofillIdMap == null) {
+            if (sVerbose) {
+                Slog.v(TAG, "triggering field classification");
+            }
+            requestAssistStructureForPccLocked(flags | FLAG_PCC_DETECTION);
+        }
+
         // Only ask IME to create inline suggestions request when
         // 1. Autofill provider supports it or client enabled client suggestions.
         // 2. The render service is available.
@@ -1376,7 +1387,6 @@
     @Override
     public void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
             @NonNull String servicePackageName, int requestFlags) {
-
         final AutofillId[] fieldClassificationIds;
 
         final LogMaker requestLog;
@@ -1609,10 +1619,68 @@
         Set<Dataset> eligibleDatasets = new ArraySet<>();
         Set<AutofillId> eligibleAutofillIds = new ArraySet<>();
         for (Dataset dataset : response.getDatasets()) {
-            if (dataset.getFieldIds() == null) continue;
+            if (dataset.getFieldIds() == null || dataset.getFieldIds().isEmpty()) continue;
             if (dataset.getAutofillDatatypes() != null
-                    && dataset.getAutofillDatatypes().size() > 0) {
-                continue;
+                    && !dataset.getAutofillDatatypes().isEmpty()) {
+                // This dataset has information relevant for detection too, so we should filter
+                // them out. It's possible that some fields are applicable to hints only, as such,
+                // they need to be filtered off.
+                // TODO(b/266379948): Verify the logic and add tests
+                // Update dataset to only have non-null fieldValues
+
+                // Figure out if we need to process results.
+                boolean conversionRequired = false;
+                int newSize = dataset.getFieldIds().size();
+                for (AutofillId id : dataset.getFieldIds()) {
+                    if (id == null) {
+                        conversionRequired = true;
+                        newSize--;
+                    }
+                }
+
+                if (conversionRequired) {
+                    ArrayList<AutofillId> fieldIds = new ArrayList<>(newSize);
+                    ArrayList<AutofillValue> fieldValues = new ArrayList<>(newSize);
+                    ArrayList<RemoteViews> fieldPresentations = new ArrayList<>(newSize);
+                    ArrayList<RemoteViews> fieldDialogPresentations = new ArrayList<>(newSize);
+                    ArrayList<InlinePresentation> fieldInlinePresentations =
+                            new ArrayList<>(newSize);
+                    ArrayList<InlinePresentation> fieldInlineTooltipPresentations =
+                            new ArrayList<>(newSize);
+                    ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>(newSize);
+
+                    for (int i = 0; i < dataset.getFieldIds().size(); i++) {
+                        AutofillId id = dataset.getFieldIds().get(i);
+                        if (id != null) {
+                            // Copy over
+                            fieldIds.add(id);
+                            fieldValues.add(dataset.getFieldValues().get(i));
+                            fieldPresentations.add(dataset.getFieldPresentation(i));
+                            fieldDialogPresentations.add(dataset.getFieldDialogPresentation(i));
+                            fieldInlinePresentations.add(dataset.getFieldInlinePresentation(i));
+                            fieldInlineTooltipPresentations.add(
+                                    dataset.getFieldInlineTooltipPresentation(i));
+                            fieldFilters.add(dataset.getFilter(i));
+                        }
+                    }
+                    dataset =
+                            new Dataset(
+                                    fieldIds,
+                                    fieldValues,
+                                    fieldPresentations,
+                                    fieldDialogPresentations,
+                                    fieldInlinePresentations,
+                                    fieldInlineTooltipPresentations,
+                                    fieldFilters,
+                                    new ArrayList<>(),
+                                    dataset.getFieldContent(),
+                                    null,
+                                    null,
+                                    null,
+                                    null,
+                                    dataset.getId(),
+                                    dataset.getAuthentication());
+                }
             }
             eligibleDatasets.add(dataset);
             for (AutofillId id : dataset.getFieldIds()) {
@@ -1639,6 +1707,7 @@
             ArrayMap<String, Set<AutofillId>> hintsToAutofillIdMap =
                     mClassificationState.mHintsToAutofillIdMap;
 
+            // TODO(266379948): Handle group hints too.
             ArrayMap<String, Set<AutofillId>> groupHintsToAutofillIdMap =
                     mClassificationState.mGroupHintsToAutofillIdMap;
 
@@ -1649,7 +1718,8 @@
 
             for (int i = 0; i < datasets.size(); i++) {
                 Dataset dataset = datasets.get(i);
-                if (dataset.getAutofillDatatypes() == null) continue;
+                if (dataset.getAutofillDatatypes() == null
+                        || dataset.getAutofillDatatypes().isEmpty()) continue;
                 if (dataset.getFieldIds() != null && dataset.getFieldIds().size() > 0) continue;
 
                 ArrayList<AutofillId> fieldIds = new ArrayList<>();
@@ -1661,6 +1731,7 @@
                 ArrayList<Dataset.DatasetFieldFilter> fieldFilters = new ArrayList<>();
 
                 for (int j = 0; j < dataset.getAutofillDatatypes().size(); j++) {
+                    if (dataset.getAutofillDatatypes().get(0) == null) continue;
                     String hint = dataset.getAutofillDatatypes().get(j);
 
                     if (hintsToAutofillIdMap.containsKey(hint)) {
@@ -4560,7 +4631,7 @@
         if (mResponses == null) {
             // Set initial capacity as 2 to handle cases where service always requires auth.
             // TODO: add a metric for number of responses set by server, so we can use its average
-            // as the initial array capacitiy.
+            // as the initial array capacity.
             mResponses = new SparseArray<>(2);
         }
         mResponses.put(requestId, newResponse);
@@ -4982,6 +5053,7 @@
             mClassificationGroupHintsMap = new ArrayMap<>();
             mHintsToAutofillIdMap = new ArrayMap<>();
             mGroupHintsToAutofillIdMap = new ArrayMap<>();
+            mClassificationCombinedHintsMap = new ArrayMap<>();
             Set<android.service.assist.classification.FieldClassification> classifications =
                     response.getClassifications();
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 24c9e0f..0b22f36 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -640,6 +640,7 @@
         mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
         setAllowListWhileInUsePermissionInFgs();
         initSystemExemptedFgsTypePermission();
+        initMediaProjectFgsTypeCustomPermission();
     }
 
     private AppStateTracker getAppStateTracker() {
@@ -2580,6 +2581,39 @@
         }
     }
 
+    /**
+     * A custom permission checker for the "mediaProjection" FGS type:
+     * if the app has been granted the permission to start a media projection via
+     * the {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()},
+     * it'll get the permission to start a foreground service with type "mediaProjection".
+     */
+    private class MediaProjectionFgsTypeCustomPermission extends ForegroundServiceTypePermission {
+        MediaProjectionFgsTypeCustomPermission() {
+            super("Media projection screen capture permission");
+        }
+
+        @Override
+        public int checkPermission(@NonNull Context context, int callerUid, int callerPid,
+                @NonNull String packageName, boolean allowWhileInUse) {
+            return mAm.isAllowedMediaProjectionNoOpCheck(callerUid)
+                    ? PERMISSION_GRANTED : PERMISSION_DENIED;
+        }
+    }
+
+    /**
+     * Set a custom permission checker for the "mediaProjection" FGS type.
+     */
+    private void initMediaProjectFgsTypeCustomPermission() {
+        final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy();
+        final ForegroundServiceTypePolicyInfo policyInfo =
+                policy.getForegroundServiceTypePolicyInfo(
+                       ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION,
+                       ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
+        if (policyInfo != null) {
+            policyInfo.setCustomPermission(new MediaProjectionFgsTypeCustomPermission());
+        }
+    }
+
     ServiceNotificationPolicy applyForegroundServiceNotificationLocked(Notification notification,
             final String tag, final int id, final String pkg, final int userId) {
         // By nature of the FGS API, all FGS notifications have a null tag
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 9e95e5f..5696004 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -25,6 +25,7 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityThread;
+import android.app.ForegroundServiceTypePolicy;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -1196,6 +1197,7 @@
                                 updateUseTieredCachedAdj();
                                 break;
                             default:
+                                updateFGSPermissionEnforcementFlagsIfNecessary(name);
                                 break;
                         }
                     }
@@ -1951,6 +1953,11 @@
             DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME);
     }
 
+    private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) {
+        ForegroundServiceTypePolicy.getDefaultPolicy()
+                .updatePermissionEnforcementFlagIfNecessary(name);
+    }
+
     @NeverCompile // Avoid size overhead of debugging code.
     void dump(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 37f744f..4b2467d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -38,6 +38,8 @@
 import static android.app.ActivityManager.StopUserOnSwitch;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
@@ -178,6 +180,7 @@
 import android.app.ActivityManagerInternal.BindServiceEventListener;
 import android.app.ActivityManagerInternal.BroadcastEventListener;
 import android.app.ActivityManagerInternal.ForegroundServiceStateListener;
+import android.app.ActivityManagerInternal.MediaProjectionTokenEvent;
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.ActivityThread;
 import android.app.AnrController;
@@ -1508,6 +1511,17 @@
 
     final AppRestrictionController mAppRestrictionController;
 
+    /**
+     * The collection of the MediaProjection tokens per UID, for the apps that are allowed to
+     * start FGS with the type "mediaProjection"; this permission is granted via the request over
+     * the call to {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()}.
+     *
+     * <p>Note, the "token" here is actually an instance of
+     * {@link android.media.projection.IMediaProjection}.</p>
+     */
+    @GuardedBy("mMediaProjectionTokenMap")
+    private final SparseArray<ArraySet<IBinder>> mMediaProjectionTokenMap = new SparseArray();
+
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
         final ProcessRecord mApp;
         final int mPid;
@@ -4929,8 +4943,14 @@
         EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
 
         synchronized (mProcLock) {
-            mOomAdjuster.setAttachingProcessStatesLSP(app);
+            app.mState.setCurAdj(ProcessList.INVALID_ADJ);
+            app.mState.setSetAdj(ProcessList.INVALID_ADJ);
+            app.mState.setVerifiedAdj(ProcessList.INVALID_ADJ);
+            mOomAdjuster.setAttachingSchedGroupLSP(app);
+            app.mState.setForcingToImportant(null);
             clearProcessForegroundLocked(app);
+            app.mState.setHasShownUi(false);
+            app.mState.setCached(false);
             app.setDebugging(false);
             app.setKilledByAm(false);
             app.setKilled(false);
@@ -5098,14 +5118,8 @@
                 app.makeActive(thread, mProcessStats);
                 checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
             }
-            app.setPendingFinishAttach(true);
-
             updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
-
-            updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
-            checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
-
             final long now = SystemClock.uptimeMillis();
             synchronized (mAppProfiler.mProfilerLock) {
                 app.mProfile.setLastRequestedGc(now);
@@ -5121,6 +5135,8 @@
 
             if (!mConstants.mEnableWaitForFinishAttachApplication) {
                 finishAttachApplicationInner(startSeq, callingUid, pid);
+            } else {
+                app.setPendingFinishAttach(true);
             }
         } catch (Exception e) {
             // We need kill the process group here. (b/148588589)
@@ -9866,7 +9882,6 @@
         boolean dumpNormalPriority = false;
         boolean dumpVisibleStacksOnly = false;
         boolean dumpFocusedStackOnly = false;
-        boolean dumpVerbose = false;
         int dumpDisplayId = INVALID_DISPLAY;
         String dumpPackage = null;
         int dumpUserId = UserHandle.USER_ALL;
@@ -9925,8 +9940,6 @@
                     return;
                 }
                 dumpClient = true;
-            } else if ("--verbose".equals(opt)) {
-                dumpVerbose = true;
             } else if ("-h".equals(opt)) {
                 ActivityManagerShellCommand.dumpHelp(pw, true);
                 return;
@@ -10213,8 +10226,7 @@
             } else {
                 // Dumping a single activity?
                 if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll,
-                        dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpVerbose, dumpDisplayId,
-                        dumpUserId)) {
+                        dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpDisplayId, dumpUserId)) {
                     ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
                     int res = shell.exec(this, null, fd, null, args, null,
                             new ResultReceiver(null));
@@ -17267,6 +17279,9 @@
         public ComponentName startSdkSandboxService(Intent service, int clientAppUid,
                 String clientAppPackage, String processName) throws RemoteException {
             validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+            if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
+                throw new IllegalArgumentException("uid does not belong to provided package");
+            }
             // TODO(b/269598719): Is passing the application thread of the system_server alright?
             // e.g. the sandbox getting privileged access due to this.
             ComponentName cn = ActivityManagerService.this.startService(
@@ -17333,6 +17348,9 @@
                 String processName, long flags)
                 throws RemoteException {
             validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+            if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
+                throw new IllegalArgumentException("uid does not belong to provided package");
+            }
             if (conn == null) {
                 throw new IllegalArgumentException("connection is null");
             }
@@ -17385,9 +17403,6 @@
             if (!UserHandle.isApp(clientAppUid)) {
                 throw new IllegalArgumentException("uid is not within application range");
             }
-            if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
-                throw new IllegalArgumentException("uid does not belong to provided package");
-            }
         }
 
         @Override
@@ -18141,7 +18156,7 @@
                         | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
                 final Bundle configChangedOptions = new BroadcastOptions()
                         .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
-                        .setDeferUntilActive(true)
+                        .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
                         .toBundle();
                 broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                         null, null, OP_NONE, configChangedOptions, false, false, MY_PID, SYSTEM_UID,
@@ -18160,7 +18175,7 @@
                             PowerExemptionManager.REASON_LOCALE_CHANGED, "");
                     bOptions.setDeliveryGroupPolicy(
                             BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
-                    bOptions.setDeferUntilActive(true);
+                    bOptions.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
                     broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                             null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID,
                             SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(),
@@ -18205,7 +18220,7 @@
 
                 final BroadcastOptions options = new BroadcastOptions()
                         .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
-                        .setDeferUntilActive(true);
+                        .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
                 if (reason != null) {
                     options.setDeliveryGroupMatchingKey(Intent.ACTION_CLOSE_SYSTEM_DIALOGS, reason);
                 }
@@ -18741,6 +18756,12 @@
                 int uid, int pid) {
             ActivityManagerService.this.logFgsApiEnd(apiType, uid, pid);
         }
+
+        @Override
+        public void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+                @MediaProjectionTokenEvent int event) {
+            ActivityManagerService.this.notifyMediaProjectionEvent(uid, projectionToken, event);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
@@ -19974,4 +19995,41 @@
             return invalidValue;
         }
     }
+
+    private void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
+            @MediaProjectionTokenEvent int event) {
+        synchronized (mMediaProjectionTokenMap) {
+            final int index = mMediaProjectionTokenMap.indexOfKey(uid);
+            ArraySet<IBinder> tokens;
+            if (event == MEDIA_PROJECTION_TOKEN_EVENT_CREATED) {
+                if (index < 0) {
+                    tokens = new ArraySet();
+                    mMediaProjectionTokenMap.put(uid, tokens);
+                } else {
+                    tokens = mMediaProjectionTokenMap.valueAt(index);
+                }
+                tokens.add(projectionToken);
+            } else if (event == MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED && index >= 0) {
+                tokens = mMediaProjectionTokenMap.valueAt(index);
+                tokens.remove(projectionToken);
+                if (tokens.isEmpty()) {
+                    mMediaProjectionTokenMap.removeAt(index);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return {@code true} if the MediaProjectionManagerService has created a media projection
+     *         for the given {@code uid} because the user has granted the permission;
+     *         it doesn't necessarily mean it has started the projection.
+     *
+     * <p>It doesn't check the {@link AppOpsManager#OP_PROJECT_MEDIA}.</p>
+     */
+    boolean isAllowedMediaProjectionNoOpCheck(int uid) {
+        synchronized (mMediaProjectionTokenMap) {
+            final int index = mMediaProjectionTokenMap.indexOfKey(uid);
+            return index >= 0 && !mMediaProjectionTokenMap.valueAt(index).isEmpty();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 01bb549..94d08bf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -46,6 +46,7 @@
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.ActivityTaskManager.RootTaskInfo;
@@ -2222,7 +2223,6 @@
         boolean wait = false;
         String opt;
         int displayId = Display.INVALID_DISPLAY;
-        boolean forceInvisible = false;
         while ((opt = getNextOption()) != null) {
             switch(opt) {
                 case "-w":
@@ -2231,27 +2231,31 @@
                 case "--display":
                     displayId = getDisplayIdFromNextArg();
                     break;
-                case "--force-invisible":
-                    forceInvisible = true;
-                    break;
                 default:
                     getErrPrintWriter().println("Error: unknown option: " + opt);
                     return -1;
             }
         }
         final int userId = Integer.parseInt(getNextArgRequired());
-        final boolean callStartProfile = !forceInvisible && isProfile(userId);
         final ProgressWaiter waiter = wait ? new ProgressWaiter(userId) : null;
-        Slogf.d(TAG, "runStartUser(): userId=%d, display=%d, waiter=%s, callStartProfile=%b, "
-                + "forceInvisible=%b", userId,  displayId, waiter, callStartProfile,
-                forceInvisible);
+
+        // For backwards compatibility, if the user is a profile, we need to define whether it
+        // should be started visible (when its parent is the current user) or not (when it isn't)
+        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+        final ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+        final int parentUserId = umi.getProfileParentId(userId);
+        final int currentUserId = ami.getCurrentUserId();
+        final boolean isProfile = parentUserId != userId;
+        final boolean isVisibleProfile = isProfile && parentUserId == currentUserId;
+        Slogf.d(TAG, "runStartUser(): userId=%d, parentUserId=%d, currentUserId=%d, isProfile=%b, "
+                + "isVisibleProfile=%b, display=%d, waiter=%s", userId, parentUserId, currentUserId,
+                isProfile, isVisibleProfile, displayId, waiter);
 
         boolean success;
         String displaySuffix = "";
-
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shell_runStartUser" + userId);
         try {
-            if (callStartProfile) {
+            if (isVisibleProfile) {
                 Slogf.d(TAG, "calling startProfileWithListener(%d, %s)", userId, waiter);
                 // startProfileWithListener() will start the profile visible (as long its parent is
                 // the current user), while startUserInBackgroundWithListener() will always start
@@ -3906,11 +3910,6 @@
         return new Resources(AssetManager.getSystem(), metrics, config);
     }
 
-    private boolean isProfile(@UserIdInt int userId) {
-        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
-        return umi.getProfileParentId(userId) != userId;
-    }
-
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -3957,7 +3956,6 @@
             pw.println("  --checkin: output checkin format, resetting data.");
             pw.println("  --C: output checkin format, not resetting data.");
             pw.println("  --proto: output dump in protocol buffer format.");
-            pw.println("  --verbose: dumps extra information.");
             pw.printf("  %s: dump just the DUMPABLE-related state of an activity. Use the %s "
                     + "option to list the supported DUMPABLEs\n", Activity.DUMP_ARG_DUMP_DUMPABLE,
                     Activity.DUMP_ARG_LIST_DUMPABLES);
@@ -4158,7 +4156,7 @@
             pw.println("      execution of that user if it is currently stopped.");
             pw.println("  get-current-user");
             pw.println("      Returns id of the current foreground user.");
-            pw.println("  start-user [-w] [--display DISPLAY_ID] [--force-invisible] <USER_ID>");
+            pw.println("  start-user [-w] [--display DISPLAY_ID] <USER_ID>");
             pw.println("      Start USER_ID in background if it is currently stopped;");
             pw.println("      use switch-user if you want to start the user in foreground.");
             pw.println("      -w: wait for start-user to complete and the user to be unlocked.");
@@ -4166,10 +4164,6 @@
                     + "which allows the user to launch activities on it.");
             pw.println("        (not supported on all devices; typically only on automotive builds "
                     + "where the vehicle has passenger displays)");
-            pw.println("      --force-invisible: always start the user invisible, even if it's a "
-                    + "profile.");
-            pw.println("        (by default, a profile is visible in the default display when its "
-                    + "parent is the current foreground user)");
             pw.println("  unlock-user <USER_ID>");
             pw.println("      Unlock the given user.  This will only work if the user doesn't");
             pw.println("      have an LSKF (PIN/pattern/password).");
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 81ca267..bef16b6 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -52,7 +52,6 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -224,22 +223,12 @@
     private static final int MSG_DELIVERY_TIMEOUT_HARD = 3;
     private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 4;
     private static final int MSG_CHECK_HEALTH = 5;
-    private static final int MSG_FINISH_RECEIVER = 6;
 
     private void enqueueUpdateRunningList() {
         mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
         mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
     }
 
-    private void enqueueFinishReceiver(@NonNull BroadcastProcessQueue queue,
-            @DeliveryState int deliveryState, @NonNull String reason) {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = queue;
-        args.argi1 = deliveryState;
-        args.arg2 = reason;
-        mLocalHandler.sendMessage(Message.obtain(mLocalHandler, MSG_FINISH_RECEIVER, args));
-    }
-
     private final Handler mLocalHandler;
 
     private final Handler.Callback mLocalCallback = (msg) -> {
@@ -278,17 +267,6 @@
                 }
                 return true;
             }
-            case MSG_FINISH_RECEIVER: {
-                synchronized (mService) {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    final BroadcastProcessQueue queue = (BroadcastProcessQueue) args.arg1;
-                    final int deliveryState = args.argi1;
-                    final String reason = (String) args.arg2;
-                    args.recycle();
-                    finishReceiverActiveLocked(queue, deliveryState, reason);
-                }
-                return true;
-            }
         }
         return false;
     };
@@ -727,7 +705,7 @@
         // Ignore registered receivers from a previous PID
         if (receiver instanceof BroadcastFilter) {
             mRunningColdStart = null;
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED,
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED,
                     "BroadcastFilter for cold app");
             return;
         }
@@ -758,7 +736,7 @@
                 hostingRecord, zygotePolicyFlags, allowWhileBooting, false);
         if (queue.app == null) {
             mRunningColdStart = null;
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
                     "startProcessLocked failed");
             return;
         }
@@ -800,7 +778,7 @@
             @NonNull BroadcastRecord r, int index) {
         final String reason = shouldSkipReceiver(queue, r, index);
         if (reason != null) {
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
             return true;
         }
         return false;
@@ -914,7 +892,7 @@
                     // TODO: consider making registered receivers of unordered
                     // broadcasts report results to detect ANRs
                     if (assumeDelivered) {
-                        enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_DELIVERED,
+                        finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED,
                                 "assuming delivered");
                     }
                 } else {
@@ -932,10 +910,10 @@
                 logw(msg);
                 app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
                         ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
-                enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
+                finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
             }
         } else {
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
                     "missing IApplicationThread");
         }
     }
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 3792625..9ff2cd0 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -31,11 +31,17 @@
     // process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has
     // elapsed, after which the current count for this breakdown will be reset.
     private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.MINUTE_IN_MILLIS;
-    // The time duration after which the rate limit buffer will be cleared.
-    private static final long RATE_LIMIT_BUFFER_EXPIRY = 3 * RATE_LIMIT_BUFFER_DURATION;
+    // Indicated how many buffer durations to wait before the rate limit buffer will be cleared.
+    // E.g. if set to 3 will wait 3xRATE_LIMIT_BUFFER_DURATION before clearing the buffer.
+    private static final long RATE_LIMIT_BUFFER_EXPIRY_FACTOR = 3;
     // The number of entries to keep per breakdown of process/eventType.
     private static final int RATE_LIMIT_ALLOWED_ENTRIES = 6;
 
+    // If a process is rate limited twice in a row we consider it crash-looping and rate limit it
+    // more aggressively.
+    private static final int STRICT_RATE_LIMIT_ALLOWED_ENTRIES = 1;
+    private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 60 * DateUtils.MINUTE_IN_MILLIS;
+
     @GuardedBy("mErrorClusterRecords")
     private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>();
     private final Clock mClock;
@@ -71,15 +77,27 @@
                 return new RateLimitResult(false, 0);
             }
 
-            if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) {
+            final long timeSinceFirstError = now - errRecord.getStartTime();
+            if (timeSinceFirstError > errRecord.getBufferDuration()) {
                 final int errCount = recentlyDroppedCount(errRecord);
                 errRecord.setStartTime(now);
                 errRecord.setCount(1);
+
+                // If this error happened exactly the next "rate limiting cycle" after the last
+                // error and the previous cycle was rate limiting then increment the successive
+                // rate limiting cycle counter. If a full "cycle" has passed since the last error
+                // then this is no longer a continuous occurrence and will be rate limited normally.
+                if (errCount > 0 && timeSinceFirstError < 2 * errRecord.getBufferDuration()) {
+                    errRecord.incrementSuccessiveRateLimitCycles();
+                } else {
+                    errRecord.setSuccessiveRateLimitCycles(0);
+                }
+
                 return new RateLimitResult(false, errCount);
             }
 
             errRecord.incrementCount();
-            if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) {
+            if (errRecord.getCount() > errRecord.getAllowedEntries()) {
                 return new RateLimitResult(true, recentlyDroppedCount(errRecord));
             }
         }
@@ -91,16 +109,19 @@
      * dropped. Resets every RATE_LIMIT_BUFFER_DURATION if events are still actively created or
      * RATE_LIMIT_BUFFER_EXPIRY if not. */
     private int recentlyDroppedCount(ErrorRecord errRecord) {
-        if (errRecord == null || errRecord.getCount() < RATE_LIMIT_ALLOWED_ENTRIES) return 0;
-        return errRecord.getCount() - RATE_LIMIT_ALLOWED_ENTRIES;
+        if (errRecord == null || errRecord.getCount() < errRecord.getAllowedEntries()) return 0;
+        return errRecord.getCount() - errRecord.getAllowedEntries();
     }
 
 
-    private void maybeRemoveExpiredRecords(long now) {
-        if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return;
+    private void maybeRemoveExpiredRecords(long currentTime) {
+        if (currentTime - mLastMapCleanUp
+                <= RATE_LIMIT_BUFFER_EXPIRY_FACTOR * RATE_LIMIT_BUFFER_DURATION) {
+            return;
+        }
 
         for (int i = mErrorClusterRecords.size() - 1; i >= 0; i--) {
-            if (now - mErrorClusterRecords.valueAt(i).getStartTime() > RATE_LIMIT_BUFFER_EXPIRY) {
+            if (mErrorClusterRecords.valueAt(i).hasExpired(currentTime)) {
                 Counter.logIncrement(
                         "stability_errors.value_dropbox_buffer_expired_count",
                         mErrorClusterRecords.valueAt(i).getCount());
@@ -108,7 +129,7 @@
             }
         }
 
-        mLastMapCleanUp = now;
+        mLastMapCleanUp = currentTime;
     }
 
     /** Resets the rate limiter memory. */
@@ -153,10 +174,12 @@
     private class ErrorRecord {
         long mStartTime;
         int mCount;
+        int mSuccessiveRateLimitCycles;
 
         ErrorRecord(long startTime, int count) {
             mStartTime = startTime;
             mCount = count;
+            mSuccessiveRateLimitCycles = 0;
         }
 
         public void setStartTime(long startTime) {
@@ -171,6 +194,14 @@
             mCount++;
         }
 
+        public void setSuccessiveRateLimitCycles(int successiveRateLimitCycles) {
+            mSuccessiveRateLimitCycles = successiveRateLimitCycles;
+        }
+
+        public void incrementSuccessiveRateLimitCycles() {
+            mSuccessiveRateLimitCycles++;
+        }
+
         public long getStartTime() {
             return mStartTime;
         }
@@ -178,6 +209,27 @@
         public int getCount() {
             return mCount;
         }
+
+        public int getSuccessiveRateLimitCycles() {
+            return mSuccessiveRateLimitCycles;
+        }
+
+        public boolean isRepeated() {
+            return mSuccessiveRateLimitCycles >= 2;
+        }
+
+        public int getAllowedEntries() {
+            return isRepeated() ? STRICT_RATE_LIMIT_ALLOWED_ENTRIES : RATE_LIMIT_ALLOWED_ENTRIES;
+        }
+
+        public long getBufferDuration() {
+            return isRepeated() ? STRICT_RATE_LIMIT_BUFFER_DURATION : RATE_LIMIT_BUFFER_DURATION;
+        }
+
+        public boolean hasExpired(long currentTime) {
+            long bufferExpiry = RATE_LIMIT_BUFFER_EXPIRY_FACTOR * getBufferDuration();
+            return currentTime - mStartTime > bufferExpiry;
+        }
     }
 
     private static class DefaultClock implements Clock {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 7121421..a944f6f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1260,19 +1260,12 @@
         for (int i = numLru - 1; i >= 0; i--) {
             ProcessRecord app = lruList.get(i);
             final ProcessStateRecord state = app.mState;
-            if (!app.isKilledByAm() && app.getThread() != null) {
+            if (!app.isKilledByAm() && app.getThread() != null && !app.isPendingFinishAttach()) {
                 // We don't need to apply the update for the process which didn't get computed
                 if (state.getCompletedAdjSeq() == mAdjSeq) {
                     applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason);
                 }
 
-                if (app.isPendingFinishAttach()) {
-                    // Avoid trimming processes that are still initializing. If they aren't
-                    // hosting any components yet because they may be unfairly killed.
-                    // We however apply any computed previously computed oom scores before skipping.
-                    continue;
-                }
-
                 final ProcessServiceRecord psr = app.mServices;
                 // Count the number of process types.
                 switch (state.getCurProcState()) {
@@ -1706,19 +1699,6 @@
             return false;
         }
 
-        if (app.isPendingFinishAttach()) {
-            state.setAdjSeq(mAdjSeq);
-            state.setCompletedAdjSeq(mAdjSeq);
-            // If the process is still initializing, we skip computing any states because we
-            // don't want to override the special states that have been set at
-            // AMS#attachApplication with OomAdjuster#setAttachingProcessStates.
-            // In this limbo state, the app has |PROC_START_TIMEOUT| to finish attach application
-            // and receive updated proc_state based on its importance.
-            // Note that in this state, the oom_score is INVALID_ADJ which is outside the standard
-            // oom score range and the app is safe from lmkd kills.
-            return false;
-        }
-
         state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN);
         state.setAdjSource(null);
         state.setAdjTarget(null);
@@ -3239,7 +3219,7 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    void setAttachingProcessStatesLSP(ProcessRecord app) {
+    void setAttachingSchedGroupLSP(ProcessRecord app) {
         int initialSchedGroup = SCHED_GROUP_DEFAULT;
         final ProcessStateRecord state = app.mState;
         // If the process has been marked as foreground, it is starting as the top app (with
@@ -3259,15 +3239,6 @@
 
         state.setSetSchedGroup(initialSchedGroup);
         state.setCurrentSchedulingGroup(initialSchedGroup);
-        state.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
-        state.setCurCapability(PROCESS_CAPABILITY_NONE);
-
-        state.setCurAdj(ProcessList.FOREGROUND_APP_ADJ);
-        state.setSetAdj(ProcessList.FOREGROUND_APP_ADJ);
-        state.setVerifiedAdj(ProcessList.FOREGROUND_APP_ADJ);
-        state.setForcingToImportant(null);
-        state.setHasShownUi(false);
-        state.setCached(true);
     }
 
     // ONLY used for unit testing in OomAdjusterTests.java
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
index dd06464..a028ae1 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
@@ -32,7 +32,7 @@
  * Trace.traceEnd. These traces are used for performance testing.
  */
 public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServiceInterface {
-    private static final long TRACE_TAG = Trace.TRACE_TAG_SYSTEM_SERVER;
+    private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
     private final AppOpsCheckingServiceInterface mService;
 
     AppOpsCheckingServiceTracingDecorator(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c50e275..490a33e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3928,8 +3928,10 @@
     }
 
     @Override
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     /** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
      * Part of service interface, check permissions and parameters here
      * Note calling package is for logging purposes only, not to be trusted
@@ -4945,8 +4947,10 @@
     }
 
     @Override
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     /**
      * @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
      */
@@ -7214,8 +7218,10 @@
      * @param device the audio device to be affected
      * @param deviceVolumeBehavior one of the device behaviors
      */
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
             @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
         // verify permissions
@@ -7295,8 +7301,11 @@
      * @param device the audio output device type
      * @return the volume behavior for the device
      */
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "QUERY_AUDIO_STATE", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.QUERY_AUDIO_STATE,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     public @AudioManager.DeviceVolumeBehavior
     int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
         // verify permissions
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 12134f7..4e3de3c 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -499,7 +499,8 @@
 
             if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
                 for (ActivityManager.RecentTaskInfo task : recentTasks.getList()) {
-                    if (packageName.equals(task.topActivityInfo.packageName)) {
+                    if (task.topActivityInfo != null && packageName.equals(
+                            task.topActivityInfo.packageName)) {
                         taskInfo = new TaskInfo();
                         taskInfo.frontTaskId = task.taskId;
                         taskInfo.isResizeable =
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1ce917c..6d3f8fd 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,6 +21,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -29,6 +30,7 @@
 import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
 import static android.os.PowerWhitelistManager.REASON_VPN;
 import static android.os.UserHandle.PER_USER_RANGE;
+import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
 
 import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
 import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
@@ -79,7 +81,9 @@
 import android.net.NetworkProvider;
 import android.net.NetworkRequest;
 import android.net.NetworkScore;
+import android.net.NetworkSpecifier;
 import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.UidRangeParcel;
 import android.net.UnderlyingNetworkInfo;
 import android.net.Uri;
@@ -127,12 +131,16 @@
 import android.system.keystore2.Domain;
 import android.system.keystore2.KeyDescriptor;
 import android.system.keystore2.KeyPermission;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.Range;
+import android.util.SparseArray;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -268,6 +276,10 @@
     private final ConnectivityManager mConnectivityManager;
     private final AppOpsManager mAppOpsManager;
     private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
+    private final TelephonyManager mTelephonyManager;
+    private final CarrierConfigManager mCarrierConfigManager;
+    private final SubscriptionManager mSubscriptionManager;
+
     // The context is for specific user which is created from mUserId
     private final Context mUserIdContext;
     @VisibleForTesting final Dependencies mDeps;
@@ -314,6 +326,14 @@
     private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS);
 
     /**
+     * Cached Map of <subscription ID, keepalive delay ms> since retrieving the PersistableBundle
+     * and the target value from CarrierConfigManager is somewhat expensive as it has hundreds of
+     * fields. This cache is cleared when the carrier config changes to ensure data freshness.
+     */
+    @GuardedBy("this")
+    private final SparseArray<Integer> mCachedKeepalivePerSubId = new SparseArray<>();
+
+    /**
      * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
      * only applies to {@link VpnService} connections.
      */
@@ -626,6 +646,10 @@
         mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
         mConnectivityDiagnosticsManager =
                 mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+        mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+
         mDeps = deps;
         mNms = netService;
         mNetd = netd;
@@ -2893,6 +2917,24 @@
          */
         private int mRetryCount = 0;
 
+        private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+                new CarrierConfigManager.CarrierConfigChangeListener() {
+                    @Override
+                    public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+                            int specificCarrierId) {
+                        synchronized (Vpn.this) {
+                            mCachedKeepalivePerSubId.remove(subId);
+
+                            // Ignore stale runner.
+                            if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
+
+                            maybeMigrateIkeSession(mActiveNetwork);
+                        }
+                        // TODO: update the longLivedTcpConnectionsExpensive value in the
+                        //  networkcapabilities of the VPN network.
+                    }
+        };
+
         IkeV2VpnRunner(
                 @NonNull Ikev2VpnProfile profile, @NonNull ScheduledThreadPoolExecutor executor) {
             super(TAG);
@@ -2918,6 +2960,9 @@
             setVpnNetworkPreference(mSessionKey,
                     createUserAndRestrictedProfilesRanges(mUserId,
                             mConfig.allowedApplications, mConfig.disallowedApplications));
+
+            mCarrierConfigManager.registerCarrierConfigChangeListener(mExecutor,
+                    mCarrierConfigChangeListener);
         }
 
         @Override
@@ -3257,8 +3302,6 @@
             mUnderlyingLinkProperties = null;
             mUnderlyingNetworkCapabilities = null;
             mRetryCount = 0;
-
-            startOrMigrateIkeSession(network);
         }
 
         @NonNull
@@ -3356,9 +3399,36 @@
         }
 
         private int guessNattKeepaliveTimerForNetwork() {
-            // TODO : guess the keepalive delay based on carrier if auto keepalive timer is
-            // enabled
-            return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+            final int subId = getCellSubIdForNetworkCapabilities(mUnderlyingNetworkCapabilities);
+            if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                Log.d(TAG, "Underlying network is not a cellular network");
+                return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+            }
+
+            synchronized (Vpn.this) {
+                if (mCachedKeepalivePerSubId.contains(subId)) {
+                    Log.d(TAG, "Get cached keepalive config");
+                    return mCachedKeepalivePerSubId.get(subId);
+                }
+
+                final TelephonyManager perSubTm = mTelephonyManager.createForSubscriptionId(subId);
+                if (perSubTm.getSimApplicationState() != TelephonyManager.SIM_STATE_LOADED) {
+                    Log.d(TAG, "SIM card is not ready on sub " + subId);
+                    return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+                }
+
+                final PersistableBundle carrierConfig =
+                        mCarrierConfigManager.getConfigForSubId(subId);
+                if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
+                    return AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+                }
+
+                final int natKeepalive =
+                        carrierConfig.getInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT);
+                mCachedKeepalivePerSubId.put(subId, natKeepalive);
+                Log.d(TAG, "Get customized keepalive=" + natKeepalive);
+                return natKeepalive;
+            }
         }
 
         boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) {
@@ -3456,7 +3526,15 @@
 
         /** Called when the NetworkCapabilities of underlying network is changed */
         public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
+            final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities;
             mUnderlyingNetworkCapabilities = nc;
+            if (oldNc == null) {
+                // A new default network is available.
+                startOrMigrateIkeSession(mActiveNetwork);
+            } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
+                // Renew carrierConfig values.
+                maybeMigrateIkeSession(mActiveNetwork);
+            }
         }
 
         /** Called when the LinkProperties of underlying network is changed */
@@ -3812,6 +3890,8 @@
 
             resetIkeState();
 
+            mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+                    mCarrierConfigChangeListener);
             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
             mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
                     mDiagnosticsCallback);
@@ -4804,6 +4884,8 @@
                     pw.println("Reset session scheduled");
                 }
             }
+            pw.println("mCachedKeepalivePerSubId=" + mCachedKeepalivePerSubId);
+
             pw.println("mUnderlyNetworkChanges (most recent first):");
             pw.increaseIndent();
             mUnderlyNetworkChanges.reverseDump(pw);
@@ -4815,4 +4897,19 @@
             pw.decreaseIndent();
         }
     }
+
+    private static int getCellSubIdForNetworkCapabilities(@Nullable NetworkCapabilities nc) {
+        if (nc == null) return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+        if (!nc.hasTransport(TRANSPORT_CELLULAR)) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+
+        final NetworkSpecifier specifier = nc.getNetworkSpecifier();
+        if (specifier instanceof TelephonyNetworkSpecifier) {
+            return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+        }
+
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
 }
diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
index 1bb34ab..f93d9ee 100644
--- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
+++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
@@ -84,7 +84,11 @@
     @Override
     public void notifyThrottling(Temperature temp) {
         @Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
+
         synchronized (mThermalObserverLock) {
+            if (mStatus == currentStatus) {
+                return; // status not changed, skip update
+            }
             mStatus = currentStatus;
             mHandler.post(this::updateVotes);
         }
@@ -188,6 +192,10 @@
             localStatus = mStatus;
             localMap = mThermalThrottlingByDisplay.get(displayId);
         }
+        if (localMap == null) {
+            Slog.d(TAG, "Updating votes, display already removed, display=" + displayId);
+            return;
+        }
         if (mLoggingEnabled) {
             Slog.d(TAG, "Updating votes for status=" + localStatus + ", display =" + displayId
                     + ", map=" + localMap);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java b/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
index dc2799e..6cd2493 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodDeviceConfigs.java
@@ -21,6 +21,8 @@
 import android.app.ActivityThread;
 import android.provider.DeviceConfig;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Class for the device-level configuration related to the input method manager
  * platform features in {@link DeviceConfig}.
@@ -56,4 +58,9 @@
     public boolean shouldHideImeWhenNoEditorFocus() {
         return mHideImeWhenNoEditorFocus;
     }
+
+    @VisibleForTesting
+    void destroy() {
+        DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
+    }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index cc41207..afae08d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -56,7 +56,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DeviceStateCache;
@@ -2514,7 +2514,7 @@
      * Starts a session to verify lock screen credentials provided by a remote device.
      */
     @NonNull
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
         return mRecoverableKeyStoreManager.startRemoteLockscreenValidation(this);
     }
 
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 c08958b..f073756 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,7 +31,7 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.content.Context;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -999,7 +999,7 @@
     /**
      * Starts a session to verify lock screen credentials provided by a remote device.
      */
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation(
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation(
             LockSettingsService lockSettingsService) {
         if (mRemoteLockscreenValidationSessionStorage == null) {
             throw new UnsupportedOperationException("Under development");
@@ -1021,8 +1021,8 @@
         int badGuesses = mDatabase.getBadRemoteGuessCounter(userId);
         int remainingAttempts = Math.max(INVALID_REMOTE_GUESS_LIMIT - badGuesses, 0);
         // TODO(b/254335492): Schedule task to remove inactive session
-        return new StartLockscreenValidationRequest.Builder()
-                .setLockscreenUiType(keyguardCredentialsType)
+        return new RemoteLockscreenValidationSession.Builder()
+                .setLockType(keyguardCredentialsType)
                 .setRemainingAttempts(remainingAttempts)
                 .setSourcePublicKey(encodedPublicKey)
                 .build();
@@ -1046,7 +1046,9 @@
                 .build();
         }
         if (session == null) {
-            throw new IllegalStateException("There is no active lock screen check session");
+            return new RemoteLockscreenValidationResult.Builder()
+                .setResultCode(RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED)
+                .build();
         }
         byte[] decryptedCredentials;
         try {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 77b9abe..4832618 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.media.projection;
 
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
+import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -484,6 +487,9 @@
             userHandle = new UserHandle(UserHandle.getUserId(uid));
             mTargetSdkVersion = targetSdkVersion;
             mIsPrivileged = isPrivileged;
+            // TODO(b/267740338): Add unit test.
+            mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
+                    MEDIA_PROJECTION_TOKEN_EVENT_CREATED);
         }
 
         @Override // Binder call
@@ -630,6 +636,9 @@
                 mToken = null;
                 unregisterCallback(mCallback);
                 mCallback = null;
+                // TODO(b/267740338): Add unit test.
+                mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
+                        MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED);
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS
index 5a19656..6c4dd6d 100644
--- a/services/core/java/com/android/server/notification/OWNERS
+++ b/services/core/java/com/android/server/notification/OWNERS
@@ -1,4 +1,6 @@
-dsandler@android.com
+# Bug component: 34005
+
 juliacr@google.com
-beverlyt@google.com
-pixel@google.com
+yurilin@google.com
+jeffdq@google.com
+dsandler@android.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 20cb485..50f88d3 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1645,7 +1645,7 @@
             return false;
         }
         int uid = injectGetPackageUid(systemChooser.getPackageName(), UserHandle.USER_SYSTEM);
-        return uid == callingUid;
+        return UserHandle.getAppId(uid) == UserHandle.getAppId(callingUid);
     }
 
     private void enforceSystemOrShell() {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2f98d34..3343172 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3504,15 +3504,15 @@
                     return;
                 }
                 final int oldMainUserId = getMainUserIdUnchecked();
-                final int oldFlags = systemUserData.info.flags;
-                final int newFlags;
+                final int oldSysFlags = systemUserData.info.flags;
+                final int newSysFlags;
                 final String newUserType;
                 if (newHeadlessSystemUserMode) {
                     newUserType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
-                    newFlags = oldFlags & ~UserInfo.FLAG_FULL & ~UserInfo.FLAG_MAIN;
+                    newSysFlags = oldSysFlags & ~UserInfo.FLAG_FULL & ~UserInfo.FLAG_MAIN;
                 } else {
                     newUserType = UserManager.USER_TYPE_FULL_SYSTEM;
-                    newFlags = oldFlags | UserInfo.FLAG_FULL;
+                    newSysFlags = oldSysFlags | UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN;
                 }
 
                 if (systemUserData.info.userType.equals(newUserType)) {
@@ -3523,18 +3523,19 @@
                 Slogf.i(LOG_TAG, "Persisting emulated system user data: type changed from %s to "
                         + "%s, flags changed from %s to %s",
                         systemUserData.info.userType, newUserType,
-                        UserInfo.flagsToString(oldFlags), UserInfo.flagsToString(newFlags));
+                        UserInfo.flagsToString(oldSysFlags), UserInfo.flagsToString(newSysFlags));
 
                 systemUserData.info.userType = newUserType;
-                systemUserData.info.flags = newFlags;
+                systemUserData.info.flags = newSysFlags;
                 writeUserLP(systemUserData);
 
-                // Switch the MainUser to a reasonable choice if needed.
-                // (But if there was no MainUser, we deliberately continue to have no MainUser.)
+                // Designate the MainUser to a reasonable choice if needed.
                 final UserData oldMain = getUserDataNoChecks(oldMainUserId);
                 if (newHeadlessSystemUserMode) {
-                    if (oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) != 0) {
-                        // System was MainUser. So we need a new choice for Main. Pick the oldest.
+                    final boolean mainIsAlreadyNonSystem =
+                            oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) == 0;
+                    if (!mainIsAlreadyNonSystem && isMainUserPermanentAdmin()) {
+                        // We need a new choice for Main. Pick the oldest.
                         // If no oldest, don't set any. Let the BootUserInitializer do that later.
                         final UserInfo newMainUser = getEarliestCreatedFullUser();
                         if (newMainUser != null) {
@@ -3544,16 +3545,16 @@
                         }
                     }
                 } else {
+                    // We already made user 0 Main above. Now strip it from the old Main user.
                     // TODO(b/256624031): For now, we demand the Main user (if there is one) is
                     //  always the system in non-HSUM. In the future, when we relax this, change how
                     //  we handle MAIN.
                     if (oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) == 0) {
-                        // Someone else was the MainUser; transfer it to System.
                         Slogf.i(LOG_TAG, "Transferring Main to user 0 from " + oldMain.info.id);
                         oldMain.info.flags &= ~UserInfo.FLAG_MAIN;
-                        systemUserData.info.flags |= UserInfo.FLAG_MAIN;
                         writeUserLP(oldMain);
-                        writeUserLP(systemUserData);
+                    } else {
+                        Slogf.i(LOG_TAG, "Designated user 0 to be Main");
                     }
                 }
             }
@@ -3817,12 +3818,14 @@
         if (userVersion < 11) {
             // Add FLAG_MAIN
             if (isHeadlessSystemUserMode()) {
-                final UserInfo earliestCreatedUser = getEarliestCreatedFullUser();
-                if (earliestCreatedUser != null) {
-                    earliestCreatedUser.flags |= UserInfo.FLAG_MAIN;
-                    userIdsToWrite.add(earliestCreatedUser.id);
+                if (isMainUserPermanentAdmin()) {
+                    final UserInfo earliestCreatedUser = getEarliestCreatedFullUser();
+                    if (earliestCreatedUser != null) {
+                        earliestCreatedUser.flags |= UserInfo.FLAG_MAIN;
+                        userIdsToWrite.add(earliestCreatedUser.id);
+                    }
                 }
-            } else {
+            } else { // not isHeadlessSystemUserMode
                 synchronized (mUsersLock) {
                     final UserData userData = mUsers.get(UserHandle.USER_SYSTEM);
                     userData.info.flags |= UserInfo.FLAG_MAIN;
diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
index 98b24ea..333c98c 100644
--- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
+++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
@@ -380,6 +380,7 @@
             final int pid = Process.myPid();
             Slogf.i(LOG_TAG, "Restarting Android runtime(PID=%d) to finalize changes", pid);
             pw.println("Restarting Android runtime to finalize changes");
+            pw.println("The restart may trigger a 'Broken pipe' message; this is to be expected.");
             pw.flush();
 
             // Ideally there should be a cleaner / safer option to restart system_server, but
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index f87f50a..a8615c2 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -108,7 +108,7 @@
     private final Object mLock = new Object();
 
     private final boolean mVisibleBackgroundUsersEnabled;
-    private final boolean mVisibleBackgroundUserOnDefaultDisplayAllowed;
+    private final boolean mVisibleBackgroundUserOnDefaultDisplayEnabled;
 
     @UserIdInt
     @GuardedBy("mLock")
@@ -168,11 +168,17 @@
     }
 
     @VisibleForTesting
-    UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled,
-            boolean visibleBackgroundUserOnDefaultDisplayAllowed, Handler handler) {
-        mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled;
-        mVisibleBackgroundUserOnDefaultDisplayAllowed =
-                visibleBackgroundUserOnDefaultDisplayAllowed;
+    UserVisibilityMediator(boolean visibleBackgroundUsersOnDisplaysEnabled,
+            boolean visibleBackgroundUserOnDefaultDisplayEnabled, Handler handler) {
+        mVisibleBackgroundUsersEnabled = visibleBackgroundUsersOnDisplaysEnabled;
+        if (visibleBackgroundUserOnDefaultDisplayEnabled
+                && !visibleBackgroundUsersOnDisplaysEnabled) {
+            throw new IllegalArgumentException("Cannot have "
+                    + "visibleBackgroundUserOnDefaultDisplayEnabled without "
+                    + "visibleBackgroundUsersOnDisplaysEnabled");
+        }
+        mVisibleBackgroundUserOnDefaultDisplayEnabled =
+                visibleBackgroundUserOnDefaultDisplayEnabled;
         if (mVisibleBackgroundUsersEnabled) {
             mUsersAssignedToDisplayOnStart = new SparseIntArray();
             mExtraDisplaysAssignedToUsers = new SparseIntArray();
@@ -318,14 +324,14 @@
 
         boolean visibleBackground = userStartMode == USER_START_MODE_BACKGROUND_VISIBLE;
         if (displayId == DEFAULT_DISPLAY && visibleBackground) {
-            if (mVisibleBackgroundUserOnDefaultDisplayAllowed && isCurrentUserLocked(userId)) {
+            if (mVisibleBackgroundUserOnDefaultDisplayEnabled && isCurrentUserLocked(userId)) {
                 // Shouldn't happen - UserController returns before calling this method
                 Slogf.wtf(TAG, "trying to start current user (%d) visible in background on default"
                         + " display", userId);
                 return USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
 
             }
-            if (!mVisibleBackgroundUserOnDefaultDisplayAllowed
+            if (!mVisibleBackgroundUserOnDefaultDisplayEnabled
                     && !isProfile(userId, profileGroupId)) {
                 Slogf.wtf(TAG, "cannot start full user (%d) visible on default display", userId);
                 return USER_ASSIGNMENT_RESULT_FAILURE;
@@ -383,7 +389,7 @@
         }
 
         return foreground || displayId != DEFAULT_DISPLAY
-                || (visibleBackground && mVisibleBackgroundUserOnDefaultDisplayAllowed)
+                || (visibleBackground && mVisibleBackgroundUserOnDefaultDisplayEnabled)
                         ? USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE
                         : USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
     }
@@ -394,7 +400,7 @@
             @UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId) {
         if (displayId == DEFAULT_DISPLAY) {
             boolean mappingNeeded = false;
-            if (mVisibleBackgroundUserOnDefaultDisplayAllowed
+            if (mVisibleBackgroundUserOnDefaultDisplayEnabled
                     && userStartMode == USER_START_MODE_BACKGROUND_VISIBLE) {
                 int userStartedOnDefaultDisplay = getUserStartedOnDisplay(DEFAULT_DISPLAY);
                 if (userStartedOnDefaultDisplay != USER_NULL
@@ -752,7 +758,7 @@
      */
     public int getDisplayAssignedToUser(@UserIdInt int userId) {
         if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
-            if (mVisibleBackgroundUserOnDefaultDisplayAllowed) {
+            if (mVisibleBackgroundUserOnDefaultDisplayEnabled) {
                 // When device supports visible bg users on default display, the default display is
                 // assigned to the current user, unless a user is started visible on it
                 int userStartedOnDefaultDisplay;
@@ -801,7 +807,7 @@
     private @UserIdInt int getUserAssignedToDisplay(@UserIdInt int displayId,
             boolean returnCurrentUserByDefault) {
         if (returnCurrentUserByDefault
-                && ((displayId == DEFAULT_DISPLAY && !mVisibleBackgroundUserOnDefaultDisplayAllowed
+                && ((displayId == DEFAULT_DISPLAY && !mVisibleBackgroundUserOnDefaultDisplayEnabled
                 || !mVisibleBackgroundUsersEnabled))) {
             return getCurrentUserId();
         }
@@ -961,8 +967,8 @@
             ipw.print("Supports visible background users on displays: ");
             ipw.println(mVisibleBackgroundUsersEnabled);
 
-            ipw.print("Allows visible background users on default display: ");
-            ipw.println(mVisibleBackgroundUserOnDefaultDisplayAllowed);
+            ipw.print("Supports visible background users on default display: ");
+            ipw.println(mVisibleBackgroundUserOnDefaultDisplayEnabled);
 
             dumpSparseIntArray(ipw, mUsersAssignedToDisplayOnStart, "user / display", "u", "d");
             dumpSparseIntArray(ipw, mExtraDisplaysAssignedToUsers, "extra display / user",
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 85f1357..646dc4e 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
 import com.android.internal.policy.IKeyguardService;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.EventLogTags;
 
 import java.io.PrintWriter;
 
@@ -255,6 +256,11 @@
     public void setOccluded(boolean isOccluded, boolean notify) {
         if (mKeyguardService != null && notify) {
             if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
+            EventLogTags.writeWmSetKeyguardOccluded(
+                    isOccluded ? 1 : 0,
+                    0 /* animate */,
+                    0 /* transit */,
+                    "setOccluded");
             mKeyguardService.setOccluded(isOccluded, false /* animate */);
         }
         mKeyguardState.occluded = isOccluded;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index c6684df..5442b6d 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2125,7 +2125,7 @@
         }
 
         @Override
-        public void notifyTvMessage(IBinder sessionToken, String type, Bundle data, int userId) {
+        public void notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "timeShiftEnablePositionTracking");
@@ -2590,7 +2590,7 @@
         }
 
         @Override
-        public void notifyAdBuffer(
+        public void notifyAdBufferReady(
                 IBinder sessionToken, AdBuffer buffer, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
@@ -2602,7 +2602,7 @@
                     try {
                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
-                        getSessionLocked(sessionState).notifyAdBuffer(buffer);
+                        getSessionLocked(sessionState).notifyAdBufferReady(buffer);
                     } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in notifyAdBuffer", e);
                     }
@@ -3765,7 +3765,7 @@
         }
 
         @Override
-        public void onTvMessage(String type, Bundle data) {
+        public void onTvMessage(int type, Bundle data) {
             synchronized (mLock) {
                 if (DEBUG) {
                     Slog.d(TAG, "onTvMessage(type=" + type + ", data=" + data + ")");
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 61c137e..0d4a76e 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -1117,7 +1117,7 @@
         }
 
         @Override
-        public void notifyTvMessage(IBinder sessionToken, String type, Bundle data, int userId) {
+        public void notifyTvMessage(IBinder sessionToken, int type, Bundle data, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
@@ -3047,16 +3047,16 @@
         }
 
         @Override
-        public void onAdBuffer(AdBuffer buffer) {
+        public void onAdBufferReady(AdBuffer buffer) {
             synchronized (mLock) {
                 if (DEBUG) {
-                    Slogf.d(TAG, "onAdBuffer(buffer=" + buffer + ")");
+                    Slogf.d(TAG, "onAdBufferReady(buffer=" + buffer + ")");
                 }
                 if (mSessionState.mSession == null || mSessionState.mClient == null) {
                     return;
                 }
                 try {
-                    mSessionState.mClient.onAdBuffer(buffer, mSessionState.mSeq);
+                    mSessionState.mClient.onAdBufferReady(buffer, mSessionState.mSeq);
                 } catch (RemoteException e) {
                     Slogf.e(TAG, "error in onAdBuffer", e);
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 28b974c..faa0cc9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1734,7 +1734,7 @@
                 : null;
     }
 
-    private void clearLastParentBeforePip() {
+    void clearLastParentBeforePip() {
         if (mLastParentBeforePip != null) {
             mLastParentBeforePip.mChildPipActivity = null;
             mLastParentBeforePip = null;
@@ -2098,7 +2098,7 @@
         mTaskSupervisor = supervisor;
 
         info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid,
-                launchMode);
+                info.launchMode, mActivityComponent);
         taskAffinity = info.taskAffinity;
         final String uid = Integer.toString(info.applicationInfo.uid);
         if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
@@ -2199,12 +2199,18 @@
      * @param affinity The affinity of the activity.
      * @param uid The user-ID that has been assigned to this application.
      * @param launchMode The activity launch mode
+     * @param componentName The activity component name. This is only useful when the given
+     *                      launchMode is {@link ActivityInfo#LAUNCH_SINGLE_INSTANCE}
      * @return The task affinity
      */
-    static String computeTaskAffinity(String affinity, int uid, int launchMode) {
+    static String computeTaskAffinity(String affinity, int uid, int launchMode,
+            ComponentName componentName) {
         final String uidStr = Integer.toString(uid);
         if (affinity != null && !affinity.startsWith(uidStr)) {
-            affinity = uidStr + (launchMode == LAUNCH_SINGLE_INSTANCE ? "-si:" : ":") + affinity;
+            affinity = uidStr + ":" + affinity;
+            if (launchMode == LAUNCH_SINGLE_INSTANCE && componentName != null) {
+                affinity += ":" + componentName.hashCode();
+            }
         }
         return affinity;
     }
@@ -7824,25 +7830,35 @@
 
     /**
      * Returns the requested {@link Configuration.Orientation} for the current activity.
-     *
-     * <p>When The current orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns the
-     * requested orientation for the activity below which is the first activity with an explicit
-     * (different from {@link SCREEN_ORIENTATION_UNSET}) orientation which is not {@link
-     * SCREEN_ORIENTATION_BEHIND}.
      */
     @Configuration.Orientation
     @Override
     int getRequestedConfigurationOrientation(boolean forDisplay) {
+        return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+    }
+
+    /**
+     * Returns the requested {@link Configuration.Orientation} for the requested
+     * {@link ActivityInfo.ScreenOrientation}.
+     *
+     * <p>When the current screen orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns
+     * the requested orientation for the activity below which is the first activity with an explicit
+     * (different from {@link SCREEN_ORIENTATION_UNSET}) orientation which is not {@link
+     * SCREEN_ORIENTATION_BEHIND}.
+     */
+    @Configuration.Orientation
+    int getRequestedConfigurationOrientation(boolean forDisplay,
+            @ActivityInfo.ScreenOrientation int requestedOrientation) {
         if (mLetterboxUiController.hasInheritedOrientation()) {
             final RootDisplayArea root = getRootDisplayArea();
             if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
-                return ActivityInfo.reverseOrientation(
+                return reverseConfigurationOrientation(
                         mLetterboxUiController.getInheritedOrientation());
             } else {
                 return mLetterboxUiController.getInheritedOrientation();
             }
         }
-        if (task != null && getOverrideOrientation() == SCREEN_ORIENTATION_BEHIND) {
+        if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) {
             // We use Task here because we want to be consistent with what happens in
             // multi-window mode where other tasks orientations are ignored.
             final ActivityRecord belowCandidate = task.getActivity(
@@ -7853,7 +7869,23 @@
                 return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
             }
         }
-        return super.getRequestedConfigurationOrientation(forDisplay);
+        return super.getRequestedConfigurationOrientation(forDisplay, requestedOrientation);
+    }
+
+    /**
+     * Returns the reversed configuration orientation.
+     * @hide
+     */
+    @Configuration.Orientation
+    public static int reverseConfigurationOrientation(@Configuration.Orientation int orientation) {
+        switch (orientation) {
+            case ORIENTATION_LANDSCAPE:
+                return ORIENTATION_PORTRAIT;
+            case ORIENTATION_PORTRAIT:
+                return ORIENTATION_LANDSCAPE;
+            default:
+                return orientation;
+        }
     }
 
     /**
@@ -7899,6 +7931,19 @@
         if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
             return;
         }
+        // This is necessary in order to avoid going into size compat mode when the orientation
+        // change request comes from the app
+        if (mWmService.mLetterboxConfiguration
+                    .isSizeCompatModeDisabledAfterOrientationChangeFromApp()
+                && getRequestedConfigurationOrientation(false, requestedOrientation)
+                    != getRequestedConfigurationOrientation(false /*forDisplay */)) {
+            // Do not change the requested configuration now, because this will be done when setting
+            // the orientation below with the new mCompatDisplayInsets
+            clearSizeCompatModeAttributes();
+        }
+        ProtoLog.v(WM_DEBUG_ORIENTATION,
+                "Setting requested orientation %s for %s",
+                ActivityInfo.screenOrientationToString(requestedOrientation), this);
         setOrientation(requestedOrientation, this);
 
         // Push the new configuration to the requested app in case where it's not pushed, e.g. when
@@ -8139,17 +8184,20 @@
                         mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
     }
 
-    @VisibleForTesting
-    void clearSizeCompatMode() {
-        final float lastSizeCompatScale = mSizeCompatScale;
+    private void clearSizeCompatModeAttributes() {
         mInSizeCompatModeForBounds = false;
         mSizeCompatScale = 1f;
         mSizeCompatBounds = null;
         mCompatDisplayInsets = null;
+    }
+
+    @VisibleForTesting
+    void clearSizeCompatMode() {
+        final float lastSizeCompatScale = mSizeCompatScale;
+        clearSizeCompatModeAttributes();
         if (mSizeCompatScale != lastSizeCompatScale) {
             forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
         }
-
         // Clear config override in #updateCompatDisplayInsets().
         final int activityType = getActivityType();
         final Configuration overrideConfig = getRequestedOverrideConfiguration();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 5ea2854..bfb735d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -490,8 +490,7 @@
     /** Dump the current activities state. */
     public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
             String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
-            boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
-            @UserIdInt int userId);
+            boolean dumpFocusedRootTaskOnly, int displayIdFilter, @UserIdInt int userId);
 
     /** Dump the current state for inclusion in oom dump. */
     public abstract void dumpForOom(PrintWriter pw);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 898b477..992743a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4205,8 +4205,7 @@
      */
     protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
             int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
-            boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
-            @UserIdInt int userId) {
+            boolean dumpFocusedRootTaskOnly, int displayIdFilter, @UserIdInt int userId) {
         ArrayList<ActivityRecord> activities;
 
         synchronized (mGlobalLock) {
@@ -4251,7 +4250,7 @@
                     }
                 }
             }
-            dumpActivity("  ", fd, pw, activities.get(i), newArgs, dumpAll, dumpVerbose);
+            dumpActivity("  ", fd, pw, activities.get(i), newArgs, dumpAll);
         }
         if (!printedAnything) {
             // Typically happpens when no task matches displayIdFilter
@@ -4265,7 +4264,7 @@
      * there is a thread associated with the activity.
      */
     private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
-            ActivityRecord r, String[] args, boolean dumpAll, boolean dumpVerbose) {
+            ActivityRecord r, String[] args, boolean dumpAll) {
         String innerPrefix = prefix + "  ";
         IApplicationThread appThread = null;
         synchronized (mGlobalLock) {
@@ -4281,15 +4280,11 @@
             } else {
                 pw.print("(not running)");
             }
-            if (dumpVerbose) {
-                pw.print(" userId=");
-                pw.print(r.mUserId);
-                pw.print(" uid=");
-                pw.print(r.getUid());
-                printDisplayInfoAndNewLine(pw, r);
-            } else {
-                pw.println();
-            }
+            pw.print(" userId=");
+            pw.print(r.mUserId);
+            pw.print(" uid=");
+            pw.print(r.getUid());
+            printDisplayInfoAndNewLine(pw, r);
             if (dumpAll) {
                 r.dump(pw, innerPrefix, /* dumpAll= */ true);
             }
@@ -6618,11 +6613,10 @@
         @Override
         public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
                 String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly,
-                boolean dumpFocusedRootTaskOnly, boolean dumpVerbose, int displayIdFilter,
+                boolean dumpFocusedRootTaskOnly, int displayIdFilter,
                 @UserIdInt int userId) {
             return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti, dumpAll,
-                    dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, dumpVerbose, displayIdFilter,
-                    userId);
+                    dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, displayIdFilter, userId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index 5e44d6c..e91c9d4 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -41,7 +41,8 @@
 
     // Desktop mode feature flag.
     static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
-            "persist.wm.debug.desktop_mode", false);
+            "persist.wm.debug.desktop_mode", false) || SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_mode_2", false);
     // Override default freeform task width when desktop mode is enabled. In dips.
     private static final int DESKTOP_MODE_DEFAULT_WIDTH_DP = SystemProperties.getInt(
             "persist.wm.debug.desktop_mode.default_width", 840);
@@ -79,8 +80,8 @@
             appendLog("not in bounds phase, skipping");
             return RESULT_SKIP;
         }
-        if (!task.inFreeformWindowingMode()) {
-            appendLog("not a freeform task, skipping");
+        if (!task.isActivityTypeStandard()) {
+            appendLog("not standard activity type, skipping");
             return RESULT_SKIP;
         }
         if (!currentParams.mBounds.isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 594929b..244656c 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -49,7 +49,7 @@
 30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
 
 # Keyguard status changed
-30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(Reason|3)
+30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(occluded|1),(Reason|3)
 
 # Out of memory for surfaces.
 31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
@@ -65,6 +65,8 @@
 
 # bootanim finished:
 31007 wm_boot_animation_done (time|2|3)
+# Notify keyguard occlude statuc change to SysUI.
+31008 wm_set_keyguard_occluded (occluded|1),(animate|1),(transit|1),(Channel|3)
 
 # Back navigation.
 31100 wm_back_navi_canceled (Reason|3)
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 32b3ccf..08a6358 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -188,6 +188,7 @@
                 keyguardShowing ? 1 : 0,
                 aodShowing ? 1 : 0,
                 state.mKeyguardGoingAway ? 1 : 0,
+                state.mOccluded ? 1 : 0,
                 "setKeyguardShown");
 
         // Update the task snapshot if the screen will not be turned off. To make sure that the
@@ -253,9 +254,10 @@
         try {
             EventLogTags.writeWmSetKeyguardShown(
                     displayId,
-                    1 /* keyguardShowing */,
+                    state.mKeyguardShowing ? 1 : 0,
                     state.mAodShowing ? 1 : 0,
                     1 /* keyguardGoingAway */,
+                    state.mOccluded ? 1 : 0,
                     "keyguardGoingAway");
             final int transitFlags = convertTransitFlags(flags);
             final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
@@ -669,6 +671,15 @@
 
             boolean hasChange = false;
             if (lastOccluded != mOccluded) {
+                if (mDisplayId == DEFAULT_DISPLAY) {
+                    EventLogTags.writeWmSetKeyguardShown(
+                            mDisplayId,
+                            mKeyguardShowing ? 1 : 0,
+                            mAodShowing ? 1 : 0,
+                            mKeyguardGoingAway ? 1 : 0,
+                            mOccluded ? 1 : 0,
+                            "updateVisibility");
+                }
                 controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
                 hasChange = true;
             } else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index fa49a6ba..37cf5bc 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -19,6 +19,7 @@
 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.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST;
+import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
@@ -314,6 +315,10 @@
         mDeviceConfig.updateFlagActiveStatus(
                 /* isActive */ mTranslucentLetterboxingEnabled,
                 /* key */ KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY);
+        mDeviceConfig.updateFlagActiveStatus(
+                /* isActive */ true,
+                /* key */ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+
         mLetterboxConfigurationPersister = letterboxConfigurationPersister;
         mLetterboxConfigurationPersister.start();
     }
@@ -327,6 +332,16 @@
     }
 
     /**
+     * Whether size compat mode is disabled after an orientation change request comes from the app.
+     * This value is controlled via {@link android.provider.DeviceConfig}.
+     */
+    // TODO(b/270356567) Clean up this flag
+    boolean isSizeCompatModeDisabledAfterOrientationChangeFromApp() {
+        return mDeviceConfig.getFlag(
+                KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+    }
+
+    /**
      * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
      * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
      * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
index df3c8f0..1651af3 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
@@ -20,7 +20,6 @@
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
 
-
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Map;
@@ -53,6 +52,11 @@
 
     private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true;
 
+    static final String KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP =
+            "disable_size_compat_mode_after_orientation_change_from_app";
+    private static final boolean
+            DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP = true;
+
     @VisibleForTesting
     static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
             KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
@@ -64,7 +68,9 @@
             KEY_ENABLE_COMPAT_FAKE_FOCUS,
             DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS,
             KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
-            DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY
+            DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
+            KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP,
+            DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP
     );
 
     // Whether camera compatibility treatment is enabled.
@@ -93,6 +99,10 @@
     private boolean mIsTranslucentLetterboxingAllowed =
             DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY;
 
+    // Whether size compat mode is disabled after an orientation change request comes from the app
+    private boolean mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+            DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
+
     // Set of active device configs that need to be updated in
     // DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged.
     private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>();
@@ -142,6 +152,8 @@
                 return mIsCompatFakeFocusAllowed;
             case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
                 return mIsTranslucentLetterboxingAllowed;
+            case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+                return mIsSizeCompatModeDisabledAfterOrientationChangeFromApp;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
@@ -169,6 +181,10 @@
             case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
                 mIsTranslucentLetterboxingAllowed = getDeviceConfig(key, defaultValue);
                 break;
+            case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+                mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+                        getDeviceConfig(key, defaultValue);
+                break;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 7208934..f355f08 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1395,9 +1395,9 @@
                 FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
                 mActivityRecord /* boundary */, false /* includeBoundary */,
                 true /* traverseTopToBottom */);
-        if (firstOpaqueActivityBeneath == null) {
+        if (firstOpaqueActivityBeneath == null || firstOpaqueActivityBeneath.isEmbedded()) {
             // We skip letterboxing if the translucent activity doesn't have any opaque
-            // activities beneath
+            // activities beneath or the activity below is embedded which never has letterbox.
             return;
         }
         inheritConfiguration(firstOpaqueActivityBeneath);
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 14b845c..cb94146 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -37,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
@@ -1221,7 +1222,7 @@
     void onActivityIdle(ActivityRecord r) {
         // Clean up the hidden tasks when going to home because the user may not be unable to return
         // to the task from recents.
-        if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome()) {
+        if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome() && r.isState(RESUMED)) {
             removeUnreachableHiddenTasks(r.getWindowingMode());
         }
         if (mCheckTrimmableTasksOnIdle) {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 3635ebb..b879d1a 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -195,6 +197,13 @@
                                 + " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
                         AppTransition.appTransitionOldToString(transit), appTargets.length,
                         wallpaperTargets.length, nonAppTargets.length);
+                if (AppTransition.isKeyguardOccludeTransitOld(transit)) {
+                    EventLogTags.writeWmSetKeyguardOccluded(
+                            transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE ? 0 : 1,
+                            1 /* animate */,
+                            transit,
+                            "onAnimationStart");
+                }
                 mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
                         wallpaperTargets, nonAppTargets, mFinishedCallback);
             } catch (RemoteException e) {
@@ -356,6 +365,11 @@
         final boolean isKeyguardOccluded = mDisplayContent.isKeyguardOccluded();
 
         try {
+            EventLogTags.writeWmSetKeyguardOccluded(
+                    isKeyguardOccluded ? 1 : 0,
+                    0 /* animate */,
+                    0 /* transit */,
+                    "onAnimationCancelled");
             mRemoteAnimationAdapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to notify cancel", e);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3680e6d..ebdc537 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2584,6 +2584,9 @@
 
         EventLogTags.writeWmTaskRemoved(mTaskId, getRootTaskId(), getDisplayId(), reason);
         clearPinnedTaskIfNeed();
+        if (mChildPipActivity != null) {
+            mChildPipActivity.clearLastParentBeforePip();
+        }
         // If applicable let the TaskOrganizer know the Task is vanishing.
         setTaskOrganizer(null);
 
@@ -5329,7 +5332,7 @@
         // the task if the affinity has changed.
 
         final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid(),
-                srec.launchMode);
+                srec.launchMode, srec.mActivityComponent);
         if (srec == null || srec.getTask().affinity == null
                 || !srec.getTask().affinity.equals(affinity)) {
             return true;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 132f5a7..d42a629 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1493,7 +1493,26 @@
      */
     @Configuration.Orientation
     int getRequestedConfigurationOrientation(boolean forDisplay) {
-        int requestedOrientation = getOverrideOrientation();
+        return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+    }
+
+    /**
+     * Gets the configuration orientation by the requested screen orientation
+     *
+     * @param forDisplay whether it is the requested config orientation for display.
+     *                   If {@code true}, we may reverse the requested orientation if the root is
+     *                   different from the display, so that when the display rotates to the
+     *                   reversed orientation, the requested app will be in the requested
+     *                   orientation.
+     * @param requestedOrientation the screen orientation({@link ScreenOrientation}) that is
+     *                   requested
+     * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+     *         {@link Configuration#ORIENTATION_PORTRAIT},
+     *         {@link Configuration#ORIENTATION_UNDEFINED}).
+     */
+    @Configuration.Orientation
+    int getRequestedConfigurationOrientation(boolean forDisplay,
+            @ScreenOrientation int requestedOrientation) {
         final RootDisplayArea root = getRootDisplayArea();
         if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
             // Reverse the requested orientation if the orientation of its root is different from
@@ -1503,7 +1522,7 @@
             // (portrait).
             // When an app below the DAG is requesting landscape, it should actually request the
             // display to be portrait, so that the DAG and the app will be in landscape.
-            requestedOrientation = reverseOrientation(getOverrideOrientation());
+            requestedOrientation = reverseOrientation(requestedOrientation);
         }
 
         if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf0fc09..f86b997 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4523,7 +4523,7 @@
 
     void requestUpdateWallpaperIfNeeded() {
         final DisplayContent dc = getDisplayContent();
-        if (dc != null && hasWallpaper()) {
+        if (dc != null && ((mIsWallpaper && !mLastConfigReportedToClient) || hasWallpaper())) {
             dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
             dc.setLayoutNeeded();
             mWmService.mWindowPlacerLocked.requestTraversal();
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index e84f0cc..94260e20 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -46,9 +46,11 @@
 
     public ClearRequestSession(Context context, int userId, int callingUid,
             IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
-            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
+            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
+            long startedTimestamp) {
         super(context, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED,
-                callingAppInfo, cancellationSignal);
+                callingAppInfo, cancellationSignal, startedTimestamp);
+        setupInitialPhaseMetric(ApiName.CLEAR_CREDENTIAL.getMetricCode(), MetricUtilities.ZERO);
     }
 
     /**
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 7e1780d..47b8c7d 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -53,9 +53,11 @@
             CreateCredentialRequest request,
             ICreateCredentialCallback callback,
             CallingAppInfo callingAppInfo,
-            CancellationSignal cancellationSignal) {
+            CancellationSignal cancellationSignal,
+            long startedTimestamp) {
         super(context, userId, callingUid, request, callback, RequestInfo.TYPE_CREATE,
-                callingAppInfo, cancellationSignal);
+                callingAppInfo, cancellationSignal, startedTimestamp);
+        setupInitialPhaseMetric(ApiName.CREATE_CREDENTIAL.getMetricCode(), MetricUtilities.UNIT);
     }
 
     /**
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index 14c49b3..8ccc61b 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -25,17 +25,19 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
-
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 
 /** Contains information on what CredentialProvider has what provisioned Credential. */
 public class CredentialDescriptionRegistry {
 
+    private static final String FLAT_STRING_SPLIT_REGEX = ";";
     private static final int MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS = 128;
     private static final int MAX_ALLOWED_ENTRIES_PER_PROVIDER = 16;
     @GuardedBy("sLock")
@@ -164,14 +166,16 @@
     /** Returns package names and entries of a CredentialProviders that can satisfy a given
      * {@link CredentialDescription}. */
     public Set<FilterResult> getFilteredResultForProvider(String packageName,
-            String flatRequestStrings) {
+            String flatRequestString) {
         Set<FilterResult> result = new HashSet<>();
         if (!mCredentialDescriptions.containsKey(packageName)) {
             return result;
         }
         Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
+        Set<String> unflattenedRequestString = flatStringToSet(flatRequestString);
         for (CredentialDescription containedDescription: currentSet) {
-            if (flatRequestStrings.equals(containedDescription.getFlattenedRequestString())) {
+            if (checkForMatch(flatStringToSet(containedDescription.getFlattenedRequestString()),
+                    unflattenedRequestString)) {
                 result.add(new FilterResult(packageName,
                         containedDescription.getFlattenedRequestString(), containedDescription
                         .getCredentialEntries()));
@@ -182,12 +186,16 @@
 
     /** Returns package names of CredentialProviders that can satisfy a given
      * {@link CredentialDescription}. */
-    public Set<FilterResult> getMatchingProviders(Set<String> flatRequestString) {
+    public Set<FilterResult> getMatchingProviders(Set<String> flatRequestStrings) {
         Set<FilterResult> result = new HashSet<>();
+        Set<Set<String>> unflattenedRequestStrings = flatRequestStrings.stream().map(
+                CredentialDescriptionRegistry::flatStringToSet).collect(Collectors.toSet());
         for (String packageName: mCredentialDescriptions.keySet()) {
             Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
             for (CredentialDescription containedDescription : currentSet) {
-                if (flatRequestString.contains(containedDescription.getFlattenedRequestString())) {
+                if (canProviderSatisfyAny(flatStringToSet(containedDescription
+                                .getFlattenedRequestString()),
+                        unflattenedRequestStrings)) {
                     result.add(new FilterResult(packageName,
                             containedDescription.getFlattenedRequestString(), containedDescription
                             .getCredentialEntries()));
@@ -203,4 +211,24 @@
         }
     }
 
+    private static boolean canProviderSatisfyAny(Set<String> registeredUnflattenedStrings,
+            Set<Set<String>> requestedUnflattenedStrings) {
+        for (Set<String> requestedUnflattenedString : requestedUnflattenedStrings) {
+            if (registeredUnflattenedStrings.containsAll(requestedUnflattenedString)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static boolean checkForMatch(Set<String> registeredUnflattenedStrings,
+            Set<String> requestedUnflattenedString) {
+        return registeredUnflattenedStrings.containsAll(requestedUnflattenedString);
+    }
+
+    static Set<String> flatStringToSet(String flatString) {
+        return new HashSet<>(Arrays
+                .asList(flatString.split(FLAT_STRING_SPLIT_REGEX)));
+    }
+
 }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 6e998c4..10d3dc0 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -29,11 +29,9 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialException;
 import android.credentials.CreateCredentialRequest;
-import android.credentials.CredentialDescription;
 import android.credentials.CredentialManager;
 import android.credentials.CredentialOption;
 import android.credentials.CredentialProviderInfo;
@@ -230,8 +228,10 @@
         if (hasPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)) {
             return;
         }
-        
-        throw new SecurityException("Caller is missing permission: QUERY_ALL_PACKAGES or LIST_ENABLED_CREDENTIAL_PROVIDERS");
+
+        throw new SecurityException(
+                "Caller is missing permission: QUERY_ALL_PACKAGES or "
+                        + "LIST_ENABLED_CREDENTIAL_PROVIDERS");
     }
 
     private boolean hasPermission(String permission) {
@@ -291,19 +291,15 @@
         List<ProviderSession> providerSessions = new ArrayList<>();
         for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
                 activeCredentialContainers) {
-            providerSessions.add(
-                    ProviderRegistryGetSession.createNewSession(
-                            mContext,
-                            UserHandle.getCallingUserId(),
-                            session,
-                            CredentialProviderInfoFactory.getCredentialProviderFromPackageName(
-                                    mContext, UserHandle.getCallingUserId() ,
-                                            result.second.mPackageName,
-                                            CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS,
-                                    new HashSet<>()),
-                            session.mClientAppInfo,
-                            result.second.mPackageName,
-                            result.first));
+            ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
+                    mContext,
+                    UserHandle.getCallingUserId(),
+                    session,
+                    session.mClientAppInfo,
+                    result.second.mPackageName,
+                    result.first);
+            providerSessions.add(providerSession);
+            session.addProviderSession(providerSession.getComponentName(), providerSession);
         }
         return providerSessions;
     }
@@ -333,10 +329,15 @@
                 new HashSet<>();
 
         for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) {
+            Set<String> registeredUnflattenedStrings = CredentialDescriptionRegistry
+                    .flatStringToSet(filterResult.mFlattenedRequest);
             for (CredentialOption credentialOption : options) {
-                if (filterResult.mFlattenedRequest.equals(credentialOption
-                        .getCredentialRetrievalData()
-                        .getString(CredentialOption.FLATTENED_REQUEST))) {
+                Set<String> requestedUnflattenedStrings = CredentialDescriptionRegistry
+                        .flatStringToSet(credentialOption
+                                .getCredentialRetrievalData()
+                                .getString(CredentialOption.FLATTENED_REQUEST));
+                if (CredentialDescriptionRegistry.checkForMatch(registeredUnflattenedStrings,
+                        requestedUnflattenedStrings)) {
                     result.add(new Pair<>(credentialOption, filterResult));
                 }
             }
@@ -408,6 +409,7 @@
                 GetCredentialRequest request,
                 IGetCredentialCallback callback,
                 final String callingPackage) {
+            final long timestampBegan = System.nanoTime();
             Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
 
@@ -429,7 +431,8 @@
                             callback,
                             request,
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
-                            CancellationSignal.fromTransport(cancelTransport));
+                            CancellationSignal.fromTransport(cancelTransport),
+                            timestampBegan);
 
             processGetCredential(request, callback, session);
             return cancelTransport;
@@ -508,6 +511,9 @@
                                     + e.getMessage());
                 }
             }
+
+            finalizeAndEmitInitialPhaseMetric(session);
+            // TODO(b/271135048) - May still be worth emitting in the empty cases above.
             providerSessions.forEach(ProviderSession::invokeSession);
         }
 
@@ -516,6 +522,7 @@
                 CreateCredentialRequest request,
                 ICreateCredentialCallback callback,
                 String callingPackage) {
+            final long timestampBegan = System.nanoTime();
             Log.i(TAG, "starting executeCreateCredential with callingPackage: "
                     + callingPackage);
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
@@ -538,7 +545,8 @@
                             request,
                             callback,
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
-                            CancellationSignal.fromTransport(cancelTransport));
+                            CancellationSignal.fromTransport(cancelTransport),
+                            timestampBegan);
 
             processCreateCredential(request, callback, session);
             return cancelTransport;
@@ -566,10 +574,17 @@
                 }
             }
 
+            finalizeAndEmitInitialPhaseMetric(session);
             // Iterate over all provider sessions and invoke the request
             providerSessions.forEach(ProviderSession::invokeSession);
         }
 
+        private void finalizeAndEmitInitialPhaseMetric(RequestSession session) {
+            var initMetric = session.mInitialPhaseMetric;
+            initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
+            MetricUtilities.logApiCalled(initMetric);
+        }
+
         @Override
         public void setEnabledProviders(
                 List<String> providers, int userId, ISetEnabledProvidersCallback callback) {
@@ -654,6 +669,7 @@
                         }
                         MetricUtilities.logApiCalled(ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
                                 ApiStatus.SUCCESS, callingUid);
+                        // TODO(b/271135048) - Update asap to use the new logging types
                         return true;
                     }
                 }
@@ -683,12 +699,22 @@
                     mContext, userId, providerFilter, getEnabledProviders());
         }
 
-        private Set<ServiceInfo> getEnabledProviders() {
-            Set<ServiceInfo> enabledProviders = new HashSet<>();
+        @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
+        // this.mLock
+        private Set<ComponentName> getEnabledProviders() {
+            Set<ComponentName> enabledProviders = new HashSet<>();
             synchronized (mLock) {
                 runForUser(
                         (service) -> {
-                            enabledProviders.add(service.getCredentialProviderInfo().getServiceInfo());
+                            try {
+                                enabledProviders.add(
+                                        service.getCredentialProviderInfo()
+                                                .getServiceInfo().getComponentName());
+                            } catch (NullPointerException e) {
+                                // Safe check
+                                Log.i(TAG, "Skipping provider as either the providerInfo"
+                                        + "or serviceInfo is null - weird");
+                            }
                         });
             }
             return enabledProviders;
@@ -699,6 +725,7 @@
                 ClearCredentialStateRequest request,
                 IClearCredentialStateCallback callback,
                 String callingPackage) {
+            final long timestampBegan = System.nanoTime();
             Log.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
             final int userId = UserHandle.getCallingUserId();
             int callingUid = Binder.getCallingUid();
@@ -716,7 +743,8 @@
                             callback,
                             request,
                             constructCallingAppInfo(callingPackage, userId, null),
-                            CancellationSignal.fromTransport(cancelTransport));
+                            CancellationSignal.fromTransport(cancelTransport),
+                            timestampBegan);
 
             // Initiate all provider sessions
             // TODO: Determine if provider needs to have clear capability in their manifest
@@ -735,6 +763,8 @@
                 }
             }
 
+            finalizeAndEmitInitialPhaseMetric(session);
+
             // Iterate over all provider sessions and invoke the request
             providerSessions.forEach(ProviderSession::invokeSession);
             return cancelTransport;
@@ -752,44 +782,6 @@
 
             enforceCallingPackage(callingPackage, Binder.getCallingUid());
 
-            List<CredentialProviderInfo> services =
-                    getServicesForCredentialDescription(UserHandle.getCallingUserId());
-
-            List<String> providers =
-                    services.stream()
-                            .map(
-                                    credentialProviderInfo ->
-                                            credentialProviderInfo.getServiceInfo().packageName)
-                            .toList();
-
-            if (!providers.contains(callingPackage)) {
-                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());
 
@@ -808,20 +800,6 @@
 
             enforceCallingPackage(callingPackage, Binder.getCallingUid());
 
-            List<CredentialProviderInfo> services =
-                    getServicesForCredentialDescription(UserHandle.getCallingUserId());
-
-            List<String> providers =
-                    services.stream()
-                            .map(
-                                    credentialProviderInfo ->
-                                            credentialProviderInfo.getServiceInfo().packageName)
-                            .toList();
-
-            if (!providers.contains(callingPackage)) {
-                throw new NonCredentialProviderCallerException(callingPackage);
-            }
-
             CredentialDescriptionRegistry session =
                     CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
 
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index f59b32c..8e90c09 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.credentials.CredentialOption;
 import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialRequest;
@@ -36,6 +37,7 @@
 import com.android.server.credentials.metrics.ProviderStatusForMetrics;
 
 import java.util.ArrayList;
+import java.util.stream.Collectors;
 
 /**
  * Central session for a single getCredentials request. This class listens to the
@@ -47,9 +49,14 @@
     private static final String TAG = "GetRequestSession";
     public GetRequestSession(Context context, int userId, int callingUid,
             IGetCredentialCallback callback, GetCredentialRequest request,
-            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
+            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
+            long startedTimestamp) {
         super(context, userId, callingUid, request, callback, RequestInfo.TYPE_GET,
-                callingAppInfo, cancellationSignal);
+                callingAppInfo, cancellationSignal, startedTimestamp);
+        int numTypes = (request.getCredentialOptions().stream()
+                .map(CredentialOption::getType).collect(
+                Collectors.toSet())).size(); // Dedupe type strings
+        setupInitialPhaseMetric(ApiName.GET_CREDENTIAL.getMetricCode(), numTypes);
     }
 
     /**
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 255b2f8..1b3e37a 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -24,8 +24,9 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.credentials.metrics.ApiName;
 import com.android.server.credentials.metrics.ApiStatus;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
 import com.android.server.credentials.metrics.ChosenProviderMetric;
+import com.android.server.credentials.metrics.InitialPhaseMetric;
 
 import java.util.Map;
 
@@ -39,7 +40,10 @@
 
     public static final int DEFAULT_INT_32 = -1;
     public static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
-
+    // Used for single count metric emits, such as singular amounts of various types
+    public static final int UNIT = 1;
+    // Used for zero count metric emits, such as zero amounts of various types
+    public static final int ZERO = 0;
 
     /**
      * This retrieves the uid of any package name, given a context and a component name for the
@@ -98,7 +102,7 @@
             int[] candidateStatusList = new int[providerSize];
             int index = 0;
             for (var session : providerSessions) {
-                CandidateProviderMetric metric = session.mCandidateProviderMetric;
+                CandidatePhaseMetric metric = session.mCandidateProviderMetric;
                 candidateUidList[index] = metric.getCandidateUid();
                 candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMicroseconds();
                 candidateStatusList[index] = metric.getProviderQueryStatus();
@@ -155,4 +159,18 @@
         }
     }
 
+    /**
+     * Handles the metric emit for the initial phase.
+     *
+     * @param initialPhaseMetric contains all the data for this emit
+     */
+    protected static void logApiCalled(InitialPhaseMetric initialPhaseMetric) {
+        /*
+        FrameworkStatsLog.write(FrameworkStatsLog.INITIAL_PHASE,
+        .. session_id .. initialPhaseMetric.getSessionId(),
+        ...
+        TODO Immediately - Fill in asap now that the split atom is checked in.
+         */
+    }
+
 }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index b7a4cd5..ab29acc 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -72,7 +72,8 @@
             ProviderInternalCallback callbacks,
             int userId, RemoteCredentialService remoteCredentialService,
             ClearCredentialStateRequest providerRequest) {
-        super(context, info, providerRequest, callbacks, userId, remoteCredentialService);
+        super(context, providerRequest, callbacks, info.getComponentName(),
+                userId, remoteCredentialService);
         setStatus(Status.PENDING);
     }
 
@@ -95,7 +96,7 @@
     /** Called when provider service dies. */
     @Override // Callback from the remote provider
     public void onProviderServiceDied(RemoteCredentialService service) {
-        if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+        if (service.getComponentName().equals(mComponentName)) {
             updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
         } else {
             Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 640cc33..8c9c6cf 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -133,7 +133,7 @@
             @NonNull BeginCreateCredentialRequest beginCreateRequest,
             @NonNull CreateCredentialRequest completeCreateRequest,
             String hybridService) {
-        super(context, info, beginCreateRequest, callbacks, userId,
+        super(context, beginCreateRequest, callbacks, info.getComponentName(), userId,
                 remoteCredentialService);
         mCompleteRequest = completeCreateRequest;
         setStatus(Status.PENDING);
@@ -161,7 +161,7 @@
     /** Called when provider service dies. */
     @Override
     public void onProviderServiceDied(RemoteCredentialService service) {
-        if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+        if (service.getComponentName().equals(mComponentName)) {
             updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
         } else {
             Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 07e2f87..851ccb3 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -169,7 +169,8 @@
             CallingAppInfo callingAppInfo,
             Map<String, CredentialOption> beginGetOptionToCredentialOptionMap,
             String hybridService) {
-        super(context, info, beginGetRequest, callbacks, userId, remoteCredentialService);
+        super(context, beginGetRequest, callbacks, info.getComponentName() ,
+                userId, remoteCredentialService);
         mCompleteRequest = completeGetRequest;
         mCallingAppInfo = callingAppInfo;
         setStatus(Status.PENDING);
@@ -196,7 +197,7 @@
     /** Called when provider service dies. */
     @Override // Callback from the remote provider
     public void onProviderServiceDied(RemoteCredentialService service) {
-        if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+        if (service.getComponentName().equals(mComponentName)) {
             updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
         } else {
             Slog.i(TAG, "Component names different in onProviderServiceDied - "
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 457806d..a5196c5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -19,10 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.credentials.CredentialOption;
-import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialResponse;
 import android.credentials.ui.Entry;
@@ -64,7 +64,6 @@
             @NonNull Context context,
             @UserIdInt int userId,
             @NonNull GetRequestSession getRequestSession,
-            @NonNull CredentialProviderInfo credentialProviderInfo,
             @NonNull CallingAppInfo callingAppInfo,
             @NonNull String credentialProviderPackageName,
             @NonNull CredentialOption requestOption) {
@@ -72,7 +71,6 @@
                 context,
                 userId,
                 getRequestSession,
-                credentialProviderInfo,
                 callingAppInfo,
                 credentialProviderPackageName,
                 requestOption);
@@ -94,11 +92,12 @@
     protected ProviderRegistryGetSession(@NonNull Context context,
             @NonNull int userId,
             @NonNull GetRequestSession session,
-            @NonNull CredentialProviderInfo credentialProviderInfo,
             @NonNull CallingAppInfo callingAppInfo,
             @NonNull String servicePackageName,
             @NonNull CredentialOption requestOption) {
-        super(context, credentialProviderInfo, requestOption, session, userId, null);
+        super(context, requestOption, session,
+                new ComponentName(servicePackageName, servicePackageName) ,
+                userId, null);
         mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(userId);
         mCallingAppInfo = callingAppInfo;
         mCredentialProviderPackageName = servicePackageName;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index a8b9bf6..03e2a32 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -31,7 +31,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
 import com.android.server.credentials.metrics.ProviderStatusForMetrics;
 
 import java.util.UUID;
@@ -59,7 +59,7 @@
     @Nullable protected R mProviderResponse;
     @NonNull protected Boolean mProviderResponseSet = false;
     // Specific candidate provider metric for the provider this session handles
-    @Nullable protected CandidateProviderMetric mCandidateProviderMetric;
+    @Nullable protected CandidatePhaseMetric mCandidateProviderMetric;
     @NonNull private int mProviderSessionUid;
 
     /**
@@ -114,19 +114,20 @@
                 @Nullable String message);
     }
 
-    protected ProviderSession(@NonNull Context context, @Nullable CredentialProviderInfo info,
+    protected ProviderSession(@NonNull Context context,
             @NonNull T providerRequest,
             @Nullable ProviderInternalCallback callbacks,
+            @NonNull ComponentName componentName,
             @NonNull int userId,
             @Nullable RemoteCredentialService remoteCredentialService) {
         mContext = context;
-        mProviderInfo = info;
+        mProviderInfo = null;
         mProviderRequest = providerRequest;
         mCallbacks = callbacks;
         mUserId = userId;
-        mComponentName = info.getServiceInfo().getComponentName();
+        mComponentName = componentName;
         mRemoteCredentialService = remoteCredentialService;
-        mCandidateProviderMetric = new CandidateProviderMetric();
+        mCandidateProviderMetric = new CandidatePhaseMetric();
         mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName);
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index c1f35d0..edddba0 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -36,8 +36,9 @@
 import com.android.internal.R;
 import com.android.server.credentials.metrics.ApiName;
 import com.android.server.credentials.metrics.ApiStatus;
-import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.CandidatePhaseMetric;
 import com.android.server.credentials.metrics.ChosenProviderMetric;
+import com.android.server.credentials.metrics.InitialPhaseMetric;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -75,7 +76,7 @@
 
     protected final Map<String, ProviderSession> mProviders = new HashMap<>();
     protected ChosenProviderMetric mChosenProviderMetric = new ChosenProviderMetric();
-    //TODO improve design to allow grouped metrics per request
+    protected InitialPhaseMetric mInitialPhaseMetric = new InitialPhaseMetric();
     protected final String mHybridService;
 
     @NonNull
@@ -96,7 +97,7 @@
             @UserIdInt int userId, int callingUid, @NonNull T clientRequest, U clientCallback,
             @NonNull String requestType,
             CallingAppInfo callingAppInfo,
-            CancellationSignal cancellationSignal) {
+            CancellationSignal cancellationSignal, long timestampStarted) {
         mContext = context;
         mUserId = userId;
         mCallingUid = callingUid;
@@ -111,6 +112,9 @@
                 mUserId, this);
         mHybridService = context.getResources().getString(
                 R.string.config_defaultCredentialManagerHybridService);
+        mInitialPhaseMetric.setCredentialServiceStartedTimeNanoseconds(timestampStarted);
+        mInitialPhaseMetric.setSessionId(mRequestId.hashCode());
+        mInitialPhaseMetric.setCallerUid(mCallingUid);
     }
 
     public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
@@ -118,6 +122,16 @@
 
     protected abstract void launchUiWithProviderData(ArrayList<ProviderData> providerDataList);
 
+    // Sets up the initial metric collector for use across all request session impls
+    protected void setupInitialPhaseMetric(int metricCode, int requestClassType) {
+        this.mInitialPhaseMetric.setApiName(metricCode);
+        this.mInitialPhaseMetric.setCountRequestClassType(requestClassType);
+    }
+
+    public void addProviderSession(ComponentName componentName, ProviderSession providerSession) {
+        mProviders.put(componentName.flattenToString(), providerSession);
+    }
+
     // UI callbacks
 
     @Override // from CredentialManagerUiCallbacks
@@ -218,7 +232,7 @@
      * @param componentName the componentName to associate with a provider
      */
     protected void setChosenMetric(ComponentName componentName) {
-        CandidateProviderMetric metric = this.mProviders.get(componentName.flattenToString())
+        CandidatePhaseMetric metric = this.mProviders.get(componentName.flattenToString())
                 .mCandidateProviderMetric;
         mChosenProviderMetric.setChosenUid(metric.getCandidateUid());
         mChosenProviderMetric.setFinalFinishTimeNanoseconds(System.nanoTime());
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
new file mode 100644
index 0000000..1c7fb69
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
@@ -0,0 +1,246 @@
+/*
+ * 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.metrics;
+
+import android.util.Log;
+
+import com.android.server.credentials.MetricUtilities;
+
+/**
+ * The central candidate provider metric object that mimics our defined metric setup.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
+ * TODO(b/270403549) - iterate on this in V3+
+ */
+public class CandidatePhaseMetric {
+
+    private static final String TAG = "CandidateProviderMetric";
+    // Since this will always be the second in the split sequence, this is statically 2
+    private static final int SESSION_ID = 2;
+    // The sequence number of this emit of the API call, default -1, equal for all candidates
+    private int mSequenceId = -1;
+    // Indicates if this provider returned from the query phase, default false
+    private boolean mQueryReturned = false;
+
+    // The candidate provider uid
+    private int mCandidateUid = -1;
+
+    // Raw timestamp in nanoseconds, will be converted to microseconds for logging
+
+    //For reference, the initial log timestamp when the service started running the API call
+    private long mServiceBeganTimeNanoseconds = -1;
+    // The moment when the query phase began
+    private long mStartQueryTimeNanoseconds = -1;
+    // The moment when the query phase ended
+    private long mQueryFinishTimeNanoseconds = -1;
+
+    // The status of this particular provider
+    private int mProviderQueryStatus = -1;
+    // Indicates if an exception was thrown by this provider, false by default
+    private boolean mHasException = false;
+    // Indicates the number of total entries available. We can also locally store the entries, but
+    // cannot emit them in the current split form. TODO(b/271135048) - possibly readjust candidate
+    // entries. Also, it may be okay to remove this and instead aggregate from inner counts.
+    // Defaults to -1
+    private int mNumEntriesTotal = -1;
+    // The count of action entries from this provider, defaults to -1
+    private int mActionEntryCount = -1;
+    // The count of credential entries from this provider, defaults to -1
+    private int mCredentialEntryCount = -1;
+    // The *type-count* of the credential entries, defaults to -1
+    private int mCredentialEntryTypeCount = -1;
+    // The count of remote entries from this provider, defaults to -1
+    private int mRemoteEntryCount = -1;
+    // The count of authentication entries from this provider, defaults to -1
+    private int mAuthenticationEntryCount = -1;
+
+    public CandidatePhaseMetric() {
+    }
+
+    /* ---------- Latencies ---------- */
+
+    /* -- Timestamps -- */
+
+    public void setServiceBeganTimeNanoseconds(long serviceBeganTimeNanoseconds) {
+        this.mServiceBeganTimeNanoseconds = serviceBeganTimeNanoseconds;
+    }
+
+    public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
+        this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
+    }
+
+    public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
+        this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
+    }
+
+    public long getServiceBeganTimeNanoseconds() {
+        return this.mServiceBeganTimeNanoseconds;
+    }
+
+    public long getStartQueryTimeNanoseconds() {
+        return this.mStartQueryTimeNanoseconds;
+    }
+
+    public long getQueryFinishTimeNanoseconds() {
+        return this.mQueryFinishTimeNanoseconds;
+    }
+
+    /* -- Actual time delta latencies (for local utility) -- */
+
+    /**
+     * Returns the latency in microseconds for the query phase.
+     */
+    public int getQueryLatencyMicroseconds() {
+        return (int) ((this.getQueryFinishTimeNanoseconds()
+                - this.getStartQueryTimeNanoseconds()) / 1000);
+    }
+
+    /* --- Time Stamp Conversion to Microseconds from Reference --- */
+
+    /**
+     * We collect raw timestamps in nanoseconds for ease of collection. However, given the scope
+     * of our logging timeframe, and size considerations of the metric, we require these to give us
+     * the microsecond timestamps from the start reference point.
+     *
+     * @param specificTimestamp the timestamp to consider, must be greater than the reference
+     * @return the microsecond integer timestamp from service start to query began
+     */
+    public int getTimestampFromReferenceStartMicroseconds(long specificTimestamp) {
+        if (specificTimestamp < this.mServiceBeganTimeNanoseconds) {
+            Log.i(TAG, "The timestamp is before service started, falling back to default int");
+            return MetricUtilities.DEFAULT_INT_32;
+        }
+        return (int) ((specificTimestamp
+                - this.mServiceBeganTimeNanoseconds) / 1000);
+    }
+
+    /* ------------- Provider Query Status ------------ */
+
+    public void setProviderQueryStatus(int providerQueryStatus) {
+        this.mProviderQueryStatus = providerQueryStatus;
+    }
+
+    public int getProviderQueryStatus() {
+        return this.mProviderQueryStatus;
+    }
+
+    /* -------------- Candidate Uid ---------------- */
+
+    public void setCandidateUid(int candidateUid) {
+        this.mCandidateUid = candidateUid;
+    }
+
+    public int getCandidateUid() {
+        return this.mCandidateUid;
+    }
+
+    /* -------------- Session Id ---------------- */
+    public int getSessionId() {
+        return SESSION_ID;
+    }
+
+    /* -------------- Sequence Id ---------------- */
+
+    public void setSequenceId(int sequenceId) {
+        mSequenceId = sequenceId;
+    }
+
+    public int getSequenceId() {
+        return mSequenceId;
+    }
+
+    /* -------------- Query Returned Status ---------------- */
+
+    public void setQueryReturned(boolean queryReturned) {
+        mQueryReturned = queryReturned;
+    }
+
+    public boolean isQueryReturned() {
+        return mQueryReturned;
+    }
+
+    /* -------------- Has Exception Status ---------------- */
+
+    public void setHasException(boolean hasException) {
+        mHasException = hasException;
+    }
+
+    public boolean isHasException() {
+        return mHasException;
+    }
+
+    /* -------------- Number of Entries ---------------- */
+
+    public void setNumEntriesTotal(int numEntriesTotal) {
+        mNumEntriesTotal = numEntriesTotal;
+    }
+
+    public int getNumEntriesTotal() {
+        return mNumEntriesTotal;
+    }
+
+    /* -------------- Count of Action Entries ---------------- */
+
+    public void setActionEntryCount(int actionEntryCount) {
+        mActionEntryCount = actionEntryCount;
+    }
+
+    public int getActionEntryCount() {
+        return mActionEntryCount;
+    }
+
+    /* -------------- Count of Credential Entries ---------------- */
+
+    public void setCredentialEntryCount(int credentialEntryCount) {
+        mCredentialEntryCount = credentialEntryCount;
+    }
+
+    public int getCredentialEntryCount() {
+        return mCredentialEntryCount;
+    }
+
+    /* -------------- Count of Credential Entry Types ---------------- */
+
+    public void setCredentialEntryTypeCount(int credentialEntryTypeCount) {
+        mCredentialEntryTypeCount = credentialEntryTypeCount;
+    }
+
+    public int getCredentialEntryTypeCount() {
+        return mCredentialEntryTypeCount;
+    }
+
+    /* -------------- Count of Remote Entries ---------------- */
+
+    public void setRemoteEntryCount(int remoteEntryCount) {
+        mRemoteEntryCount = remoteEntryCount;
+    }
+
+    public int getRemoteEntryCount() {
+        return mRemoteEntryCount;
+    }
+
+    /* -------------- Count of Authentication Entries ---------------- */
+
+    public void setAuthenticationEntryCount(int authenticationEntryCount) {
+        mAuthenticationEntryCount = authenticationEntryCount;
+    }
+
+    public int getAuthenticationEntryCount() {
+        return mAuthenticationEntryCount;
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
deleted file mode 100644
index 9f438ec..0000000
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.metrics;
-
-/**
- * The central candidate provider metric object that mimics our defined metric setup.
- * Some types are redundant across these metric collectors, but that has debug use-cases as
- * these data-types are available at different moments of the flow (and typically, one can feed
- * into the next).
- * TODO(b/270403549) - iterate on this in V3+
- */
-public class CandidateProviderMetric {
-
-    private static final String TAG = "CandidateProviderMetric";
-    private int mCandidateUid = -1;
-
-    // Raw timestamp in nanoseconds, will be converted to microseconds for logging
-
-    private long mStartQueryTimeNanoseconds = -1;
-    private long mQueryFinishTimeNanoseconds = -1;
-
-    private int mProviderQueryStatus = -1;
-
-    public CandidateProviderMetric() {
-    }
-
-    /* ---------- Latencies ---------- */
-
-    public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
-        this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
-    }
-
-    public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
-        this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
-    }
-
-    public long getStartQueryTimeNanoseconds() {
-        return this.mStartQueryTimeNanoseconds;
-    }
-
-    public long getQueryFinishTimeNanoseconds() {
-        return this.mQueryFinishTimeNanoseconds;
-    }
-
-    /**
-     * Returns the latency in microseconds for the query phase.
-     */
-    public int getQueryLatencyMicroseconds() {
-        return (int) ((this.getQueryFinishTimeNanoseconds()
-                - this.getStartQueryTimeNanoseconds()) / 1000);
-    }
-
-    // TODO (in direct next dependent CL, so this is transient) - add reference timestamp in micro
-    // seconds for this too.
-
-    /* ------------- Provider Query Status ------------ */
-
-    public void setProviderQueryStatus(int providerQueryStatus) {
-        this.mProviderQueryStatus = providerQueryStatus;
-    }
-
-    public int getProviderQueryStatus() {
-        return this.mProviderQueryStatus;
-    }
-
-    /* -------------- Candidate Uid ---------------- */
-
-    public void setCandidateUid(int candidateUid) {
-        this.mCandidateUid = candidateUid;
-    }
-
-    public int getCandidateUid() {
-        return this.mCandidateUid;
-    }
-}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
index 0310255..1a61091 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
@@ -64,7 +64,7 @@
     /* ---------------- Latencies ------------------ */
 
 
-    /* ----- Direct Latencies ------- */
+    /* ----- Direct Delta Latencies for Local Utility ------- */
 
     /**
      * In order for a chosen provider to be selected, the call must have successfully begun.
@@ -85,7 +85,7 @@
      * metric.
      *
      * @param queryPhaseLatencyMicroseconds the millisecond latency for the query phase, typically
-     *                                      passed in through the {@link CandidateProviderMetric}
+     *                                      passed in through the {@link CandidatePhaseMetric}
      */
     public void setQueryPhaseLatencyMicroseconds(int queryPhaseLatencyMicroseconds) {
         mQueryPhaseLatencyMicroseconds = queryPhaseLatencyMicroseconds;
@@ -106,7 +106,7 @@
 
     /**
      * Returns the full provider (invocation to response) latency in microseconds. Expects the
-     * start time to be provided, such as from {@link CandidateProviderMetric}.
+     * start time to be provided, such as from {@link CandidatePhaseMetric}.
      */
     public int getEntireProviderLatencyMicroseconds() {
         return (int) ((this.mFinalFinishTimeNanoseconds
@@ -172,7 +172,7 @@
         return mFinalFinishTimeNanoseconds;
     }
 
-    /* --- Time Stamp Conversion to Microseconds --- */
+    /* --- Time Stamp Conversion to Microseconds from Reference Point --- */
 
     /**
      * We collect raw timestamps in nanoseconds for ease of collection. However, given the scope
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 5f062b0..31c6f6f 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -25,6 +25,8 @@
  */
 public class InitialPhaseMetric {
     private static final String TAG = "PreCandidateMetric";
+    // A sequence id to order united emits, due to split, this will statically always be 1
+    public static final int SEQUENCE_ID = 1;
 
     // The api being called, default set to unknown
     private int mApiName = ApiName.UNKNOWN.getMetricCode();
@@ -32,8 +34,6 @@
     private int mCallerUid = -1;
     // The session id to unite multiple atom emits, default to -1
     private long mSessionId = -1;
-    // A sequence id to order united emits, default to -1
-    private int mSequenceId = -1;
     private int mCountRequestClassType = -1;
 
     // Raw timestamps in nanoseconds, *the only* one logged as such (i.e. 64 bits) since it is a
@@ -50,7 +50,7 @@
 
     /* ---------- Latencies ---------- */
 
-    /* -- Direct Latencies -- */
+    /* -- Direct Latency Utility -- */
 
     public int getServiceStartToQueryLatencyMicroseconds() {
         return (int) ((this.mCredentialServiceStartedTimeNanoseconds
@@ -108,15 +108,6 @@
         return mSessionId;
     }
 
-    /* ------ SequenceId ------ */
-
-    public void setSequenceId(int sequenceId) {
-        mSequenceId = sequenceId;
-    }
-
-    public int getSequenceId() {
-        return mSequenceId;
-    }
 
     /* ------ Count Request Class Types ------ */
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a4e563b..a220ad1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11219,7 +11219,7 @@
         } else {
             Objects.requireNonNull(admin, "ComponentName is null");
             Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
-                    || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
+                    || isProfileOwnerOfOrganizationOwnedDevice(caller));
         }
         if (parent) {
             userId = getProfileParentId(mInjector.userHandleGetCallingUserId());
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 9501b96..9829e57 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -65,6 +65,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
@@ -121,6 +122,14 @@
     protected InputMethodManagerService mInputMethodManagerService;
     protected ServiceThread mServiceThread;
 
+    @BeforeClass
+    public static void setupClass() {
+        // Make sure DeviceConfig's lazy-initialized ContentProvider gets
+        // a real instance before we stub out all system services below.
+        // TODO(b/272229177): remove dependency on real ContentProvider
+        new InputMethodDeviceConfigs().destroy();
+    }
+
     @Before
     public void setUp() throws RemoteException {
         mMockingSession =
@@ -231,6 +240,10 @@
 
     @After
     public void tearDown() {
+        if (mInputMethodManagerService != null) {
+            mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
+        }
+
         if (mServiceThread != null) {
             mServiceThread.quitSafely();
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 0b4c70c2..485ce33 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1968,36 +1968,6 @@
 
     @SuppressWarnings("GuardedBy")
     @Test
-    public void testUpdateOomAdj_DoOne_PendingFinishAttach() {
-        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
-                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.setPendingFinishAttach(true);
-        app.mState.setHasForegroundActivities(false);
-
-        sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
-        updateOomAdj(app);
-
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
-                SCHED_GROUP_DEFAULT);
-    }
-
-    @SuppressWarnings("GuardedBy")
-    @Test
-    public void testUpdateOomAdj_DoOne_TopApp_PendingFinishAttach() {
-        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
-                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.setPendingFinishAttach(true);
-        app.mState.setHasForegroundActivities(true);
-
-        sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
-        updateOomAdj(app);
-
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
-                SCHED_GROUP_TOP_APP);
-    }
-
-    @SuppressWarnings("GuardedBy")
-    @Test
     public void testUpdateOomAdj_UidIdle_StopService() {
         final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
index 2774803..239b6fd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -139,6 +139,13 @@
     }
 
     @Test
+    public void testInvalidMode() {
+        assertThrows(IllegalArgumentException.class, () -> new UserVisibilityMediator(
+                /* visibleBackgroundUsersOnDisplaysEnabled= */ false,
+                /* visibleBackgroundUserOnDefaultDisplayAllowed= */ true, mHandler));
+    }
+
+    @Test
     public final void testAssignUserToDisplayOnStart_invalidUserIds() {
         assertThrows(IllegalArgumentException.class, () -> mMediator
                 .assignUserToDisplayOnStart(USER_NULL, USER_ID, FG, DEFAULT_DISPLAY));
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index e68a8a0..01563e2 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -106,6 +106,64 @@
                 mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
     }
 
+    @Test
+    public void testStrictRepeatedLimiting() throws Exception {
+        // The first 6 entries should not be rate limited.
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        // The 7th entry of the same process should be rate limited.
+        assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+        // After 11 minutes there should be nothing left in the buffer and the same type of entry
+        // should not get rate limited anymore.
+        mClock.setOffsetMillis(11 * 60 * 1000);
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        // The first 6 entries should not be rate limited again.
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+        // The 7th entry of the same process should be rate limited.
+        assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+        // After 11 more minutes there should be nothing left in the buffer and the same type of
+        // entry should not get rate limited anymore.
+        mClock.setOffsetMillis(22 * 60 * 1000);
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+        // Repeated crashes after the last reset being rate limited should be restricted faster.
+        assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+        // We now need to wait 61 minutes for the buffer should be empty again.
+        mClock.setOffsetMillis(83 * 60 * 1000);
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+        // After yet another 61 minutes, this time without triggering rate limiting, the strict
+        // limiting should be turnd off.
+        mClock.setOffsetMillis(144 * 60 * 1000);
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+        // As rate limiting was not triggered in the last reset, after another 11 minutes the
+        // buffer should still act as normal.
+        mClock.setOffsetMillis(155 * 60 * 1000);
+        // The first 6 entries should not be rate limited.
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        // The 7th entry of the same process should be rate limited.
+        assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+    }
+
     private static class TestClock implements DropboxRateLimiter.Clock {
         long mOffsetMillis = 0L;
 
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
index b7085f15..169210f 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialDescriptionRegistryTest.java
@@ -52,8 +52,10 @@
     private static final String CALLING_PACKAGE_NAME_2 = "com.credman.app2";
     private static final String MDOC_CREDENTIAL_TYPE = "MDOC";
     private static final String PASSKEY_CREDENTIAL_TYPE = "PASSKEY";
-    private static final String FLATTENED_REQUEST = "FLATTENED_REQ";
-    private static final String FLATTENED_REQUEST_2 = "FLATTENED_REQ_2";
+    private static final String FLATTENED_REGISTRY =
+            "FLATTENED_REQ;FLATTENED_REQ123;FLATTENED_REQa";
+    private static final String FLATTENED_REGISTRY_2 = "FLATTENED_REQ_2";
+    private static final String FLATTENED_REQUEST = "FLATTENED_REQ;FLATTENED_REQ123";
 
     private CredentialDescriptionRegistry mCredentialDescriptionRegistry;
     private CredentialEntry mEntry;
@@ -104,12 +106,12 @@
     @Test
     public void testEvictProvider_existingProviders_succeeds() {
         final CredentialDescription credentialDescription =
-                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
                         Collections.emptyList());
         final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
                 new RegisterCredentialDescriptionRequest(credentialDescription);
         final CredentialDescription credentialDescription2 =
-                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST_2,
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY_2,
                         Collections.emptyList());
         final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
                 new RegisterCredentialDescriptionRequest(credentialDescription2);
@@ -130,12 +132,12 @@
     @Test
     public void testGetMatchingProviders_existingProviders_succeeds() {
         final CredentialDescription credentialDescription =
-                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
                         Collections.emptyList());
         final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
                 new RegisterCredentialDescriptionRequest(credentialDescription);
         final CredentialDescription credentialDescription2 =
-                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REQUEST,
+                new CredentialDescription(MDOC_CREDENTIAL_TYPE, FLATTENED_REGISTRY,
                         Collections.emptyList());
         final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest2 =
                 new RegisterCredentialDescriptionRequest(credentialDescription2);
@@ -171,11 +173,11 @@
     public void testExecuteRegisterRequest_existingProviders_filterSucceeds() {
         final CredentialDescription credentialDescription =
                 new CredentialDescription(MDOC_CREDENTIAL_TYPE,
-                        FLATTENED_REQUEST,
+                        FLATTENED_REGISTRY,
                         List.of(mEntry, mEntry2));
         final CredentialDescription credentialDescription2 =
                 new CredentialDescription(PASSKEY_CREDENTIAL_TYPE,
-                        FLATTENED_REQUEST_2,
+                        FLATTENED_REGISTRY_2,
                         List.of(mEntry3));
         final RegisterCredentialDescriptionRequest registerCredentialDescriptionRequest =
                 new RegisterCredentialDescriptionRequest(Set.of(credentialDescription,
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 3d52ac5..4c8e70a 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -36,7 +36,6 @@
 import android.content.pm.SigningInfo;
 import android.credentials.Credential;
 import android.credentials.CredentialOption;
-import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialResponse;
 import android.credentials.ui.GetCredentialProviderData;
@@ -45,7 +44,6 @@
 import android.os.Bundle;
 import android.service.credentials.CallingAppInfo;
 import android.service.credentials.CredentialEntry;
-import android.service.credentials.CredentialProviderInfoFactory;
 import android.service.credentials.CredentialProviderService;
 import android.service.credentials.GetCredentialRequest;
 
@@ -61,7 +59,6 @@
 import org.mockito.MockitoAnnotations;
 
 import java.security.cert.CertificateException;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -89,7 +86,6 @@
     @Mock private GetRequestSession mGetRequestSession;
     private CredentialOption mGetCredentialOption;
     @Mock private ServiceInfo mServiceInfo;
-    private CredentialProviderInfo mCredentialProviderInfo;
     private CallingAppInfo mCallingAppInfo;
     @Mock private CredentialDescriptionRegistry mCredentialDescriptionRegistry;
     private Bundle mRetrievalData;
@@ -111,12 +107,6 @@
         mGetCredentialOption = new CredentialOption(CREDENTIAL_TYPE, mRetrievalData,
                 new Bundle(), false);
         when(mServiceInfo.getComponentName()).thenReturn(CREDENTIAL_PROVIDER_COMPONENT);
-        mCredentialProviderInfo = CredentialProviderInfoFactory
-                .createForTests(mServiceInfo,
-                        /* overrideLabel= */ "test",
-                        /* isSystemProvider= */ false,
-                        /* isEnabled= */ true,
-                        /* capabilities= */ Collections.EMPTY_LIST);
         CredentialDescriptionRegistry.setSession(USER_ID_1, mCredentialDescriptionRegistry);
         mResponse = new HashSet<>();
         mSlice = createSlice();
@@ -130,7 +120,7 @@
         when(mCredentialDescriptionRegistry.getFilteredResultForProvider(anyString(), anyString()))
                 .thenReturn(mResponse);
         mProviderRegistryGetSession = ProviderRegistryGetSession
-                .createNewSession(context, USER_ID_1, mGetRequestSession, mCredentialProviderInfo,
+                .createNewSession(context, USER_ID_1, mGetRequestSession,
                         mCallingAppInfo,
                         CALLING_PACKAGE_NAME,
                         mGetCredentialOption);
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index dd0cd96..fd1889c 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -18,6 +18,7 @@
 
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import android.hardware.display.DisplayManager;
@@ -49,6 +50,7 @@
     private static final float FLOAT_TOLERANCE = 0.01f;
     private static final int DISPLAY_ID = 1;
     private static final int DISPLAY_ID_OTHER = 2;
+    private static final int DISPLAY_ID_ADDED = 3;
 
     SkinThermalStatusObserver mObserver;
 
@@ -167,6 +169,42 @@
         assertEquals(120, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
     }
 
+    @Test
+    public void testDisplayAdded() {
+        // GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
+        mObserver.observe();
+        mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
+        // WHEN new display is added
+        mObserver.onDisplayAdded(DISPLAY_ID_ADDED);
+        mHandler.flush();
+        // THEN 3rd vote is added to storage with (0,60) render refresh rate(default behaviour)
+        assertEquals(3, mStorage.mVoteRegistry.size());
+
+        SparseArray<DisplayModeDirector.Vote> displayVotes = mStorage.mVoteRegistry.get(
+                DISPLAY_ID_ADDED);
+        assertEquals(1, displayVotes.size());
+
+        DisplayModeDirector.Vote vote = displayVotes.get(
+                DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE);
+        assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
+        assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+    }
+
+    @Test
+    public void testDisplayAddedAndThenImmediatelyRemoved() {
+        // GIVEN 2 displays with no thermalThrottling config AND temperature level CRITICAL
+        mObserver.observe();
+        mObserver.notifyThrottling(createTemperature(Temperature.THROTTLING_CRITICAL));
+        // WHEN new display is added and immediately removed
+        mObserver.onDisplayAdded(DISPLAY_ID_ADDED);
+        mObserver.onDisplayRemoved(DISPLAY_ID_ADDED);
+        mHandler.flush();
+        // THEN there are 2 votes in registry
+        assertEquals(2, mStorage.mVoteRegistry.size());
+        assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID));
+        assertNotNull(mStorage.mVoteRegistry.get(DISPLAY_ID_OTHER));
+    }
+
     private static Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
         return new Temperature(40.0f, Temperature.TYPE_SKIN, "test_temp", status);
     }
diff --git a/services/tests/servicestests/src/com/android/server/dreams/OWNERS b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
new file mode 100644
index 0000000..2f19cf5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/dreams/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index c9612cd..ad63da5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -39,6 +39,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.hardware.authsecret.IAuthSecret;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
@@ -97,6 +98,7 @@
     MockLockSettingsContext mContext;
     LockSettingsStorageTestable mStorage;
 
+    Resources mResources;
     FakeGateKeeperService mGateKeeperService;
     NotificationManager mNotificationManager;
     UserManager mUserManager;
@@ -122,6 +124,7 @@
 
     @Before
     public void setUp_baseServices() throws Exception {
+        mResources = createMockResources();
         mGateKeeperService = new FakeGateKeeperService();
         mNotificationManager = mock(NotificationManager.class);
         mUserManager = mock(UserManager.class);
@@ -146,7 +149,7 @@
         LocalServices.addService(WindowManagerInternal.class, mMockWindowManager);
 
         final Context origContext = InstrumentationRegistry.getContext();
-        mContext = new MockLockSettingsContext(origContext,
+        mContext = new MockLockSettingsContext(origContext, mResources,
                 mSettingsRule.mockContentResolver(origContext), mUserManager, mNotificationManager,
                 mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
                 mock(KeyguardManager.class), mFingerprintManager, mFaceManager, mPackageManager);
@@ -245,6 +248,22 @@
         mLocalService = LocalServices.getService(LockSettingsInternal.class);
     }
 
+    private Resources createMockResources() {
+        Resources res = mock(Resources.class);
+
+        // Set up some default configs, copied from core/res/res/values/config.xml
+        when(res.getBoolean(eq(com.android.internal.R.bool.config_disableLockscreenByDefault)))
+                .thenReturn(false);
+        when(res.getBoolean(
+                eq(com.android.internal.R.bool.config_enableCredentialFactoryResetProtection)))
+                .thenReturn(true);
+        when(res.getBoolean(eq(com.android.internal.R.bool.config_isMainUserPermanentAdmin)))
+                .thenReturn(true);
+        when(res.getBoolean(eq(com.android.internal.R.bool.config_strongAuthRequiredOnBoot)))
+                .thenReturn(true);
+        return res;
+    }
+
     protected void setDeviceProvisioned(boolean provisioned) {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.DEVICE_PROVISIONED, provisioned ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 05208441e..10ed882 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.database.sqlite.SQLiteDatabase;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
@@ -106,8 +107,8 @@
         when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
 
         MockLockSettingsContext context = new MockLockSettingsContext(origContext,
-                mSettingsRule.mockContentResolver(origContext), mockUserManager,
-                mock(NotificationManager.class), mock(DevicePolicyManager.class),
+                mock(Resources.class), mSettingsRule.mockContentResolver(origContext),
+                mockUserManager, mock(NotificationManager.class), mock(DevicePolicyManager.class),
                 mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class),
                 mock(FingerprintManager.class), mock(FaceManager.class),
                 mock(PackageManager.class));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
index 21c367b..96d7cbe 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
@@ -27,6 +27,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Handler;
@@ -36,6 +37,7 @@
 
 public class MockLockSettingsContext extends ContextWrapper {
 
+    private final Resources mResources;
     private final ContentResolver mContentResolver;
     private final UserManager mUserManager;
     private final NotificationManager mNotificationManager;
@@ -47,13 +49,14 @@
     private final FaceManager mFaceManager;
     private final PackageManager mPackageManager;
 
-    public MockLockSettingsContext(Context base, ContentResolver contentResolver,
-            UserManager userManager, NotificationManager notificationManager,
-            DevicePolicyManager devicePolicyManager, StorageManager storageManager,
-            TrustManager trustManager, KeyguardManager keyguardManager,
-            FingerprintManager fingerprintManager, FaceManager faceManager,
-            PackageManager packageManager) {
+    public MockLockSettingsContext(Context base, Resources resources,
+            ContentResolver contentResolver, UserManager userManager,
+            NotificationManager notificationManager, DevicePolicyManager devicePolicyManager,
+            StorageManager storageManager, TrustManager trustManager,
+            KeyguardManager keyguardManager, FingerprintManager fingerprintManager,
+            FaceManager faceManager, PackageManager packageManager) {
         super(base);
+        mResources = resources;
         mContentResolver = contentResolver;
         mUserManager = userManager;
         mNotificationManager = notificationManager;
@@ -67,6 +70,11 @@
     }
 
     @Override
+    public Resources getResources() {
+        return mResources;
+    }
+
+    @Override
     public ContentResolver getContentResolver() {
         return mContentResolver;
     }
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 2affe92..8b178dd 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
@@ -41,7 +41,7 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -1326,11 +1326,10 @@
         when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                 LockPatternUtils.CREDENTIAL_TYPE_PIN);
 
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
 
-        int credetialsType = request.getLockscreenUiType();
-        assertThat(credetialsType).isEqualTo(KeyguardManager.PIN);
+        assertThat(request.getLockType()).isEqualTo(KeyguardManager.PIN);
         assertThat(request.getRemainingAttempts()).isEqualTo(5);
         verify(mLockSettingsService).getCredentialType(anyInt());
     }
@@ -1340,11 +1339,10 @@
                 LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
         mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 3);
 
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
 
-        int credetialsType = request.getLockscreenUiType();
-        assertThat(credetialsType).isEqualTo(KeyguardManager.PATTERN);
+        assertThat(request.getLockType()).isEqualTo(KeyguardManager.PATTERN);
         assertThat(request.getRemainingAttempts()).isEqualTo(2);
     }
     @Test
@@ -1353,24 +1351,23 @@
                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
         mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 7);
 
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
 
-        int credetialsType = request.getLockscreenUiType();
         assertThat(request.getRemainingAttempts()).isEqualTo(0);
-        assertThat(credetialsType).isEqualTo(KeyguardManager.PASSWORD);
+        assertThat(request.getLockType()).isEqualTo(KeyguardManager.PASSWORD);
     }
     @Test
     public void validateRemoteLockscreen_noActiveSession() throws Exception {
         when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
-                LockPatternUtils.CREDENTIAL_TYPE_NONE);
-        try {
-            mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS,
-                    mLockSettingsService);
-            fail("should have thrown");
-        } catch (IllegalStateException e) {
-            assertThat(e.getMessage()).contains("session");
-        }
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
+
+        RemoteLockscreenValidationResult result =
+                mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS,
+                        mLockSettingsService);
+
+        assertThat(result.getResultCode()).isEqualTo(
+                RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED);
     }
     @Test
     public void validateRemoteLockscreen_decryptionError() throws Exception {
@@ -1456,7 +1453,7 @@
     }
 
     private byte[] encryptCredentialsForNewSession(byte[] credentials) throws Exception {
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
         PublicKey publicKey = SecureBox.decodePublicKey(request.getSourcePublicKey());
         return SecureBox.encrypt(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 354420f..41a9504 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -416,7 +416,7 @@
         // Shell permisssions will override permissions of our app, so add all necessary permissions
         // for this test here:
         InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
-                "android.permission.WRITE_DEVICE_CONFIG",
+                "android.permission.ALLOWLISTED_WRITE_DEVICE_CONFIG",
                 "android.permission.READ_DEVICE_CONFIG",
                 "android.permission.READ_CONTACTS");
 
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 49f215a..6147633 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2377,7 +2377,7 @@
                 .setScreenOrientation(SCREEN_ORIENTATION_BEHIND)
                 .build();
         final int topOrientation = activityTop.getRequestedConfigurationOrientation();
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, topOrientation);
+        assertEquals(ORIENTATION_PORTRAIT, topOrientation);
     }
 
     private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index e3cb5fb..b46a3b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1728,7 +1728,7 @@
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
         info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
-                0 /* launchMode */);
+                0 /* launchMode */, null /* componentName */);
         info.requiredDisplayCategory = "automotive";
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
                 .build();
@@ -1754,7 +1754,7 @@
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
         info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
-                0 /* launchMode */);
+                0 /* launchMode */, null /* componentName */);
         info.requiredDisplayCategory = "automotive";
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
                 .build();
@@ -1780,7 +1780,7 @@
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
         info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
-                0 /* launchMode */);
+                0 /* launchMode */, null /* componentName */);
         info.requiredDisplayCategory = "automotive";
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
                 .build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 7830e90..3a456fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -16,8 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
@@ -79,24 +80,24 @@
     }
 
     @Test
-    public void testReturnsSkipIfTaskNotInFreeform() {
-        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
-                WINDOWING_MODE_FULLSCREEN).build();
+    public void testReturnsSkipIfTaskNotUsingActivityTypeStandard() {
+        final Task task = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_ASSISTANT).build();
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
     }
 
     @Test
     public void testReturnsSkipIfCurrentParamsHasBounds() {
-        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
-                WINDOWING_MODE_FREEFORM).build();
+        final Task task = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_STANDARD).build();
         mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100);
         assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
     }
 
     @Test
     public void testUsesDefaultBounds() {
-        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
-                WINDOWING_MODE_FREEFORM).build();
+        final Task task = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_STANDARD).build();
         assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
         assertEquals(dpiToPx(task, 840), mResult.mBounds.width());
         assertEquals(dpiToPx(task, 630), mResult.mBounds.height());
@@ -104,8 +105,8 @@
 
     @Test
     public void testUsesDisplayAreaAndWindowingModeFromSource() {
-        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(
-                WINDOWING_MODE_FREEFORM).build();
+        final Task task = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_STANDARD).build();
         TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class);
         mCurrent.mPreferredTaskDisplayArea = mockTaskDisplayArea;
         mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 74dd361..12f9a9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -448,12 +448,15 @@
         final String taskAffinity = "affinity";
         final int uid = 10123;
         final Task task1 = createTaskBuilder(".Task1").build();
-        task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+        final ComponentName componentName = getUniqueComponentName();
+        task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
+                componentName);
         mRecentTasks.add(task1);
 
         // Add another task to recents, and make sure the previous task was removed.
         final Task task2 = createTaskBuilder(".Task2").build();
-        task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+        task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
+                componentName);
         mRecentTasks.add(task2);
         assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
@@ -461,7 +464,7 @@
         // Add another single-instance task to recents, and make sure no task is removed.
         final Task task3 = createTaskBuilder(".Task3").build();
         task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid,
-                LAUNCH_SINGLE_INSTANCE);
+                LAUNCH_SINGLE_INSTANCE, componentName);
         mRecentTasks.add(task3);
         assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 65c7125..753cc62 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -384,7 +384,25 @@
     }
 
     @Test
-    public void testTranslucentActivitiesDontGoInSizeCompactMode() {
+    public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
+        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+        setUpDisplaySizeWithApp(2000, 1000);
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Mock the activity as embedded without additional TaskFragment layer in the task for
+        // simplicity.
+        doReturn(true).when(mActivity).isEmbedded();
+        // Translucent Activity
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).build();
+        doReturn(false).when(translucentActivity).matchParentBounds();
+        doReturn(false).when(translucentActivity).fillsParent();
+        mTask.addChild(translucentActivity);
+        // Check the strategy has not being applied
+        assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
+    }
+
+    @Test
+    public void testTranslucentActivitiesDontGoInSizeCompatMode() {
         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
         setUpDisplaySizeWithApp(2800, 1400);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2472,11 +2490,11 @@
         assertFalse(mActivity.inSizeCompatMode());
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
-
-        assertTrue(mActivity.inSizeCompatMode());
-        // We should remember the original orientation.
+        // Activity is not in size compat mode because the orientation change request came from the
+        // app itself
+        assertFalse(mActivity.inSizeCompatMode());
         assertEquals(mActivity.getResolvedOverrideConfiguration().orientation,
-                Configuration.ORIENTATION_PORTRAIT);
+                Configuration.ORIENTATION_UNDEFINED);
     }
 
     @Test
@@ -3051,6 +3069,25 @@
     }
 
     @Test
+    public void testAppRequestsOrientationChange_notInSizeCompat() {
+        setUpDisplaySizeWithApp(2200, 1800);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+        // Activity is not in size compat mode because the orientation change request came from the
+        // app itself
+        assertFalse(mActivity.inSizeCompatMode());
+
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
+        // Activity should go into size compat mode now because the orientation change came from the
+        // system (device rotation)
+        assertTrue(mActivity.inSizeCompatMode());
+    }
+
+    @Test
     public void testLetterboxDetailsForStatusBar_noLetterbox() {
         setUpDisplaySizeWithApp(2800, 1000);
         addStatusBar(mActivity.mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
index c538727..e30206e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
@@ -41,6 +41,7 @@
 @Presubmit
 public class SurfaceSyncGroupTest {
     private static final String TAG = "SurfaceSyncGroupTest";
+    private static final int TIMEOUT_MS = 100;
 
     private final Executor mExecutor = Runnable::run;
 
@@ -86,7 +87,7 @@
 
         syncTarget2.markSyncReady();
 
-        finishedLatch.await(5, TimeUnit.SECONDS);
+        finishedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch.getCount());
     }
 
@@ -124,13 +125,13 @@
 
         syncTarget1.markSyncReady();
 
-        finishedLatch1.await(5, TimeUnit.SECONDS);
+        finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch1.getCount());
         assertNotEquals(0, finishedLatch2.getCount());
 
         syncTarget2.markSyncReady();
 
-        finishedLatch2.await(5, TimeUnit.SECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch2.getCount());
     }
 
@@ -156,17 +157,17 @@
         // Finish syncTarget2 first to test that the syncGroup is not complete until the merged sync
         // is also done.
         syncTarget2.markSyncReady();
-        finishedLatch2.await(1, TimeUnit.SECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         // Sync did not complete yet
         assertNotEquals(0, finishedLatch2.getCount());
 
         syncTarget1.markSyncReady();
 
         // The first sync will still get a callback when it's sync requirements are done.
-        finishedLatch1.await(5, TimeUnit.SECONDS);
+        finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch1.getCount());
 
-        finishedLatch2.await(5, TimeUnit.SECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch2.getCount());
     }
 
@@ -189,7 +190,7 @@
         syncTarget1.markSyncReady();
 
         // The first sync will still get a callback when it's sync requirements are done.
-        finishedLatch1.await(5, TimeUnit.SECONDS);
+        finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch1.getCount());
 
         syncGroup2.add(syncGroup1, null /* runnable */);
@@ -198,7 +199,7 @@
 
         // Verify that the second sync will receive complete since the merged sync was already
         // completed before the merge.
-        finishedLatch2.await(5, TimeUnit.SECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch2.getCount());
     }
 
@@ -232,8 +233,8 @@
         syncTarget3.markSyncReady();
 
         // Neither SyncGroup will be ready.
-        finishedLatch1.await(1, TimeUnit.SECONDS);
-        finishedLatch2.await(1, TimeUnit.SECONDS);
+        finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
 
         assertEquals(1, finishedLatch1.getCount());
         assertEquals(1, finishedLatch2.getCount());
@@ -241,8 +242,8 @@
         syncTarget2.markSyncReady();
 
         // Both sync groups should be ready after target2 completed.
-        finishedLatch1.await(5, TimeUnit.SECONDS);
-        finishedLatch2.await(5, TimeUnit.SECONDS);
+        finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch1.getCount());
         assertEquals(0, finishedLatch2.getCount());
     }
@@ -275,8 +276,8 @@
         syncTarget1.markSyncReady();
 
         // Only SyncGroup1 will be ready, but SyncGroup2 still needs its own targets to be ready.
-        finishedLatch1.await(1, TimeUnit.SECONDS);
-        finishedLatch2.await(1, TimeUnit.SECONDS);
+        finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
 
         assertEquals(0, finishedLatch1.getCount());
         assertEquals(1, finishedLatch2.getCount());
@@ -284,7 +285,7 @@
         syncTarget3.markSyncReady();
 
         // SyncGroup2 is finished after target3 completed.
-        finishedLatch2.await(1, TimeUnit.SECONDS);
+        finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch2.getCount());
     }
 
@@ -357,6 +358,27 @@
         } catch (InterruptedException e) {
             throw new RuntimeException(e);
         }
+
+        assertEquals(0, finishedLatch.getCount());
+    }
+
+    public void testSurfaceSyncGroupTimeout() throws InterruptedException {
+        final CountDownLatch finishedLatch = new CountDownLatch(1);
+        SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
+        syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown);
+        SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1");
+        SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2");
+
+        syncGroup.add(syncTarget1, null /* runnable */);
+        syncGroup.add(syncTarget2, null /* runnable */);
+        syncGroup.markSyncReady();
+
+        syncTarget1.markSyncReady();
+        assertNotEquals(0, finishedLatch.getCount());
+
+        // Never finish syncTarget2 so it forces the timeout. Timeout is 1 second so wait a little
+        // over 1 second to make sure it completes.
+        finishedLatch.await(1100, TimeUnit.MILLISECONDS);
         assertEquals(0, finishedLatch.getCount());
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index ef2b691..65631ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -52,6 +52,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.DisplayShape;
@@ -62,6 +63,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.window.ClientWindowFrames;
 
 import androidx.test.filters.SmallTest;
 
@@ -340,6 +342,29 @@
     }
 
     @Test
+    public void testWallpaperReportConfigChange() {
+        final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
+        createWallpaperTargetWindow(mDisplayContent);
+        final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
+        makeWindowVisible(wallpaperWindow);
+        wallpaperWindow.mLayoutSeq = mDisplayContent.mLayoutSeq;
+        // Assume the token was invisible and the latest config was reported.
+        wallpaperToken.commitVisibility(false);
+        wallpaperWindow.fillClientWindowFramesAndConfiguration(new ClientWindowFrames(),
+                new MergedConfiguration(), true /* useLatestConfig */, false /* relayoutVisible */);
+        assertTrue(wallpaperWindow.isLastConfigReportedToClient());
+
+        final Rect bounds = wallpaperToken.getBounds();
+        wallpaperToken.setBounds(new Rect(0, 0, bounds.width() / 2, bounds.height() / 2));
+        assertFalse(wallpaperWindow.isLastConfigReportedToClient());
+        // If there is a pending config change when changing to visible, it should tell the client
+        // to redraw by WindowState#reportResized.
+        wallpaperToken.commitVisibility(true);
+        waitUntilHandlersIdle();
+        assertTrue(wallpaperWindow.isLastConfigReportedToClient());
+    }
+
+    @Test
     public void testWallpaperTokenVisibility() {
         final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
         final WindowState wallpaperWindow = createWallpaperWindow(dc);
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index cbac34f..0325ba6 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -31,6 +31,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.DeviceConfig;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
@@ -258,6 +259,9 @@
      */
     public static void showErrorIfSubscriptionAssociatedWithManagedProfile(Context context,
             int subId) {
+        if (!isSwitchToManagedProfileDialogFlagEnabled()) {
+            return;
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             SubscriptionManager subscriptionManager = context.getSystemService(
@@ -286,6 +290,11 @@
         }
     }
 
+    public static boolean isSwitchToManagedProfileDialogFlagEnabled() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+                "enable_switch_to_managed_profile_dialog", false);
+    }
+
     /**
      * Check if the process with given uid is foreground.
      *
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c4d760f..dd7e2d7 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -997,7 +997,8 @@
      */
     public static final int IWLAN_CONGESTION = 0x3C8C;
 
-    /** IKE configuration error resulting in failure  */
+    // Below IWLAN error codes are defined by the UE and do not relate to any 3GPP spec value
+    /** IKE configuration error resulting in failure */
     public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
     /**
      * Sent in the response to an IKE_AUTH message when, for some reason,
@@ -1014,6 +1015,57 @@
     public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005;
     /** Expected to update or bring down an ePDG tunnel, but no tunnel found*/
     public static final int IWLAN_TUNNEL_NOT_FOUND = 0x4006;
+    /**
+     * Failed to apply tunnel transform
+     *
+     * @hide
+     */
+    public static final int IWLAN_TUNNEL_TRANSFORM_FAILED = 0x4007;
+    /**
+     * IWLAN PDN setup failed due to Wi-Fi lost during IKE tunnel setup,
+     * match exception reported by IKE module
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_NETWORK_LOST_EXCEPTION = 0x4008;
+    /**
+     * Carrier-specific error codes during IKEv2 SA setup
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_PRIVATE_PROTOCOL_ERROR = 0x4009;
+    /**
+     * IKE Session closed before child session opened
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED = 0x400A;
+    /**
+     * IKE Init timeout, no response from EPDG
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_INIT_TIMEOUT = 0x400B;
+    /**
+     * DPD message does not get an ack after the re-tx attempts and duration, i.e., times out.
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_DPD_TIMEOUT = 0x400C;
+    /**
+     * The Wi-Fi to Wi-Fi handover of the IMS PDN fails because the network does not respond to the
+     * MOBIKE/rekey mobility message in the expected manner
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_MOBILITY_TIMEOUT = 0x400D;
+    /**
+     * IKE client sent "IKE AUTH request 3" to the network but got "Internal address failure" from
+     * the network since no internal addresses can be assigned.
+     *
+     * @hide
+     */
+    public static final int IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE = 0x400E;
 
     // OEM sepecific error codes. To be used by OEMs when they don't
     // want to reveal error code which would be replaced by ERROR_UNSPECIFIED
@@ -1508,6 +1560,16 @@
         sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE");
         sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT");
         sFailCauseMap.put(IWLAN_TUNNEL_NOT_FOUND, "IWLAN_TUNNEL_NOT_FOUND");
+        sFailCauseMap.put(IWLAN_TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED");
+        sFailCauseMap.put(IWLAN_IKE_INIT_TIMEOUT, "IWLAN_IKE_INIT_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKE_NETWORK_LOST_EXCEPTION, "IWLAN_IKE_NETWORK_LOST_EXCEPTION");
+        sFailCauseMap.put(IWLAN_IKE_PRIVATE_PROTOCOL_ERROR, "IWLAN_IKE_PRIVATE_PROTOCOL_ERROR");
+        sFailCauseMap.put(IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED,
+                "IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED");
+        sFailCauseMap.put(IWLAN_IKE_DPD_TIMEOUT, "IWLAN_IKE_DPD_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKE_MOBILITY_TIMEOUT, "IWLAN_IKE_MOBILITY_TIMEOUT");
+        sFailCauseMap.put(IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE,
+                "IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE");
         sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
         sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
         sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 788d0e8..cf0561d 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1530,6 +1530,10 @@
      * @return the E.164 representation, or null if the given phone number is not valid.
      */
     public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
         return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
     }
 
@@ -1541,6 +1545,10 @@
      * @return the RFC3966 representation, or null if the given phone number is not valid.
      */
     public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
         return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
     }
 
@@ -1591,6 +1599,10 @@
             return false;
         }
 
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
         try {
             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
@@ -1619,6 +1631,10 @@
             return phoneNumber;
         }
 
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
         String result = null;
         try {
@@ -1671,6 +1687,10 @@
      */
     public static String formatNumber(
             String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
         int len = phoneNumber.length();
         for (int i = 0; i < len; i++) {
             if (!isDialable(phoneNumber.charAt(i))) {
@@ -2900,7 +2920,11 @@
         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
         PhoneNumber n1;
         PhoneNumber n2;
-        defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
         try {
             n1 = util.parseAndKeepRawInput(number1, defaultCountryIso);
             n2 = util.parseAndKeepRawInput(number2, defaultCountryIso);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 758372a..5cbbe37 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -4374,7 +4374,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
     public void setSubscriptionUserHandle(int subscriptionId, @Nullable UserHandle userHandle) {
         if (!isValidSubscriptionId(subscriptionId)) {
@@ -4409,7 +4408,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
     public @Nullable UserHandle getSubscriptionUserHandle(int subscriptionId) {
         if (!isValidSubscriptionId(subscriptionId)) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index eb537bb..323fa6f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1848,6 +1848,22 @@
             String gid2, String plmn, String spn, String carrierPrivilegeRules, String apn);
 
     /**
+     * Forcibly sets a package as a carrier provisioning package.
+     *
+     * This override is ephemeral, and will disappear upon phone process restart (including
+     * device reboot).
+     *
+     * WARNING: This API is meant for testing purposes only. This enables automated testing for
+     * cases where the carrier service package is used as a permissioning gate for features such as
+     * restricted carrier network access (restricted APNs and Merged Carrier Wifi)
+     *
+     * @param carrierServicePackage The package that should be marked as the carrier service
+     *     package, or {@code null} to disable the override.
+     */
+    void setCarrierServicePackageOverride(int subId, String carrierServicePackage,
+                String callingPackage);
+
+    /**
      * A test API to return installed carrier id list version.
      */
     int getCarrierIdListVersion(int subId);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 0e852b6..6e4e937 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -58,7 +58,51 @@
     }
 
     /**
-     * Expands the PIP window my using the pinch out gesture.
+     * Drags the PIP window away from the screen edge while not crossing the display center.
+     *
+     * @throws IllegalStateException if default display bounds are not available
+     * @return initial bounds of the PIP window
+     */
+    fun dragPipWindowAwayFromEdge(wmHelper: WindowManagerStateHelper, steps: Int): Rect {
+        val initWindowRect = getWindowRect(wmHelper).clone()
+
+        // initial pointer at the center of the window
+        val startX = initWindowRect.centerX()
+        val y = initWindowRect.centerY()
+
+        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+                ?: throw IllegalStateException("Default display is null")
+
+        // the offset to the right of the display center to drag the window to
+        val offset = 20
+
+        // the actual final x coordinate with the offset included
+        // if the pip window is closer to the right edge of the display the offset is positive
+        // otherwise the offset is negative
+        val endX = displayRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) 1 else -1)
+
+        // drag the window to the left but not beyond the center of the display
+        uiDevice.drag(startX, y, endX, y, steps)
+
+        return initWindowRect
+    }
+
+    /**
+     * Returns true if PIP window is closer to the right edge of the display than left.
+     *
+     * @throws IllegalStateException if default display bounds are not available
+     */
+    private fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
+        val windowRect = getWindowRect(wmHelper)
+
+        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+                ?: throw IllegalStateException("Default display is null")
+
+        return windowRect.centerX() > displayRect.centerX()
+    }
+
+    /**
+     * Expands the PIP window by using the pinch out gesture.
      *
      * @param percent The percentage by which to increase the pip window size.
      * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
@@ -106,7 +150,7 @@
     }
 
     /**
-     * Minimizes the PIP window my using the pinch in gesture.
+     * Minimizes the PIP window by using the pinch in gesture.
      *
      * @param percent The percentage by which to decrease the pip window size.
      * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
@@ -307,6 +351,27 @@
             .waitForAndVerify()
     }
 
+    /**
+     * Waits until the PIP window snaps horizontally to the provided bounds.
+     *
+     * @param finalRightX the final x coordinate of the right edge of the pip window
+     */
+    fun waitForPipToSnapTo(wmHelper: WindowManagerStateHelper, finalBounds: android.graphics.Rect) {
+        wmHelper
+            .StateSyncBuilder()
+            .add("pipWindowSnapped") {
+                val pipAppWindow =
+                    it.wmState.visibleWindows.firstOrNull { window ->
+                        this.windowMatchesAnyOf(window)
+                    }
+                        ?: return@add false
+                val pipRegionBounds = pipAppWindow.frameRegion.bounds
+                return@add pipRegionBounds.left == finalBounds.left &&
+                    pipRegionBounds.right == finalBounds.right
+            }
+            .waitForAndVerify()
+    }
+
     companion object {
         private const val TAG = "PipAppHelper"
         private const val ENTER_PIP_BUTTON_ID = "enter_pip"
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index e2668bc..0f04d6a 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -33,11 +33,15 @@
 import android.util.ArrayMap;
 import android.view.LayoutInflater;
 
+import androidx.annotation.Nullable;
+
 import org.junit.rules.TestRule;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
+import java.util.ArrayList;
+
 /**
  * A ContextWrapper with utilities specifically designed to make Testing easier.
  *
@@ -61,6 +65,7 @@
     private final TestableContentResolver mTestableContentResolver;
     private final TestableSettingsProvider mSettingsProvider;
 
+    private ArrayList<MockServiceResolver> mMockServiceResolvers;
     private ArrayMap<String, Object> mMockSystemServices;
     private ArrayMap<ComponentName, IBinder> mMockServices;
     private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
@@ -214,12 +219,15 @@
     /**
      * Adds a mock service to be connected to by a bindService call.
      * <p>
-     *     Normally a TestableContext will pass through all bind requests to the base context
-     *     but when addMockService has been called for a ComponentName being bound, then
-     *     TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
-     *     with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
-     *     when the service is unbound.
+     * Normally a TestableContext will pass through all bind requests to the base context
+     * but when addMockService has been called for a ComponentName being bound, then
+     * TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
+     * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
+     * when the service is unbound.
      * </p>
+     *
+     * @see #addMockServiceResolver(MockServiceResolver) for custom resolution of service Intents to
+     * ComponentNames
      */
     public void addMockService(ComponentName component, IBinder service) {
         if (mMockServices == null) mMockServices = new ArrayMap<>();
@@ -227,12 +235,38 @@
     }
 
     /**
+     * Strategy to resolve a service {@link Intent} to a mock service {@link ComponentName}.
+     */
+    public interface MockServiceResolver {
+        @Nullable
+        ComponentName resolve(Intent service);
+    }
+
+    /**
+     * Registers a strategy to resolve service intents to registered mock services.
+     * <p>
+     * The result of the first {@link MockServiceResolver} to return a non-null
+     * {@link ComponentName} is used to look up a mock service. The mock service must be registered
+     * via {@link #addMockService(ComponentName, IBinder)} separately, using the same component
+     * name.
+     *
+     * If none of the resolvers return a non-null value, or the first returned component name
+     * does not link to a registered mock service, the bind requests are passed to the base context
+     *
+     * The resolvers are queried in order of registration.
+     */
+    public void addMockServiceResolver(MockServiceResolver resolver) {
+        if (mMockServiceResolvers == null) mMockServiceResolvers = new ArrayList<>();
+        mMockServiceResolvers.add(resolver);
+    }
+
+    /**
      * @see #addMockService(ComponentName, IBinder)
      */
     @Override
     public boolean bindService(Intent service, ServiceConnection conn, int flags) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
+        if (checkMocks(service, conn)) return true;
         return super.bindService(service, conn, flags);
     }
 
@@ -243,7 +277,7 @@
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             Handler handler, UserHandle user) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
+        if (checkMocks(service, conn)) return true;
         return super.bindServiceAsUser(service, conn, flags, handler, user);
     }
 
@@ -254,18 +288,36 @@
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             UserHandle user) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
+        if (checkMocks(service, conn)) return true;
         return super.bindServiceAsUser(service, conn, flags, user);
     }
 
-    private boolean checkMocks(ComponentName component, ServiceConnection conn) {
-        if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
-            if (mActiveServices == null) mActiveServices = new ArrayMap<>();
-            mActiveServices.put(conn, component);
-            conn.onServiceConnected(component, mMockServices.get(component));
-            return true;
+    private boolean checkMocks(Intent service, ServiceConnection conn) {
+        if (mMockServices == null) return false;
+
+        ComponentName serviceComponent = resolveMockServiceComponent(service);
+        if (serviceComponent == null) return false;
+
+        IBinder serviceImpl = mMockServices.get(serviceComponent);
+        if (serviceImpl == null) return false;
+
+        if (mActiveServices == null) mActiveServices = new ArrayMap<>();
+        mActiveServices.put(conn, serviceComponent);
+        conn.onServiceConnected(serviceComponent, serviceImpl);
+        return true;
+    }
+
+    private ComponentName resolveMockServiceComponent(Intent service) {
+        ComponentName specifiedComponentName = service.getComponent();
+        if (specifiedComponentName != null) return specifiedComponentName;
+
+        if (mMockServiceResolvers == null) return null;
+
+        for (MockServiceResolver resolver : mMockServiceResolvers) {
+            ComponentName resolvedComponent = resolver.resolve(service);
+            if (resolvedComponent != null) return resolvedComponent;
         }
-        return false;
+        return null;
     }
 
     /**
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
index d3b7b12..9bfeb63 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
@@ -207,10 +207,14 @@
         }
     }
 
-    private static void validate(long deviceId, @NetworkType int networkType, String networkName) {
+    private static void validate(long deviceId, @NetworkType int networkType, String networkName,
+            NetworkProviderInfo networkProviderInfo) {
         if (deviceId < 0) {
             throw new IllegalArgumentException("DeviceId must be set");
         }
+        if (Objects.isNull(networkProviderInfo)) {
+            throw new IllegalArgumentException("NetworkProviderInfo must be set");
+        }
         if (networkType != NETWORK_TYPE_CELLULAR && networkType != NETWORK_TYPE_WIFI
                 && networkType != NETWORK_TYPE_ETHERNET && networkType != NETWORK_TYPE_UNKNOWN) {
             throw new IllegalArgumentException("Illegal network type");
@@ -230,7 +234,8 @@
             @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) {
         validate(deviceId,
                 networkType,
-                networkName);
+                networkName,
+                networkProviderInfo);
         mDeviceId = deviceId;
         mNetworkProviderInfo = networkProviderInfo;
         mNetworkType = networkType;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index 6046415..15fd817 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -161,9 +161,9 @@
         Resources resources = context.getResources();
         try {
             String servicePackageName = resources.getString(
-                    R.string.shared_connectivity_service_package);
+                    R.string.config_sharedConnectivityServicePackage);
             String serviceIntentAction = resources.getString(
-                    R.string.shared_connectivity_service_intent_action);
+                    R.string.config_sharedConnectivityServiceIntentAction);
             return new SharedConnectivityManager(context, servicePackageName, serviceIntentAction);
         } catch (Resources.NotFoundException e) {
             Log.e(TAG, "To support shared connectivity service on this device, the service's"
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index c53da9c..57108e4 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
@@ -40,8 +41,11 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.R;
+
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 
 /**
@@ -382,6 +386,30 @@
     }
 
     /**
+     * System and settings UI support on the device for instant tether.
+     * @return True if the UI can display Instant Tether network data. False otherwise.
+     */
+    public static boolean areHotspotNetworksEnabledForService(@NonNull Context context) {
+        String servicePackage = context.getResources()
+                .getString(R.string.config_sharedConnectivityServicePackage);
+        return Objects.equals(context.getPackageName(), servicePackage)
+                && context.getResources()
+                        .getBoolean(R.bool.config_hotspotNetworksEnabledForService);
+    }
+
+    /**
+     * System and settings UI support on the device for known networks.
+     * @return True if the UI can display known networks data. False otherwise.
+     */
+    public static boolean areKnownNetworksEnabledForService(@NonNull Context context) {
+        String servicePackage = context.getResources()
+                .getString(R.string.config_sharedConnectivityServicePackage);
+        return Objects.equals(context.getPackageName(), servicePackage)
+                && context.getResources()
+                        .getBoolean(R.bool.config_knownNetworksEnabledForService);
+    }
+
+    /**
      * Implementing application should implement this method.
      *
      * Implementation should initiate a connection to the Hotspot Network indicated.
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
index 8e396b6..8302094 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
@@ -26,12 +26,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
+
 import android.os.Parcel;
 import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 
@@ -39,6 +43,7 @@
  * Unit tests for {@link HotspotNetwork}.
  */
 @SmallTest
+@RunWith(AndroidJUnit4.class)
 public class HotspotNetworkTest {
     private static final long DEVICE_ID = 11L;
     private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
@@ -67,7 +72,7 @@
      */
     @Test
     public void testParcelOperation() {
-        HotspotNetwork network = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
 
         Parcel parcelW = Parcel.obtain();
         network.writeToParcel(parcelW, 0);
@@ -88,30 +93,30 @@
      */
     @Test
     public void testEqualsOperation() {
-        HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
-        HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
+        HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
         assertThat(network1).isEqualTo(network2);
 
-        HotspotNetwork.Builder builder = buildHotspotNetworkBuilder().setDeviceId(DEVICE_ID_1);
+        HotspotNetwork.Builder builder = buildHotspotNetworkBuilder(true).setDeviceId(DEVICE_ID_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
+        builder = buildHotspotNetworkBuilder(true).setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setHostNetworkType(NETWORK_TYPE_1);
+        builder = buildHotspotNetworkBuilder(true).setHostNetworkType(NETWORK_TYPE_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setNetworkName(NETWORK_NAME_1);
+        builder = buildHotspotNetworkBuilder(true).setNetworkName(NETWORK_NAME_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1);
+        builder = buildHotspotNetworkBuilder(true).setHotspotSsid(HOTSPOT_SSID_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1);
+        builder = buildHotspotNetworkBuilder(true).setHotspotBssid(HOTSPOT_BSSID_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder();
-        HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder();
+        builder = buildHotspotNetworkBuilder(true);
+        HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder(true);
         Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType);
 
         assertThat(builder1.build()).isNotEqualTo(builder.build());
@@ -122,7 +127,7 @@
      */
     @Test
     public void testGetMethods() {
-        HotspotNetwork network = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
         ArraySet<Integer> securityTypes = new ArraySet<>();
         Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add);
 
@@ -137,21 +142,30 @@
 
     @Test
     public void testHashCode() {
-        HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
-        HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
+        HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
 
         assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
     }
 
-    private HotspotNetwork.Builder buildHotspotNetworkBuilder() {
+    @Test
+    public void networkProviderInfoNotSet_shouldThrowException() {
+        Exception e = assertThrows(IllegalArgumentException.class,
+                () -> buildHotspotNetworkBuilder(false).build());
+        assertThat(e.getMessage()).contains("NetworkProviderInfo");
+    }
+
+    private HotspotNetwork.Builder buildHotspotNetworkBuilder(boolean withNetworkProviderInfo) {
         HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
                 .setDeviceId(DEVICE_ID)
-                .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
                 .setHostNetworkType(NETWORK_TYPE)
                 .setNetworkName(NETWORK_NAME)
                 .setHotspotSsid(HOTSPOT_SSID)
                 .setHotspotBssid(HOTSPOT_BSSID);
         Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
+        if (withNetworkProviderInfo) {
+            builder.setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
+        }
         return builder;
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index 19effe5..b8b6b767 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -26,10 +26,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
 import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
 import android.net.wifi.sharedconnectivity.app.KnownNetwork;
@@ -86,6 +88,9 @@
     @Mock
     Context mContext;
 
+    @Mock
+    Resources mResources;
+
     static class FakeSharedConnectivityService extends SharedConnectivityService {
         public void attachBaseContext(Context context) {
             super.attachBaseContext(context);
@@ -180,6 +185,48 @@
                 .isEqualTo(KNOWN_NETWORK_CONNECTION_STATUS);
     }
 
+    @Test
+    public void areHotspotNetworksEnabledForService() {
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getPackageName()).thenReturn("package");
+        when(mResources.getString(anyInt())).thenReturn("package");
+        when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+        assertThat(SharedConnectivityService.areHotspotNetworksEnabledForService(mContext))
+                .isTrue();
+    }
+
+    @Test
+    public void areHotspotNetworksEnabledForService_notSamePackage_shouldReturnFalse() {
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getPackageName()).thenReturn("package");
+        when(mResources.getString(anyInt())).thenReturn("other_package");
+        when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+        assertThat(SharedConnectivityService.areHotspotNetworksEnabledForService(mContext))
+                .isFalse();
+    }
+
+    @Test
+    public void areKnownNetworksEnabledForService() {
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getPackageName()).thenReturn("package");
+        when(mResources.getString(anyInt())).thenReturn("package");
+        when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+        assertThat(SharedConnectivityService.areKnownNetworksEnabledForService(mContext)).isTrue();
+    }
+
+    @Test
+    public void areKnownNetworksEnabledForService_notSamePackage_shouldReturnFalse() {
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getPackageName()).thenReturn("package");
+        when(mResources.getString(anyInt())).thenReturn("other_package");
+        when(mResources.getBoolean(anyInt())).thenReturn(true);
+
+        assertThat(SharedConnectivityService.areKnownNetworksEnabledForService(mContext)).isFalse();
+    }
+
     private SharedConnectivityService createService() {
         FakeSharedConnectivityService service = new FakeSharedConnectivityService();
         service.attachBaseContext(mContext);