Merge "Camera Extensions: Add Get API to Framework" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index c4a1cb3..d7a99d3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6880,6 +6880,7 @@
     method public CharSequence getName();
     method @Nullable public String getParentChannelId();
     method public android.net.Uri getSound();
+    method @FlaggedApi("android.app.notification_channel_vibration_effect_api") @Nullable public android.os.VibrationEffect getVibrationEffect();
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
     method public boolean hasUserSetSound();
@@ -6899,6 +6900,7 @@
     method public void setName(CharSequence);
     method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri, android.media.AudioAttributes);
+    method @FlaggedApi("android.app.notification_channel_vibration_effect_api") public void setVibrationEffect(@Nullable android.os.VibrationEffect);
     method public void setVibrationPattern(long[]);
     method public boolean shouldShowLights();
     method public boolean shouldVibrate();
@@ -13839,6 +13841,7 @@
     method public android.content.res.AssetFileDescriptor openRawResourceFd(@RawRes int) throws android.content.res.Resources.NotFoundException;
     method public void parseBundleExtra(String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
     method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @FlaggedApi("android.content.res.register_resource_paths") public static void registerResourcePaths(@NonNull String, @NonNull android.content.pm.ApplicationInfo);
     method public void removeLoaders(@NonNull android.content.res.loader.ResourcesLoader...);
     method @Deprecated public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
     field @AnyRes public static final int ID_NULL = 0; // 0x0
@@ -20268,6 +20271,7 @@
     method @Nullable public android.hardware.input.HostUsiVersion getHostUsiVersion(@NonNull android.view.Display);
     method @Nullable public android.view.InputDevice getInputDevice(int);
     method public int[] getInputDeviceIds();
+    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") @Nullable public android.view.InputDevice.ViewBehavior getInputDeviceViewBehavior(int);
     method @FloatRange(from=0, to=1) public float getMaximumObscuringOpacityForTouch();
     method public boolean isStylusPointerIconEnabled();
     method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
@@ -27320,6 +27324,7 @@
     field public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = 18; // 0x12
     field public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6; // 0x6
     field public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5; // 0x5
+    field @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19; // 0x13
     field public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; // 0x1
     field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0
     field public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; // 0x2
@@ -27411,6 +27416,7 @@
     method public void onRemoveBroadcastInfo(int);
     method public void onRequestAd(@NonNull android.media.tv.AdRequest);
     method public void onRequestBroadcastInfo(@NonNull android.media.tv.BroadcastInfoRequest);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onResumePlayback();
     method public boolean onSelectAudioPresentation(int, int);
     method public boolean onSelectTrack(int, @Nullable String);
     method public abstract void onSetCaptionEnabled(boolean);
@@ -27418,6 +27424,7 @@
     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(int, boolean);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onStopPlayback(int);
     method public void onSurfaceChanged(int, int, int);
     method public long onTimeShiftGetCurrentPosition();
     method public long onTimeShiftGetStartPosition();
@@ -27552,6 +27559,7 @@
     method public boolean onUnhandledInputEvent(android.view.InputEvent);
     method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
     method public void reset();
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void resumePlayback();
     method public void selectAudioPresentation(int, int);
     method public void selectTrack(int, String);
     method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
@@ -27564,6 +27572,7 @@
     method public void setTvMessageEnabled(int, boolean);
     method public void setZOrderMediaOverlay(boolean);
     method public void setZOrderOnTop(boolean);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void stopPlayback(int);
     method public void timeShiftPause();
     method public void timeShiftPlay(String, android.net.Uri);
     method public void timeShiftResume();
@@ -27613,6 +27622,59 @@
 package android.media.tv.ad {
 
   @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public class TvAdManager {
+    method @NonNull public java.util.List<android.media.tv.ad.TvAdServiceInfo> getTvAdServiceList();
+  }
+
+  public abstract static class TvAdManager.TvAdServiceCallback {
+    ctor public TvAdManager.TvAdServiceCallback();
+    method public void onAdServiceAdded(@NonNull String);
+    method public void onAdServiceRemoved(@NonNull String);
+    method public void onAdServiceUpdated(@NonNull String);
+  }
+
+  @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public abstract class TvAdService extends android.app.Service {
+    ctor public TvAdService();
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @Nullable public abstract android.media.tv.ad.TvAdService.Session onCreateSession(@NonNull String, @NonNull String);
+    field public static final String SERVICE_INTERFACE = "android.media.tv.ad.TvAdService";
+    field public static final String SERVICE_META_DATA = "android.media.tv.ad.service";
+  }
+
+  public abstract static class TvAdService.Session implements android.view.KeyEvent.Callback {
+    ctor public TvAdService.Session(@NonNull android.content.Context);
+    method @CallSuper public void layoutSurface(int, int, int, int);
+    method public boolean onGenericMotionEvent(@NonNull android.view.MotionEvent);
+    method public boolean onKeyDown(int, @Nullable android.view.KeyEvent);
+    method public boolean onKeyLongPress(int, @Nullable android.view.KeyEvent);
+    method public boolean onKeyMultiple(int, int, @Nullable android.view.KeyEvent);
+    method public boolean onKeyUp(int, @Nullable android.view.KeyEvent);
+    method public abstract void onRelease();
+    method public abstract boolean onSetSurface(@Nullable android.view.Surface);
+    method public void onSurfaceChanged(int, int, int);
+    method public boolean onTouchEvent(@NonNull android.view.MotionEvent);
+    method public boolean onTrackballEvent(@NonNull android.view.MotionEvent);
+  }
+
+  @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public final class TvAdServiceInfo implements android.os.Parcelable {
+    ctor public TvAdServiceInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName);
+    method public int describeContents();
+    method @NonNull public String getId();
+    method @Nullable public android.content.pm.ServiceInfo getServiceInfo();
+    method @NonNull public java.util.List<java.lang.String> getSupportedTypes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.ad.TvAdServiceInfo> CREATOR;
+  }
+
+  @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public class TvAdView extends android.view.ViewGroup {
+    ctor public TvAdView(@NonNull android.content.Context);
+    ctor public TvAdView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
+    ctor public TvAdView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public void onVisibilityChanged(@NonNull android.view.View, int);
+    method public void prepareAdService(@NonNull String, @NonNull String);
   }
 
 }
@@ -27750,6 +27812,7 @@
     method public void onRecordingTuned(@NonNull String, @NonNull android.net.Uri);
     method public abstract void onRelease();
     method public void onResetInteractiveApp();
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onSelectedTrackInfo(@NonNull java.util.List<android.media.tv.TvTrackInfo>);
     method public abstract boolean onSetSurface(@Nullable android.view.Surface);
     method public void onSetTeletextAppEnabled(boolean);
     method public void onSignalStrength(int);
@@ -27784,6 +27847,7 @@
     method @CallSuper public void requestCurrentVideoBounds();
     method @CallSuper public void requestScheduleRecording(@NonNull String, @NonNull String, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.os.Bundle);
     method @CallSuper public void requestScheduleRecording(@NonNull String, @NonNull String, @NonNull android.net.Uri, long, long, int, @NonNull android.os.Bundle);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") @CallSuper public void requestSelectedTrackInfo();
     method @CallSuper public void requestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
     method @CallSuper public void requestStartRecording(@NonNull String, @Nullable android.net.Uri);
     method @CallSuper public void requestStopRecording(@NonNull String);
@@ -27848,6 +27912,7 @@
     method public void sendCurrentChannelUri(@Nullable android.net.Uri);
     method public void sendCurrentTvInputId(@Nullable String);
     method public void sendCurrentVideoBounds(@NonNull android.graphics.Rect);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void sendSelectedTrackInfo(@Nullable java.util.List<android.media.tv.TvTrackInfo>);
     method public void sendSigningResult(@NonNull String, @NonNull byte[]);
     method public void sendStreamVolume(float);
     method public void sendTimeShiftMode(int);
@@ -27883,6 +27948,7 @@
     method public void onRequestCurrentVideoBounds(@NonNull String);
     method public void onRequestScheduleRecording(@NonNull String, @NonNull String, @NonNull String, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.os.Bundle);
     method public void onRequestScheduleRecording(@NonNull String, @NonNull String, @NonNull String, @NonNull android.net.Uri, long, long, int, @NonNull android.os.Bundle);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onRequestSelectedTrackInfo(@NonNull String);
     method public void onRequestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
     method public void onRequestStartRecording(@NonNull String, @NonNull String, @Nullable android.net.Uri);
     method public void onRequestStopRecording(@NonNull String, @NonNull String);
@@ -50439,6 +50505,10 @@
     method public boolean isFromSource(int);
   }
 
+  @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public static final class InputDevice.ViewBehavior {
+    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public boolean shouldSmoothScroll(int, int);
+  }
+
   public abstract class InputEvent implements android.os.Parcelable {
     method public int describeContents();
     method public final android.view.InputDevice getDevice();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d331455..37be5c7 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -132,6 +132,11 @@
     field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000
   }
 
+  @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public class SignedPackage {
+    method @NonNull public byte[] getCertificateDigest();
+    method @NonNull public String getPkgName();
+  }
+
 }
 
 package android.hardware.usb {
@@ -428,6 +433,8 @@
 
   public class SystemConfigManager {
     method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String);
+    method @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public java.util.Set<android.content.pm.SignedPackage> getEnhancedConfirmationTrustedInstallers();
+    method @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public java.util.Set<android.content.pm.SignedPackage> getEnhancedConfirmationTrustedPackages();
   }
 
   public final class Trace {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7763d84..ca45be8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13450,6 +13450,7 @@
 
   public abstract class Connection extends android.telecom.Conferenceable {
     method @Deprecated public final android.telecom.AudioState getAudioState();
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public final int getCallDirection();
     method @IntRange(from=0) public final long getConnectTimeMillis();
     method public final long getConnectionStartElapsedRealtimeMillis();
     method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
@@ -13464,7 +13465,9 @@
     method public void setTelecomCallId(@NonNull String);
     field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
     field public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 262144; // 0x40000
+    field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EVENT_CALL_QUALITY_REPORT = "android.telecom.event.CALL_QUALITY_REPORT";
     field public static final String EVENT_DEVICE_TO_DEVICE_MESSAGE = "android.telecom.event.DEVICE_TO_DEVICE_MESSAGE";
+    field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EXTRA_CALL_QUALITY_REPORT = "android.telecom.extra.CALL_QUALITY_REPORT";
     field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_TYPE";
     field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_VALUE";
     field public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL";
@@ -13732,6 +13735,7 @@
     method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
     field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
@@ -15040,7 +15044,7 @@
 
   @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public class TelephonyRegistryManager {
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
-    method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
   }
 
   public final class ThermalMitigationRequest implements android.os.Parcelable {
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 8b8576a..b5e5074 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -721,28 +721,28 @@
     /**
      * Returns whether the device is currently locked for the user.
      * <p>
-     * This returns the device locked state for the {@link Context}'s user. If this user is the
-     * current user, then the device is considered "locked" when the lock screen is showing (i.e.
-     * {@link #isKeyguardLocked()} returns {@code true}) and is not trivially dismissible (e.g. with
-     * swipe), and the user has a PIN, pattern, or password.
+     * This method returns the device locked state for the {@link Context}'s user. The device is
+     * considered to be locked for a user when the user's apps are currently inaccessible and some
+     * form of lock screen authentication is required to regain access to them. The lock screen
+     * authentication typically uses PIN, pattern, password, or biometric. Some devices may support
+     * additional methods, such as unlock using a paired smartwatch. "Swipe" does not count as
+     * authentication; if the lock screen is dismissible with swipe, for example due to the lock
+     * screen being set to Swipe or due to the device being kept unlocked by being near a trusted
+     * bluetooth device or in a trusted location, the device is considered unlocked.
+     * <div class="note">
      * <p>
-     * Note: the above definition implies that a user with no PIN, pattern, or password is never
-     * considered locked, even if the lock screen is showing and requesting a SIM card PIN. The
-     * device PIN and SIM PIN are separate. Also, the user is not considered locked if face
-     * authentication has just completed or a trust agent is keeping the device unlocked, since in
-     * these cases the lock screen is dismissible with swipe.
+     * <b>Note:</b> In the case of multiple full users, each user can have their own lock screen
+     * authentication configured. The device-locked state may differ between different users. For
+     * example, the device may be unlocked for the current user, but locked for a non-current user
+     * if lock screen authentication would be required to access that user's apps after switching to
+     * that user.
      * <p>
-     * For a user that is not the current user but can be switched to (usually this means "another
-     * full user"), and that has a PIN, pattern, or password, the device is always considered
-     * locked.
-     * <p>
-     * For a profile with a unified challenge, the device locked state is the same as that of the
-     * parent user.
-     * <p>
-     * For a profile with a separate challenge, the device becomes unlocked when the profile's PIN,
-     * pattern, password, or biometric is verified. It becomes locked when the parent user becomes
-     * locked, the screen turns off, the device reboots, the device policy controller locks the
-     * profile, or the timeout set by the device policy controller expires.
+     * In the case of a profile, when the device goes to the main lock screen, up to two layers of
+     * authentication may be required to regain access to the profile's apps: one to unlock the main
+     * lock screen, and one to unlock the profile (when a separate profile challenge is required).
+     * For a profile, the device is considered to be locked as long as any challenge remains, either
+     * the parent user's challenge (when applicable) or the profile's challenge (when applicable).
+     * </div>
      *
      * @return {@code true} if the device is currently locked for the user
      * @see #isKeyguardLocked()
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d705eeb..8883907 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5946,6 +5946,12 @@
                 // there is enough space to do so (and fall back to the left edge if not).
                 big.setInt(R.id.actions, "setCollapsibleIndentDimen",
                         R.dimen.call_notification_collapsible_indent);
+                if (CallStyle.USE_NEW_ACTION_LAYOUT) {
+                    if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
+                        Log.d(TAG, "setting evenly divided mode on action list");
+                    }
+                    big.setBoolean(R.id.actions, "setEvenlyDividedMode", true);
+                }
             }
             big.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
             if (numActions > 0 && !p.mHideActions) {
@@ -6421,7 +6427,15 @@
                     // Remove full-length color spans and ensure text contrast with the button fill.
                     title = ContrastColorUtil.ensureColorSpanContrast(title, buttonFillColor);
                 }
-                button.setTextViewText(R.id.action0, ensureColorSpanContrast(title, p));
+                final CharSequence label = ensureColorSpanContrast(title, p);
+                if (p.mCallStyleActions && CallStyle.USE_NEW_ACTION_LAYOUT) {
+                    if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
+                        Log.d(TAG, "new action layout enabled, gluing instead of setting text");
+                    }
+                    button.setCharSequence(R.id.action0, "glueLabel", label);
+                } else {
+                    button.setTextViewText(R.id.action0, label);
+                }
                 int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
                         buttonFillColor, mInNightMode);
                 if (tombstone) {
@@ -6438,7 +6452,14 @@
                 button.setColorStateList(R.id.action0, "setButtonBackground",
                         ColorStateList.valueOf(buttonFillColor));
                 if (p.mCallStyleActions) {
-                    button.setImageViewIcon(R.id.action0, action.getIcon());
+                    if (CallStyle.USE_NEW_ACTION_LAYOUT) {
+                        if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
+                            Log.d(TAG, "new action layout enabled, gluing instead of setting icon");
+                        }
+                        button.setIcon(R.id.action0, "glueIcon", action.getIcon());
+                    } else {
+                        button.setImageViewIcon(R.id.action0, action.getIcon());
+                    }
                     boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
                     button.setBoolean(R.id.action0, "setIsPriority", priority);
                     int minWidthDimen =
@@ -9565,6 +9586,15 @@
      * </pre>
      */
     public static class CallStyle extends Style {
+        /**
+         * @hide
+         */
+        public static final boolean USE_NEW_ACTION_LAYOUT = false;
+
+        /**
+         * @hide
+         */
+        public static final boolean DEBUG_NEW_ACTION_LAYOUT = true;
 
         /**
          * @hide
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 15d692a..ab5395e 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,7 @@
  */
 package android.app;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -30,6 +31,9 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.VibrationEffect;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.os.vibrator.persistence.VibrationXmlSerializer;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.text.TextUtils;
@@ -49,6 +53,8 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -146,6 +152,7 @@
     private static final String ATT_LIGHTS = "lights";
     private static final String ATT_LIGHT_COLOR = "light_color";
     private static final String ATT_VIBRATION = "vibration";
+    private static final String ATT_VIBRATION_EFFECT = "vibration_effect";
     private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
     private static final String ATT_SOUND = "sound";
     private static final String ATT_USAGE = "usage";
@@ -253,7 +260,8 @@
     private boolean mSoundRestored = false;
     private boolean mLights;
     private int mLightColor = DEFAULT_LIGHT_COLOR;
-    private long[] mVibration;
+    private long[] mVibrationPattern;
+    private VibrationEffect mVibrationEffect;
     // Bitwise representation of fields that have been changed by the user, preventing the app from
     // making changes to these fields.
     private int mUserLockedFields;
@@ -324,9 +332,13 @@
             mSound = null;
         }
         mLights = in.readByte() != 0;
-        mVibration = in.createLongArray();
-        if (mVibration != null && mVibration.length > MAX_VIBRATION_LENGTH) {
-            mVibration = Arrays.copyOf(mVibration, MAX_VIBRATION_LENGTH);
+        mVibrationPattern = in.createLongArray();
+        if (mVibrationPattern != null && mVibrationPattern.length > MAX_VIBRATION_LENGTH) {
+            mVibrationPattern = Arrays.copyOf(mVibrationPattern, MAX_VIBRATION_LENGTH);
+        }
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            mVibrationEffect =
+                    in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null;
         }
         mUserLockedFields = in.readInt();
         mUserVisibleTaskShown = in.readByte() != 0;
@@ -381,7 +393,15 @@
             dest.writeByte((byte) 0);
         }
         dest.writeByte(mLights ? (byte) 1 : (byte) 0);
-        dest.writeLongArray(mVibration);
+        dest.writeLongArray(mVibrationPattern);
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            if (mVibrationEffect != null) {
+                dest.writeInt(1);
+                mVibrationEffect.writeToParcel(dest, /* flags= */ 0);
+            } else {
+                dest.writeInt(0);
+            }
+        }
         dest.writeInt(mUserLockedFields);
         dest.writeByte(mUserVisibleTaskShown ? (byte) 1 : (byte) 0);
         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
@@ -585,8 +605,8 @@
 
     /**
      * Sets the vibration pattern for notifications posted to this channel. If the provided
-     * pattern is valid (non-null, non-empty), will enable vibration on this channel
-     * (equivalent to calling {@link #enableVibration(boolean)} with {@code true}).
+     * pattern is valid (non-null, non-empty with at least 1 non-zero value), will enable vibration
+     * on this channel (equivalent to calling {@link #enableVibration(boolean)} with {@code true}).
      * Otherwise, vibration will be disabled unless {@link #enableVibration(boolean)} is
      * used with {@code true}, in which case the default vibration will be used.
      *
@@ -595,7 +615,56 @@
      */
     public void setVibrationPattern(long[] vibrationPattern) {
         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
-        this.mVibration = vibrationPattern;
+        this.mVibrationPattern = vibrationPattern;
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            try {
+                this.mVibrationEffect =
+                        VibrationEffect.createWaveform(vibrationPattern, /* repeat= */ -1);
+            } catch (IllegalArgumentException | NullPointerException e) {
+                this.mVibrationEffect = null;
+            }
+        }
+    }
+
+    /**
+     * Sets a {@link VibrationEffect} for notifications posted to this channel. If the
+     * provided effect is non-null, will enable vibration on this channel (equivalent
+     * to calling {@link #enableVibration(boolean)} with {@code true}). Otherwise
+     * vibration will be disabled unless {@link #enableVibration(boolean)} is used with
+     * {@code true}, in which case the default vibration will be used.
+     *
+     * <p>The effect passed here will be returned from {@link #getVibrationEffect()}.
+     * If the provided {@link VibrationEffect} is an equivalent to a wave-form
+     * vibration pattern, the equivalent wave-form pattern will be returned from
+     * {@link #getVibrationPattern()}.
+     *
+     * <p>Note that some {@link VibrationEffect}s may not be playable on some devices.
+     * In cases where such an effect is passed here, vibration will still be enabled
+     * for the channel, but the default vibration will be used. Nonetheless, the
+     * provided effect will be stored and be returned from {@link #getVibrationEffect}
+     * calls, and could be used by the same channel on a different device, for example,
+     * in cases the user backs up and restores to a device that does have the ability
+     * to play the effect, where that effect will be used instead of the default. To
+     * avoid such issues that could make the vibration behavior of your notification
+     * channel differ among different devices, it's recommended that you avoid
+     * vibration effect primitives, as the support for them differs widely among
+     * devices (read {@link VibrationEffect.Composition} for more on vibration
+     * primitives).
+     *
+     * <p>Only modifiable before the channel is submitted to
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
+     *
+     * @see #getVibrationEffect()
+     * @see Vibrator#areEffectsSupported(int...)
+     * @see Vibrator#arePrimitivesSupported(int...)
+     */
+    @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
+    public void setVibrationEffect(@Nullable VibrationEffect effect) {
+        this.mVibrationEnabled = effect != null;
+        this.mVibrationEffect = effect;
+        this.mVibrationPattern =
+                effect == null
+                ? null : effect.computeCreateWaveformOffOnTimingsOrNull();
     }
 
     /**
@@ -768,7 +837,35 @@
      * vibration is not enabled ({@link #shouldVibrate()}).
      */
     public long[] getVibrationPattern() {
-        return mVibration;
+        return mVibrationPattern;
+    }
+
+    /**
+     * Returns the {@link VibrationEffect} for notifications posted to this channel.
+     * The returned effect is derived from either the effect provided in the
+     * {@link #setVibrationEffect(VibrationEffect)} method, or the equivalent vibration effect
+     * of the pattern set via the {@link #setVibrationPattern(long[])} method, based on setter
+     * method that was called last.
+     *
+     * The returned effect will be ignored in one of the following cases:
+     * <ul>
+     *   <li> vibration is not enabled for the channel (i.e. {@link #shouldVibrate()}
+     *        returns {@code false}).
+     *   <li> the effect is not supported/playable by the device. In this case, if
+     *        vibration is enabled for the channel, the default channel vibration will
+     *        be used instead.
+     * </ul>
+     *
+     * @return the {@link VibrationEffect} set via {@link
+     *         #setVibrationEffect(VibrationEffect)}, or the equivalent of the
+     *         vibration set via {@link #setVibrationPattern(long[])}.
+     *
+     *  @see VibrationEffect#createWaveform(long[], int)
+     */
+    @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
+    @Nullable
+    public VibrationEffect getVibrationEffect() {
+        return mVibrationEffect;
     }
 
     /**
@@ -991,7 +1088,19 @@
 
         enableLights(safeBool(parser, ATT_LIGHTS, false));
         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
+        // Set the pattern before the effect, so that we can properly handle cases where the pattern
+        // is null, but the effect is not null (i.e. for non-waveform VibrationEffects - the ones
+        // which cannot be represented as a vibration pattern).
         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            VibrationEffect vibrationEffect = safeVibrationEffect(parser, ATT_VIBRATION_EFFECT);
+            if (vibrationEffect != null) {
+                // Restore the effect only if it is not null. This allows to avoid undoing a
+                // `setVibrationPattern` call above, if that was done with a non-null pattern
+                // (e.g. back up from a version that did not support `setVibrationEffect`).
+                setVibrationEffect(vibrationEffect);
+            }
+        }
         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
         setDeleted(safeBool(parser, ATT_DELETED, false));
@@ -1180,6 +1289,9 @@
         if (getVibrationPattern() != null) {
             out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
         }
+        if (getVibrationEffect() != null) {
+            out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+        }
         if (getUserLockedFields() != 0) {
             out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
         }
@@ -1260,6 +1372,9 @@
         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
         record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isUserVisibleTaskShown()));
         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
+        if (getVibrationEffect() != null) {
+            record.put(ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+        }
         record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
         record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs()));
@@ -1287,6 +1402,30 @@
         return val == null ? null : Uri.parse(val);
     }
 
+    private static String vibrationToString(VibrationEffect effect) {
+        StringWriter writer = new StringWriter();
+        try {
+            VibrationXmlSerializer.serialize(
+                    effect, writer, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to serialize vibration: " + effect, e);
+        }
+        return writer.toString();
+    }
+
+    private static VibrationEffect safeVibrationEffect(TypedXmlPullParser parser, String att) {
+        final String val = parser.getAttributeValue(null, att);
+        if (val != null) {
+            try {
+                return VibrationXmlParser.parseVibrationEffect(
+                        new StringReader(val), VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to read serialized vibration effect", e);
+            }
+        }
+        return null;
+    }
+
     private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
         return parser.getAttributeInt(null, att, defValue);
     }
@@ -1361,7 +1500,8 @@
                 && Objects.equals(getName(), that.getName())
                 && Objects.equals(mDesc, that.mDesc)
                 && Objects.equals(getSound(), that.getSound())
-                && Arrays.equals(mVibration, that.mVibration)
+                && Arrays.equals(mVibrationPattern, that.mVibrationPattern)
+                && Objects.equals(getVibrationEffect(), that.getVibrationEffect())
                 && Objects.equals(getGroup(), that.getGroup())
                 && Objects.equals(getAudioAttributes(), that.getAudioAttributes())
                 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
@@ -1379,9 +1519,9 @@
                 getUserLockedFields(), isUserVisibleTaskShown(),
                 mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(),
                 getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles,
-                mImportanceLockedDefaultApp, mOriginalImportance,
+                mImportanceLockedDefaultApp, mOriginalImportance, getVibrationEffect(),
                 mParentId, mConversationId, mDemoted, mImportantConvo);
-        result = 31 * result + Arrays.hashCode(mVibration);
+        result = 31 * result + Arrays.hashCode(mVibrationPattern);
         return result;
     }
 
@@ -1413,7 +1553,9 @@
                 + ", mSound=" + mSound
                 + ", mLights=" + mLights
                 + ", mLightColor=" + mLightColor
-                + ", mVibration=" + Arrays.toString(mVibration)
+                + ", mVibrationPattern=" + Arrays.toString(mVibrationPattern)
+                + ", mVibrationEffect="
+                        + (mVibrationEffect == null ? "null" : mVibrationEffect.toString())
                 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
                 + ", mUserVisibleTaskShown=" + mUserVisibleTaskShown
                 + ", mVibrationEnabled=" + mVibrationEnabled
@@ -1448,8 +1590,8 @@
         }
         proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
         proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
-        if (mVibration != null) {
-            for (long v : mVibration) {
+        if (mVibrationPattern != null) {
+            for (long v : mVibrationPattern) {
                 proto.write(NotificationChannelProto.VIBRATION, v);
             }
         }
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index edf0a46..6a114f9 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -114,6 +114,22 @@
     }
 
     /**
+     * Remove all Messages from the Handler with the given code.
+     *
+     * This method intentionally avoids creating the Handler if it doesn't
+     * already exist.
+     */
+    private static void handlerRemoveMessages(int what) {
+        synchronized (sLock) {
+            if (sHandler == null) {
+                // Nothing to remove
+                return;
+            }
+            getHandler().removeMessages(what);
+        }
+    }
+
+    /**
      * Add a finisher-runnable to wait for {@link #queue asynchronously processed work}.
      *
      * Used by SharedPreferences$Editor#startCommit().
@@ -156,17 +172,13 @@
         long startTime = System.currentTimeMillis();
         boolean hadMessages = false;
 
-        Handler handler = getHandler();
-
         synchronized (sLock) {
-            if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) {
-                // Delayed work will be processed at processPendingWork() below
-                handler.removeMessages(QueuedWorkHandler.MSG_RUN);
-
-                if (DEBUG) {
-                    hadMessages = true;
-                    Log.d(LOG_TAG, "waiting");
-                }
+            if (DEBUG) {
+                hadMessages = getHandler().hasMessages(QueuedWorkHandler.MSG_RUN);
+            }
+            handlerRemoveMessages(QueuedWorkHandler.MSG_RUN);
+            if (DEBUG && hadMessages) {
+                Log.d(LOG_TAG, "waiting");
             }
 
             // We should not delay any work as this might delay the finishers
@@ -257,7 +269,7 @@
                 sWork = new LinkedList<>();
 
                 // Remove all msg-s as all work will be processed now
-                getHandler().removeMessages(QueuedWorkHandler.MSG_RUN);
+                handlerRemoveMessages(QueuedWorkHandler.MSG_RUN);
             }
 
             if (work.size() > 0) {
diff --git a/core/java/android/app/contextualsearch/OWNERS b/core/java/android/app/contextualsearch/OWNERS
new file mode 100644
index 0000000..0c2612c
--- /dev/null
+++ b/core/java/android/app/contextualsearch/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/contextualsearch/OWNERS
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index a5d4a14..c40b23e 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -50,3 +50,10 @@
   description: "Adds a new voicemail category for notifications"
   bug: "322806700"
 }
+
+flag {
+  name: "notification_channel_vibration_effect_api"
+  namespace: "systemui"
+  description: "This flag enables the API to allow setting VibrationEffect for NotificationChannels"
+  bug: "241732519"
+}
\ No newline at end of file
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e9b94c9..87fb843 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -185,7 +185,7 @@
      *
      * @param context A Context object which should be some mock instance (like the
      * instance of {@link android.test.mock.MockContext}).
-     * @param readPermission The read permision you want this instance should have in the
+     * @param readPermission The read permission you want this instance should have in the
      * test, which is available via {@link #getReadPermission()}.
      * @param writePermission The write permission you want this instance should have
      * in the test, which is available via {@link #getWritePermission()}.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9253998..a126363 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -387,7 +387,7 @@
      * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
      * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in
      * the arguments {@link Bundle}, the Content framework will attempt to
-     * synthesize an QUERY_ARG_SQL* argument using the corresponding
+     * synthesize a QUERY_ARG_SQL* argument using the corresponding
      * QUERY_ARG_SORT* values.
      */
     public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 1f25fd0..451c0e5 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -80,7 +80,7 @@
             long timeout);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
-    void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
+    void requestArchive(String packageName, String callerPackageName, int flags, in IntentSender statusReceiver, in UserHandle userHandle);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
     void requestUnarchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c4bf18d..5df23c0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2362,8 +2362,8 @@
     public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
             throws PackageManager.NameNotFoundException {
         try {
-            mInstaller.requestArchive(packageName, mInstallerPackageName, statusReceiver,
-                    new UserHandle(mUserId));
+            mInstaller.requestArchive(packageName, mInstallerPackageName, /*flags=*/ 0,
+                    statusReceiver, new UserHandle(mUserId));
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
         } catch (RemoteException e) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8744eae..bc29f8b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4479,6 +4479,10 @@
      * the Android Keystore backed by an isolated execution environment. The version indicates
      * which features are implemented in the isolated execution environment:
      * <ul>
+     * <li>300: Ability to include a second IMEI in the ID attestation record, see
+     * {@link android.app.admin.DevicePolicyManager#ID_TYPE_IMEI}.
+     * <li>200: Hardware support for Curve 25519 (including both Ed25519 signature generation and
+     * X25519 key agreement).
      * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
      * for app-generated attestation keys (see {@link
      * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
@@ -4508,6 +4512,11 @@
      * StrongBox</a>. If this feature has a version, the version number indicates which features are
      * implemented in StrongBox:
      * <ul>
+     * <li>300: Ability to include a second IMEI in the ID attestation record, see
+     * {@link android.app.admin.DevicePolicyManager#ID_TYPE_IMEI}.
+     * <li>200: No new features for StrongBox (the Android Keystore environment backed by an
+     * isolated execution environment has gained support for Curve 25519 in this version, but
+     * the implementation backed by a dedicated secure processor is not expected to implement it).
      * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
      * for app-generated attestation keys (see {@link
      * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
diff --git a/core/java/android/content/pm/SignedPackage.java b/core/java/android/content/pm/SignedPackage.java
new file mode 100644
index 0000000..4d1b136
--- /dev/null
+++ b/core/java/android/content/pm/SignedPackage.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A data class representing a package and (SHA-256 hash of) a signing certificate.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+public class SignedPackage {
+    @NonNull
+    private final SignedPackageParcel mData;
+
+    /** @hide */
+    public SignedPackage(@NonNull String pkgName, @NonNull byte[] certificateDigest) {
+        SignedPackageParcel data = new SignedPackageParcel();
+        data.pkgName = pkgName;
+        data.certificateDigest = certificateDigest;
+        mData = data;
+    }
+
+    /** @hide */
+    public SignedPackage(@NonNull SignedPackageParcel data) {
+        mData = data;
+    }
+
+    /** @hide */
+    public final @NonNull SignedPackageParcel getData() {
+        return mData;
+    }
+
+    public @NonNull String getPkgName() {
+        return mData.pkgName;
+    }
+
+    public @NonNull byte[] getCertificateDigest() {
+        return mData.certificateDigest;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SignedPackage that)) return false;
+        return mData.pkgName.equals(that.mData.pkgName) && Arrays.equals(mData.certificateDigest,
+                that.mData.certificateDigest);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mData.pkgName, Arrays.hashCode(mData.certificateDigest));
+    }
+}
diff --git a/core/java/android/content/pm/SignedPackageParcel.aidl b/core/java/android/content/pm/SignedPackageParcel.aidl
new file mode 100644
index 0000000..7957f7f
--- /dev/null
+++ b/core/java/android/content/pm/SignedPackageParcel.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.ComponentName;
+
+/** @hide */
+parcelable SignedPackageParcel {
+    String pkgName;
+    byte[] certificateDigest;
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 1b37092..3671980 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -29,6 +29,7 @@
 import android.annotation.DimenRes;
 import android.annotation.Discouraged;
 import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
 import android.annotation.FontRes;
 import android.annotation.FractionRes;
 import android.annotation.IntegerRes;
@@ -46,6 +47,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
+import android.content.pm.ApplicationInfo;
 import android.content.res.loader.ResourcesLoader;
 import android.graphics.Movie;
 import android.graphics.Typeface;
@@ -2825,4 +2827,22 @@
             }
         }
     }
+
+    /**
+     * Register the resources paths of a package (e.g. a shared library). This will collect the
+     * package resources' paths from its ApplicationInfo and add them to all existing and future
+     * contexts while the application is running.
+     * A second call with the same uniqueId is a no-op.
+     * The paths are not persisted during application restarts. The application is responsible for
+     * calling the API again if this happens.
+     *
+     * @param uniqueId The unique id for the ApplicationInfo object, to detect and ignore repeated
+     *                 API calls.
+     * @param appInfo The ApplicationInfo that contains resources paths of the package.
+     */
+    @FlaggedApi(android.content.res.Flags.FLAG_REGISTER_RESOURCE_PATHS)
+    public static void registerResourcePaths(@NonNull String uniqueId,
+            @NonNull ApplicationInfo appInfo) {
+        throw new UnsupportedOperationException("The implementation has not been done yet.");
+    }
 }
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index db81e84..f660770 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -40,3 +40,12 @@
     description: "Feature flag for creating an frro from a 9-patch"
     bug: "309232726"
 }
+
+flag {
+    name: "register_resource_paths"
+    namespace: "resource_manager"
+    description: "Feature flag for register resource paths for shared library"
+    bug: "306202569"
+    # This flag is read in ResourcesImpl at boot time.
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index c3a09ae..e8d5d37 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -20,6 +20,7 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.content.ComponentName;
@@ -49,7 +50,7 @@
     public static Intent createCredentialSelectorIntent(
             @NonNull RequestInfo requestInfo,
             @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
-            @NonNull
+            @Nullable
             ArrayList<ProviderData> enabledProviderDataList,
             @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
             @NonNull
@@ -57,23 +58,30 @@
             @NonNull ResultReceiver resultReceiver,
             boolean isRequestForAllOptions) {
 
-        Intent intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
-                disabledProviderDataList, resultReceiver);
+        Intent intent;
+        if (enabledProviderDataList != null) {
+            intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
+                    disabledProviderDataList, resultReceiver);
+        } else {
+            intent = createCredentialSelectorIntent(requestInfo,
+                    disabledProviderDataList, resultReceiver);
+        }
         intent.putExtra(Constants.EXTRA_REQ_FOR_ALL_OPTIONS, isRequestForAllOptions);
 
         return intent;
     }
 
-    /** Generate a new launch intent to the Credential Selector UI. */
+    /**
+     * Generate a new launch intent to the Credential Selector UI.
+     *
+     * @hide
+     */
     @NonNull
     public static Intent createCredentialSelectorIntent(
             @NonNull RequestInfo requestInfo,
             @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
-                    @NonNull
-                    ArrayList<ProviderData> enabledProviderDataList,
-            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
-                    @NonNull
-                    ArrayList<DisabledProviderData> disabledProviderDataList,
+            @NonNull
+            ArrayList<DisabledProviderData> disabledProviderDataList,
             @NonNull ResultReceiver resultReceiver) {
         Intent intent = new Intent();
         ComponentName componentName =
@@ -83,9 +91,6 @@
                                         com.android.internal.R.string
                                                 .config_credentialManagerDialogComponent));
         intent.setComponent(componentName);
-
-        intent.putParcelableArrayListExtra(
-                ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
         intent.putParcelableArrayListExtra(
                 ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList);
         intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo);
@@ -95,6 +100,24 @@
         return intent;
     }
 
+    /** Generate a new launch intent to the Credential Selector UI. */
+    @NonNull
+    public static Intent createCredentialSelectorIntent(
+            @NonNull RequestInfo requestInfo,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<ProviderData> enabledProviderDataList,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<DisabledProviderData> disabledProviderDataList,
+            @NonNull ResultReceiver resultReceiver) {
+        Intent intent = createCredentialSelectorIntent(requestInfo,
+                disabledProviderDataList, resultReceiver);
+        intent.putParcelableArrayListExtra(
+                ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
+        return intent;
+    }
+
     /**
      * Creates an Intent that cancels any UI matching the given request token id.
      *
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index cb1d3f5..3b7ade2 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -511,7 +511,7 @@
     Bundle getExtras();
 
     /**
-     * This is an out-of-band way for the the user of a cursor to communicate with the cursor. The
+     * This is an out-of-band way for the user of a cursor to communicate with the cursor. The
      * structure of each bundle is entirely defined by the cursor.
      *
      * <p>One use of this is to tell a cursor that it should retry its network request after it
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 665d8d2..451d6fb 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5313,7 +5313,7 @@
      * </code></pre>
      * <ul>
      * <li>VIDEO_STABILIZATION_MODES: {OFF, PREVIEW}</li>
-     * <li>AE_TARGET_FPS_RANGE: {{<em>, 30}, {</em>, 60}}</li>
+     * <li>AE_TARGET_FPS_RANGE: { {<em>, 30}, {</em>, 60} }</li>
      * <li>DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}</li>
      * </ul>
      * <p>This key is available on all devices.</p>
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 4ebbde7..db992cd 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,9 +16,11 @@
 
 package android.hardware.input;
 
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
 import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -294,6 +296,23 @@
     }
 
     /**
+     * Gets the {@link InputDevice.ViewBehavior} of the input device with a given {@code id}.
+     *
+     * <p>Use this API to query a fresh view behavior instance whenever the input device
+     * changes.
+     *
+     * @param deviceId the id of the input device whose view behavior is being requested.
+     * @return the view behavior of the input device with the provided id, or {@code null} if there
+     *      is not input device with the provided id.
+     */
+    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+    @Nullable
+    public InputDevice.ViewBehavior getInputDeviceViewBehavior(int deviceId) {
+        InputDevice device = getInputDevice(deviceId);
+        return device == null ? null : device.getViewBehavior();
+    }
+
+    /**
      * Gets information about the input device with the specified descriptor.
      * @param descriptor The input device descriptor.
      * @return The input device or null if not found.
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index b7649ba..650aead 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -17,6 +17,8 @@
 package android.os;
 
 import android.content.ComponentName;
+import android.os.Bundle;
+import android.content.pm.SignedPackageParcel;
 
 /**
   * Binder interface to query SystemConfig in the system server.
@@ -57,4 +59,14 @@
      * @see SystemConfigManager#getPreventUserDisablePackages
      */
     List<String> getPreventUserDisablePackages();
+
+    /**
+     * @see SystemConfigManager#getEnhancedConfirmationTrustedPackages
+     */
+    List<SignedPackageParcel> getEnhancedConfirmationTrustedPackages();
+
+    /**
+     * @see SystemConfigManager#getEnhancedConfirmationTrustedInstallers
+     */
+    List<SignedPackageParcel> getEnhancedConfirmationTrustedInstallers();
 }
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 21ffbf1..13bc398 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -16,12 +16,15 @@
 package android.os;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.SignedPackage;
+import android.content.pm.SignedPackageParcel;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -29,6 +32,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 
 /**
@@ -175,4 +179,69 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+
+    /**
+     * Returns a set of signed packages, represented as (packageName, certificateDigest) pairs, that
+     * should be considered "trusted packages" by ECM (Enhanced Confirmation Mode).
+     *
+     * <p>"Trusted packages" are exempt from ECM (i.e., they will never be considered "restricted").
+     *
+     * <p>A package will be considered "trusted package" if and only if it *matches* least one of
+     * the (*packageName*, *certificateDigest*) pairs in this set, where *matches* means satisfying
+     * both of the following:
+     *
+     * <ol>
+     *   <li>The package's name equals *packageName*
+     *   <li>The package is, or was ever, signed by *certificateDigest*, according to the package's
+     *       {@link android.content.pm.SigningDetails}
+     * </ol>
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    @RequiresPermission(Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+    @NonNull
+    public Set<SignedPackage> getEnhancedConfirmationTrustedPackages() {
+        try {
+            List<SignedPackageParcel> parcels = mInterface.getEnhancedConfirmationTrustedPackages();
+            return parcels.stream().map(SignedPackage::new).collect(Collectors.toSet());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a set of signed packages, represented as (packageName, certificateDigest) pairs, that
+     * should be considered "trusted installers" by ECM (Enhanced Confirmation Mode).
+     *
+     * <p>"Trusted installers", and all apps installed by a trusted installer, are exempt from ECM
+     * (i.e., they will never be considered "restricted").
+     *
+     * <p>A package will be considered a "trusted installer" if and only if it *matches* least one
+     * of the (*packageName*, *certificateDigest*) pairs in this set, where *matches* means
+     * satisfying both of the following:
+     *
+     * <ol>
+     *   <li>The package's name equals *packageName*
+     *   <li>The package is, or was ever, signed by *certificateDigest*, according to the package's
+     *       {@link android.content.pm.SigningDetails}
+     * </ol>
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    @RequiresPermission(Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+    @NonNull
+    public Set<SignedPackage> getEnhancedConfirmationTrustedInstallers() {
+        try {
+            List<SignedPackageParcel> parcels =
+                    mInterface.getEnhancedConfirmationTrustedInstallers();
+            return parcels.stream().map(SignedPackage::new).collect(Collectors.toSet());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index f3496e7..b1ef05a 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -71,7 +71,7 @@
  */
 public class ZygoteProcess {
 
-    private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000;
+    private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 60000;
 
     /**
      * Use a relatively short delay, because for app zygote, this is in the critical path of
diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
new file mode 100644
index 0000000..463adf4
--- /dev/null
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -0,0 +1,3 @@
+srazdan@google.com
+volnov@google.com
+hackz@google.com
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 0a813a3..d39c4ce 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -19,12 +19,15 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.SystemService;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
@@ -73,9 +76,15 @@
  * Limit API access to only carrier apps with certain permissions or apps running on
  * privileged UID.
  *
+ * TelephonyRegistryManager is intended for use on devices that implement
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
+ *
  * @hide
  */
 @SystemApi
+@SystemService(Context.TELEPHONY_REGISTRY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
 @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
 public class TelephonyRegistryManager {
 
@@ -389,10 +398,11 @@
     }
 
     /**
-     * Notify call state changed on all subscriptions.
+     * Notify call state changed on all subscriptions, excluding over-the-top VOIP calls (otherwise
+     * known as self-managed calls in the Android Platform).
      *
      * @param state latest call state. e.g, offhook, ringing
-     * @param incomingNumber incoming phone number.
+     * @param incomingNumber incoming phone number or null in the case for OTT VOIP calls
      * @hide
      */
     @SystemApi
@@ -627,17 +637,20 @@
     }
 
     /**
-     * Notify outgoing emergency call.
+     * Notify outgoing emergency call to all applications that have registered a listener
+     * ({@link PhoneStateListener}) or a callback ({@link TelephonyCallback}) to monitor changes in
+     * telephony states.
      * @param simSlotIndex Sender phone ID.
-     * @param subId Sender subscription ID.
+     * @param subscriptionId Sender subscription ID.
      * @param emergencyNumber Emergency number.
      * @hide
      */
     @SystemApi
-    public void notifyOutgoingEmergencyCall(int simSlotIndex, int subId,
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyOutgoingEmergencyCall(int simSlotIndex, int subscriptionId,
             @NonNull EmergencyNumber emergencyNumber) {
         try {
-            sRegistry.notifyOutgoingEmergencyCall(simSlotIndex, subId, emergencyNumber);
+            sRegistry.notifyOutgoingEmergencyCall(simSlotIndex, subscriptionId, emergencyNumber);
         } catch (RemoteException ex) {
             // system process is dead
             throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/tracing/perfetto/DataSourceInstance.java b/core/java/android/tracing/perfetto/DataSourceInstance.java
index 4994501..3710b4d 100644
--- a/core/java/android/tracing/perfetto/DataSourceInstance.java
+++ b/core/java/android/tracing/perfetto/DataSourceInstance.java
@@ -69,4 +69,8 @@
     public final void release() {
         mDataSource.releaseDataSourceInstance(mInstanceIndex);
     }
+
+    public final int getInstanceIndex() {
+        return mInstanceIndex;
+    }
 }
diff --git a/core/java/android/tracing/transition/TransitionDataSource.java b/core/java/android/tracing/transition/TransitionDataSource.java
index baece75..82559da 100644
--- a/core/java/android/tracing/transition/TransitionDataSource.java
+++ b/core/java/android/tracing/transition/TransitionDataSource.java
@@ -24,8 +24,8 @@
 import android.tracing.perfetto.StopCallbackArguments;
 import android.util.proto.ProtoInputStream;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @hide
@@ -38,6 +38,9 @@
     private final Runnable mOnFlushStaticCallback;
     private final Runnable mOnStopStaticCallback;
 
+    private final ConcurrentHashMap<Integer, ConcurrentHashMap<String, Integer>> mHandlerMappings =
+            new ConcurrentHashMap<>();
+
     public TransitionDataSource(Runnable onStart, Runnable onFlush, Runnable onStop) {
         super(DATA_SOURCE_NAME);
         this.mOnStartStaticCallback = onStart;
@@ -47,11 +50,16 @@
 
     @Override
     protected TlsState createTlsState(CreateTlsStateArgs<DataSourceInstance> args) {
-        return new TlsState();
+        return new TlsState(args.getDataSourceInstanceLocked().getInstanceIndex());
     }
 
     public class TlsState {
-        public final Map<String, Integer> handlerMapping = new HashMap<>();
+        public final Map<String, Integer> handlerMapping;
+
+        public TlsState(int instanceIndex) {
+            handlerMapping = mHandlerMappings
+                    .computeIfAbsent(instanceIndex, index -> new ConcurrentHashMap<>());
+        }
     }
 
     @Override
@@ -70,6 +78,7 @@
             @Override
             protected void onStop(StopCallbackArguments args) {
                 mOnStopStaticCallback.run();
+                mHandlerMappings.remove(instanceIndex);
             }
         };
     }
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index f2c3abc..891e2a2 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,7 +16,10 @@
 
 package android.view;
 
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
+
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +31,7 @@
 import android.hardware.SensorManager;
 import android.hardware.input.HostUsiVersion;
 import android.hardware.input.InputDeviceIdentifier;
+import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerGlobal;
 import android.hardware.lights.LightsManager;
 import android.icu.util.ULocale;
@@ -90,6 +94,8 @@
     private final int mAssociatedDisplayId;
     private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
 
+    private final ViewBehavior mViewBehavior = new ViewBehavior(this);
+
     @GuardedBy("mMotionRanges")
     private Vibrator mVibrator; // guarded by mMotionRanges during initialization
 
@@ -539,6 +545,8 @@
             addMotionRange(in.readInt(), in.readInt(), in.readFloat(), in.readFloat(),
                     in.readFloat(), in.readFloat(), in.readFloat());
         }
+
+        mViewBehavior.mShouldSmoothScroll = in.readBoolean();
     }
 
     /**
@@ -571,6 +579,7 @@
         private int mUsiVersionMinor = -1;
         private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
         private List<MotionRange> mMotionRanges = new ArrayList<>();
+        private boolean mShouldSmoothScroll;
 
         /** @see InputDevice#getId() */
         public Builder setId(int id) {
@@ -706,6 +715,16 @@
             return this;
         }
 
+        /**
+         * Sets the view behavior for smooth scrolling ({@code false} by default).
+         *
+         * @see ViewBehavior#shouldSmoothScroll(int, int)
+         */
+        public Builder setShouldSmoothScroll(boolean shouldSmoothScroll) {
+            mShouldSmoothScroll = shouldSmoothScroll;
+            return this;
+        }
+
         /** Build {@link InputDevice}. */
         public InputDevice build() {
             InputDevice device = new InputDevice(
@@ -745,6 +764,8 @@
                         range.getResolution());
             }
 
+            device.setShouldSmoothScroll(mShouldSmoothScroll);
+
             return device;
         }
     }
@@ -1123,6 +1144,22 @@
         return mMotionRanges;
     }
 
+    /**
+     * Provides the {@link ViewBehavior} for the device.
+     *
+     * <p>This behavior is designed to be obtained using the
+     * {@link InputManager#getInputDeviceViewBehavior(int)} API, to allow associating the behavior
+     * with a {@link Context} (since input device is not associated with a context).
+     * The ability to associate the behavior with a context opens capabilities like linking the
+     * behavior to user settings, for example.
+     *
+     * @hide
+     */
+    @NonNull
+    public ViewBehavior getViewBehavior() {
+        return mViewBehavior;
+    }
+
     // Called from native code.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void addMotionRange(int axis, int source,
@@ -1130,6 +1167,11 @@
         mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
     }
 
+    // Called from native code.
+    private void setShouldSmoothScroll(boolean shouldSmoothScroll) {
+        mViewBehavior.mShouldSmoothScroll = shouldSmoothScroll;
+    }
+
     /**
      * Returns the Bluetooth address of this input device, if known.
      *
@@ -1447,6 +1489,82 @@
         }
     }
 
+    /**
+     * Provides information on how views processing {@link MotionEvent}s generated by this input
+     * device should respond to the events. Use {@link InputManager#getInputDeviceViewBehavior(int)}
+     * to get an instance of the view behavior for an input device.
+     *
+     * <p>See an example below how a {@link View} can use this class to determine and apply the
+     * scrolling behavior for a generic {@link MotionEvent}.
+     *
+     * <pre>{@code
+     *     public boolean onGenericMotionEvent(MotionEvent event) {
+     *         InputManager manager = context.getSystemService(InputManager.class);
+     *         ViewBehavior viewBehavior = manager.getInputDeviceViewBehavior(event.getDeviceId());
+     *         // Assume a helper function that tells us which axis to use for scrolling purpose.
+     *         int axis = getScrollAxisForGenericMotionEvent(event);
+     *         int source = event.getSource();
+     *
+     *         boolean shouldSmoothScroll =
+     *                 viewBehavior != null && viewBehavior.shouldSmoothScroll(axis, source);
+     *         // Proceed to running the scrolling logic...
+     *     }
+     * }</pre>
+     *
+     * @see InputManager#getInputDeviceViewBehavior(int)
+     */
+    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+    public static final class ViewBehavior {
+        private static final boolean DEFAULT_SHOULD_SMOOTH_SCROLL = false;
+
+        private final InputDevice mInputDevice;
+
+        // TODO(b/246946631): implement support for InputDevices to adjust this configuration
+        // by axis and source. When implemented, the axis/source specific config will take
+        // precedence over this global config.
+        /** A global smooth scroll configuration applying to all motion axis and input source. */
+        private boolean mShouldSmoothScroll = DEFAULT_SHOULD_SMOOTH_SCROLL;
+
+        /** @hide */
+        public ViewBehavior(@NonNull InputDevice inputDevice) {
+            mInputDevice = inputDevice;
+        }
+
+        /**
+         * Returns whether a view should smooth scroll when scrolling due to a {@link MotionEvent}
+         * generated by the input device.
+         *
+         * <p>Smooth scroll in this case refers to a scroll that animates the transition between
+         * the starting and ending positions of the scroll. When this method returns {@code true},
+         * views should try to animate a scroll generated by this device at the given axis and with
+         * the given source to produce a good scroll user experience. If this method returns
+         * {@code false}, animating scrolls is not necessary.
+         *
+         * <p>If the input device does not have a {@link MotionRange} with the provided axis and
+         * source, this method returns {@code false}.
+         *
+         * @param axis the {@link MotionEvent} axis whose value is used to get the scroll extent.
+         * @param source the {link InputDevice} source from which the {@link MotionEvent} that
+         *      triggers the scroll came.
+         * @return {@code true} if smooth scrolling should be used for the scroll, or {@code false}
+         *      if smooth scrolling is not necessary, or if the provided axis and source combination
+         *      is not available for the input device.
+         */
+        @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+        public boolean shouldSmoothScroll(int axis, int source) {
+            // Note: although we currently do not use axis and source in computing the return value,
+            // we will keep the API params to avoid further public API changes when we start
+            // supporting axis/source configuration. Also, having these params lets OEMs provide
+            // their custom implementation of the API that depends on axis and source.
+
+            // TODO(b/246946631): speed up computation using caching of results.
+            if (mInputDevice.getMotionRange(axis, source) == null) {
+                return false;
+            }
+            return mShouldSmoothScroll;
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel out, int flags) {
         mKeyCharacterMap.writeToParcel(out, flags);
@@ -1484,6 +1602,8 @@
             out.writeFloat(range.mFuzz);
             out.writeFloat(range.mResolution);
         }
+
+        out.writeBoolean(mViewBehavior.mShouldSmoothScroll);
     }
 
     @Override
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 59ec605..9db1060 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -162,6 +162,12 @@
     public float alpha;
 
     /**
+     * Sets a property on this window indicating that its visible region should be considered when
+     * computing TrustedPresentation Thresholds.
+     */
+    public boolean canOccludePresentation;
+
+    /**
      * The input token for the window to which focus should be transferred when this input window
      * can be successfully focused. If null, this input window will not transfer its focus to
      * any other window.
@@ -205,6 +211,7 @@
         focusTransferTarget = other.focusTransferTarget;
         contentSize = new Size(other.contentSize.getWidth(), other.contentSize.getHeight());
         alpha = other.alpha;
+        canOccludePresentation = other.canOccludePresentation;
     }
 
     @Override
@@ -219,6 +226,7 @@
                 .append(", isClone=").append((inputConfig & InputConfig.CLONE) != 0)
                 .append(", contentSize=").append(contentSize)
                 .append(", alpha=").append(alpha)
+                .append(", canOccludePresentation=").append(canOccludePresentation)
                 .toString();
 
     }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3ed0385..3c0ac06 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -168,6 +168,8 @@
             boolean isTrustedOverlay);
     private static native void nativeSetDropInputMode(
             long transactionObj, long nativeObject, int flags);
+    private static native void nativeSetCanOccludePresentation(long transactionObj,
+            long nativeObject, boolean canOccludePresentation);
     private static native void nativeSurfaceFlushJankData(long nativeSurfaceObject);
     private static native boolean nativeClearContentFrameStats(long nativeObject);
     private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
@@ -589,6 +591,28 @@
     public static final int DISPLAY_DECORATION = 0x00000200;
 
     /**
+     * Ignore any destination frame set on the layer. This is used when the buffer scaling mode
+     * is freeze and the destination frame is applied asynchronously with the buffer submission.
+     * This is needed to maintain compatibility for SurfaceView scaling behavior.
+     * See SurfaceView scaling behavior for more details.
+     * @hide
+     */
+    public static final int IGNORE_DESTINATION_FRAME = 0x00000400;
+
+    /**
+     * Special casing for layer that is a refresh rate indicator
+     * @hide
+     */
+    public static final int LAYER_IS_REFRESH_RATE_INDICATOR = 0x00000800;
+
+    /**
+     * Sets a property on this layer indicating that its visible region should be considered when
+     * computing TrustedPresentation Thresholds
+     * @hide
+     */
+    public static final int CAN_OCCLUDE_PRESENTATION = 0x00001000;
+
+    /**
      * Surface creation flag: Creates a surface where color components are interpreted
      * as "non pre-multiplied" by their alpha channel. Of course this flag is
      * meaningless for surfaces without an alpha channel. By default
@@ -4163,6 +4187,29 @@
         }
 
         /**
+         * Sets a property on this SurfaceControl and all its children indicating that the visible
+         * region of this SurfaceControl should be considered when computing TrustedPresentation
+         * Thresholds.
+         * <p>
+         * API Guidance:
+         * The goal of this API is to identify windows that can be used to occlude content on
+         * another window. This includes windows controlled by the user or the system. If the window
+         * is transient, like Toast or notification shade, the window should not set this flag since
+         * the user or the app cannot use the window to occlude content in a persistent manner. All
+         * apps should have this flag set.
+         * <p>
+         * The caller must hold the ACCESS_SURFACE_FLINGER permission.
+         * @hide
+         */
+        public Transaction setCanOccludePresentation(SurfaceControl sc,
+                boolean canOccludePresentation) {
+            checkPreconditions(sc);
+            final int value = (canOccludePresentation) ? CAN_OCCLUDE_PRESENTATION : 0;
+            nativeSetFlags(mNativeObject, sc.mNativeObject, value, CAN_OCCLUDE_PRESENTATION);
+            return this;
+        }
+
+        /**
          * Sends a flush jank data transaction for the given surface.
          * @hide
          */
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1fdd1a5..f54ef38 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -425,12 +425,12 @@
     int mMotionViewNewTop;
 
     /**
-     * The X value associated with the the down motion event
+     * The X value associated with the down motion event
      */
     int mMotionX;
 
     /**
-     * The Y value associated with the the down motion event
+     * The Y value associated with the down motion event
      */
     @UnsupportedAppUsage
     int mMotionY;
@@ -7381,7 +7381,7 @@
 
             scrap.dispatchStartTemporaryDetach();
 
-            // The the accessibility state of the view may change while temporary
+            // the accessibility state of the view may change while temporary
             // detached and we do not allow detached views to fire accessibility
             // events. So we are announcing that the subtree changed giving a chance
             // to clients holding on to a view in this subtree to refresh it.
@@ -7750,7 +7750,7 @@
     }
 
     /**
-     * Abstract positon scroller used to handle smooth scrolling.
+     * Abstract position scroller used to handle smooth scrolling.
      */
     static abstract class AbsPositionScroller {
         public abstract void start(int position);
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 76e97ad..3b7e1e9 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -170,7 +170,7 @@
      * @see android.view.View#measure(int, int)
      *
      * Figure out the dimensions of this Spinner. The width comes from
-     * the widthMeasureSpec as Spinnners can't have their width set to
+     * the widthMeasureSpec as Spinners can't have their width set to
      * UNSPECIFIED. The height is based on the height of the selected item
      * plus padding.
      */
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index e20357fa..1dc90ed 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -15,3 +15,5 @@
 per-file Remote* = file:../appwidget/OWNERS
 
 per-file Toast.java = juliacr@google.com, jeffdq@google.com
+
+per-file flags/notification_widget_flags.aconfig = juliacr@google.com, jeffdq@google.com
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index f234637..14fb17c 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -84,6 +84,14 @@
 }
 
 flag {
+  name: "delegate_unhandled_drags"
+  namespace: "multitasking"
+  description: "Enables delegating unhandled drags to SystemUI"
+  bug: "320797628"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "insets_decoupled_configuration"
   namespace: "windowing_frontend"
   description: "Configuration decoupled from insets"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 4a6e8d7..2e20cce 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -16,7 +16,7 @@
     namespace: "windowing_sdk"
     name: "activity_embedding_overlay_presentation_flag"
     description: "Whether the overlay presentation feature is enabled"
-    bug: "243518738"
+    bug: "293370683"
 }
 
 flag {
@@ -30,7 +30,7 @@
     namespace: "windowing_sdk"
     name: "fullscreen_dim_flag"
     description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
-    bug: "253533308"
+    bug: "293797706"
 }
 
 flag {
@@ -44,7 +44,7 @@
     namespace: "windowing_sdk"
     name: "untrusted_embedding_any_app_permission"
     description: "Feature flag to enable the permission to embed any app in untrusted mode."
-    bug: "289199433"
+    bug: "293647332"
     is_fixed_read_only: true
 }
 
@@ -60,5 +60,5 @@
     namespace: "windowing_sdk"
     name: "embedded_activity_back_nav_flag"
     description: "Refines embedded activity back navigation behavior"
-    bug: "240575809"
+    bug: "293642394"
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index ce6af49..5cda3f2 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -16,16 +16,30 @@
 
 package com.android.internal.widget;
 
+import static android.app.Notification.CallStyle.DEBUG_NEW_ACTION_LAYOUT;
+import static android.app.Notification.CallStyle.USE_NEW_ACTION_LAYOUT;
+import static android.text.style.DynamicDrawableSpan.ALIGN_CENTER;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
 import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableWrapper;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.Icon;
 import android.graphics.drawable.RippleDrawable;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.style.ImageSpan;
+import android.text.style.MetricAffectingSpan;
+import android.text.style.ReplacementSpan;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.RemotableViewMethod;
 import android.widget.Button;
 import android.widget.RemoteViews;
@@ -43,6 +57,14 @@
     private final GradientDrawable mBackground;
     private boolean mPriority;
 
+    private int mInitialDrawablePadding;
+    private int mIconSize;
+
+    private Drawable mIconToGlue;
+    private CharSequence mLabelToGlue;
+    private int mGluedLayoutDirection = LAYOUT_DIRECTION_UNDEFINED;
+    private boolean mGluePending;
+
     public EmphasizedNotificationButton(Context context) {
         this(context, null);
     }
@@ -58,10 +80,25 @@
     public EmphasizedNotificationButton(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
         mRipple = (RippleDrawable) getBackground();
         mRipple.mutate();
         DrawableWrapper inset = (DrawableWrapper) mRipple.getDrawable(0);
         mBackground = (GradientDrawable) inset.getDrawable();
+
+        mIconSize = mContext.getResources().getDimensionPixelSize(
+                R.dimen.notification_actions_icon_drawable_size);
+
+        try (TypedArray typedArray = context.obtainStyledAttributes(
+                attrs, android.R.styleable.TextView, defStyleAttr, defStyleRes)) {
+            mInitialDrawablePadding = typedArray.getDimensionPixelSize(
+                    android.R.styleable.TextView_drawablePadding, 0);
+        }
+
+        if (DEBUG_NEW_ACTION_LAYOUT) {
+            Log.v(TAG, "iconSize = " + mIconSize + "px, "
+                    + "initialDrawablePadding = " + mInitialDrawablePadding + "px");
+        }
     }
 
     @RemotableViewMethod
@@ -95,19 +132,248 @@
         return () -> setImageDrawable(drawable);
     }
 
-    private void setImageDrawable(Drawable drawable) {
+    private void setImageDrawable(@Nullable Drawable drawable) {
         if (drawable != null) {
-            drawable.mutate();
-            drawable.setTintList(getTextColors());
-            drawable.setTintBlendMode(BlendMode.SRC_IN);
-            int iconSize = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.notification_actions_icon_drawable_size);
-            drawable.setBounds(0, 0, iconSize, iconSize);
+            prepareIcon(drawable);
         }
         setCompoundDrawablesRelative(drawable, null, null, null);
     }
 
     /**
+     * Sets an icon to be 'glued' to the label when this button is displayed, so the icon will stay
+     * with the text if the button is wider than needed and the text isn't start-aligned.
+     *
+     * As with {@link #setImageIcon(Icon)}, the Icon will have its size constrained and will be set
+     * to the same color as the text, and this must be called after {@link #setTextColor(int)} for
+     * the latter to work.
+     *
+     * This must be called along with {@link #glueLabel(CharSequence)}, in any order, before the
+     * button is displayed.
+     */
+    @RemotableViewMethod(asyncImpl = "glueIconAsync")
+    public void glueIcon(@Nullable Icon icon) {
+        final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+        setIconToGlue(drawable);
+    }
+
+    /**
+     * @hide
+     */
+    @RemotableViewMethod
+    public Runnable glueIconAsync(@Nullable Icon icon) {
+        final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+        return () -> setIconToGlue(drawable);
+    }
+
+    private void setIconToGlue(@Nullable Drawable icon) {
+        if (!USE_NEW_ACTION_LAYOUT) {
+            Log.e(TAG, "glueIcon: new action layout disabled; doing nothing");
+            return;
+        }
+
+        prepareIcon(icon);
+
+        mIconToGlue = icon;
+        mGluePending = true;
+
+        glueIconAndLabelIfNeeded();
+    }
+
+    private void prepareIcon(@NonNull Drawable drawable) {
+        drawable.mutate();
+        drawable.setTintList(getTextColors());
+        drawable.setTintBlendMode(BlendMode.SRC_IN);
+        drawable.setBounds(0, 0, mIconSize, mIconSize);
+    }
+
+    /**
+     * Sets a label to be 'glued' to the icon when this button is displayed, so the icon will stay
+     * with the text if the button is wider than needed and the text isn't start-aligned.
+     *
+     * This must be called along with {@link #glueIcon(Icon)}, in any order, before the button is
+     * displayed.
+     */
+    @RemotableViewMethod(asyncImpl = "glueLabelAsync")
+    public void glueLabel(@Nullable CharSequence label) {
+        setLabelToGlue(label);
+    }
+
+    /**
+     * @hide
+     */
+    @RemotableViewMethod
+    public Runnable glueLabelAsync(@Nullable CharSequence label) {
+        return () -> setLabelToGlue(label);
+    }
+
+    private void setLabelToGlue(@Nullable CharSequence label) {
+        if (!USE_NEW_ACTION_LAYOUT) {
+            Log.e(TAG, "glueLabel: new action layout disabled; doing nothing");
+            return;
+        }
+
+        mLabelToGlue = label;
+        mGluePending = true;
+
+        glueIconAndLabelIfNeeded();
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+
+        if (DEBUG_NEW_ACTION_LAYOUT) {
+            Log.v(TAG, "onRtlPropertiesChanged: layoutDirection = " + layoutDirection + ", "
+                    + "gluedLayoutDirection = " + mGluedLayoutDirection);
+        }
+
+        if (layoutDirection != mGluedLayoutDirection) {
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.d(TAG, "onRtlPropertiesChanged: layout direction changed; regluing");
+            }
+            mGluePending = true;
+        }
+
+        glueIconAndLabelIfNeeded();
+    }
+
+    private void glueIconAndLabelIfNeeded() {
+        // Don't need to glue:
+
+        if (!mGluePending) {
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.v(TAG, "glueIconAndLabelIfNeeded: glue not pending; doing nothing");
+            }
+            return;
+        }
+
+        if (mIconToGlue == null && mLabelToGlue == null) {
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.v(TAG, "glueIconAndLabelIfNeeded: no icon or label to glue; doing nothing");
+            }
+            mGluePending = false;
+            return;
+        }
+
+        if (!USE_NEW_ACTION_LAYOUT) {
+            Log.e(TAG, "glueIconAndLabelIfNeeded: new action layout disabled; doing nothing");
+            return;
+        }
+
+        // Not ready to glue yet:
+
+        if (!isLayoutDirectionResolved()) {
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.v(TAG, "glueIconAndLabelIfNeeded: "
+                        + "layout direction not resolved; doing nothing");
+            }
+            return;
+        }
+
+        // Ready to glue but don't have an icon *and* a label:
+        //
+        // (Note that this will *not* happen while the button is being initialized, since we won't
+        // be ready to glue. This can only happen if the button is initialized and displayed and
+        // *then* someone calls glueIcon or glueLabel.
+
+        if (mIconToGlue == null) {
+            Log.w(TAG, "glueIconAndLabelIfNeeded: label glued without icon; doing nothing");
+            return;
+        }
+
+        if (mLabelToGlue == null) {
+            Log.w(TAG, "glueIconAndLabelIfNeeded: icon glued without label; doing nothing");
+            return;
+        }
+
+        // Can't glue:
+
+        final int layoutDirection = getLayoutDirection();
+        if (layoutDirection != LAYOUT_DIRECTION_LTR && layoutDirection != LAYOUT_DIRECTION_RTL) {
+            Log.e(TAG, "glueIconAndLabelIfNeeded: "
+                    + "resolved layout direction neither LTR nor RTL; "
+                    + "doing nothing");
+            return;
+        }
+
+        // No excuses left, let's glue it!
+
+        glueIconAndLabel(layoutDirection);
+
+        mGluePending = false;
+        mGluedLayoutDirection = layoutDirection;
+    }
+
+    // Unicode replacement character
+    private static final String IMAGE_SPAN_TEXT = "\ufffd";
+
+    // Unicode no-break space
+    private static final String SPACER_SPAN_TEXT = "\u00a0";
+
+    private static final String LEFT_TO_RIGHT_ISOLATE = "\u2066";
+    private static final String RIGHT_TO_LEFT_ISOLATE = "\u2067";
+    private static final String FIRST_STRONG_ISOLATE = "\u2068";
+    private static final String POP_DIRECTIONAL_ISOLATE = "\u2069";
+
+    private void glueIconAndLabel(int layoutDirection) {
+        final boolean rtlLayout = layoutDirection == LAYOUT_DIRECTION_RTL;
+
+        if (DEBUG_NEW_ACTION_LAYOUT) {
+            Log.d(TAG, "glueIconAndLabel: "
+                    + "icon = " + mIconToGlue + ", "
+                    + "iconSize = " + mIconSize + "px, "
+                    + "initialDrawablePadding = " + mInitialDrawablePadding + "px, "
+                    + "labelToGlue.length = " + mLabelToGlue.length() + ", "
+                    + "rtlLayout = " + rtlLayout);
+        }
+
+        logIfTextDirectionNotFirstStrong();
+
+        final SpannableStringBuilder builder = new SpannableStringBuilder();
+
+        // The text direction of the label might not match the layout direction of the button, so
+        // wrap the entire string in a LEFT-TO-RIGHT ISOLATE or RIGHT-TO-LEFT ISOLATE to match the
+        // layout direction. This puts the icon, padding, and label in the right order.
+        builder.append(rtlLayout ? RIGHT_TO_LEFT_ISOLATE : LEFT_TO_RIGHT_ISOLATE);
+
+        appendSpan(builder, IMAGE_SPAN_TEXT, new ImageSpan(mIconToGlue, ALIGN_CENTER));
+        appendSpan(builder, SPACER_SPAN_TEXT, new SpacerSpan(mInitialDrawablePadding));
+
+        // If the text and layout directions are different, we would end up with the *label* in the
+        // wrong direction, so wrap the label in a FIRST STRONG ISOLATE. This triggers the same
+        // automatic text direction heuristic that Android uses by default.
+        builder.append(FIRST_STRONG_ISOLATE);
+
+        appendSpan(builder, mLabelToGlue, new CenterBesideImageSpan(mIconSize));
+
+        builder.append(POP_DIRECTIONAL_ISOLATE);
+        builder.append(POP_DIRECTIONAL_ISOLATE);
+
+        setText(builder);
+    }
+
+    private void logIfTextDirectionNotFirstStrong() {
+        if (!isTextDirectionResolved()) {
+            Log.e(TAG, "glueIconAndLabel: text direction not resolved; "
+                    + "letting View assume FIRST STRONG");
+        }
+        final int textDirection = getTextDirection();
+        if (textDirection != TEXT_DIRECTION_FIRST_STRONG) {
+            Log.w(TAG, "glueIconAndLabel: "
+                    + "expected text direction TEXT_DIRECTION_FIRST_STRONG "
+                    + "but found " + textDirection + "; "
+                    + "will use a FIRST STRONG ISOLATE regardless");
+        }
+    }
+
+    private void appendSpan(SpannableStringBuilder builder, CharSequence text, Object span) {
+        final int spanStart = builder.length();
+        builder.append(text);
+        final int spanEnd = builder.length();
+        builder.setSpan(span, spanStart, spanEnd, 0);
+    }
+
+    /**
      * Sets whether this view is a priority over its peers (which affects width).
      * Specifically, this is used by {@link NotificationActionListLayout} to give this view width
      * priority ahead of user-defined buttons when allocating horizontal space.
@@ -123,4 +389,104 @@
     public boolean isPriority() {
         return mPriority;
     }
+
+    private static class SpacerSpan extends ReplacementSpan {
+        private int mWidth;
+
+        SpacerSpan(int width) {
+            mWidth = width;
+
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.d(TAG, "width = " + mWidth + "px");
+            }
+        }
+
+
+        @Override
+        public int getSize(@NonNull Paint paint, CharSequence text, int start, int end,
+                           @Nullable Paint.FontMetricsInt fontMetrics) {
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.v(TAG, "getSize returning " + mWidth + "px");
+            }
+
+            return mWidth;
+        }
+
+        @Override
+        public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end,
+                         float x, int top, int y, int bottom, @NonNull Paint paint) {
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.v(TAG, "drawing nothing");
+            }
+
+            // Draw nothing, it's a spacer.
+        }
+
+        private static final String TAG = "SpacerSpan";
+    }
+
+    private static class CenterBesideImageSpan extends MetricAffectingSpan {
+        private int mImageHeight;
+
+        private boolean mMeasured;
+        private int mBaselineShiftOffset;
+
+        CenterBesideImageSpan(int imageHeight) {
+            mImageHeight = imageHeight;
+
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.d(TAG, "imageHeight = " + mImageHeight + "px");
+            }
+        }
+
+        @Override
+        public void updateMeasureState(@NonNull TextPaint textPaint) {
+            final int textHeight = (int) -textPaint.ascent();
+
+            /*
+             * We only need to shift the text *up* if the text is shorter than the image; ImageSpan
+             * with ALIGN_CENTER will shift the *image* up if the text is taller than the image.
+             */
+            if (textHeight < mImageHeight) {
+                mBaselineShiftOffset = -(mImageHeight - textHeight) / 2;
+            } else {
+                mBaselineShiftOffset = 0;
+            }
+
+            mMeasured = true;
+
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.d(TAG, "updateMeasureState: "
+                        + "imageHeight = " + mImageHeight + "px, "
+                        + "textHeight = " + textHeight + "px, "
+                        + "baselineShiftOffset = " + mBaselineShiftOffset + "px");
+            }
+
+            textPaint.baselineShift += mBaselineShiftOffset;
+        }
+
+        @Override
+        public void updateDrawState(TextPaint textPaint) {
+            if (textPaint == null) {
+                Log.e(TAG, "updateDrawState: textPaint is null; doing nothing");
+                return;
+            }
+
+            if (!mMeasured) {
+                Log.e(TAG, "updateDrawState: called without measure; doing nothing");
+                return;
+            }
+
+            if (DEBUG_NEW_ACTION_LAYOUT) {
+                Log.v(TAG, "updateDrawState: "
+                        + "baselineShiftOffset = " + mBaselineShiftOffset + "px");
+            }
+
+            textPaint.baselineShift += mBaselineShiftOffset;
+        }
+
+        private static final String TAG = "CenterBesideImageSpan";
+    }
+
+    private static final String TAG = "EmphasizedNotificationButton";
 }
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index a7a69c9..69d2544 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -16,12 +16,16 @@
 
 package com.android.internal.widget;
 
+import static android.app.Notification.CallStyle.DEBUG_NEW_ACTION_LAYOUT;
+import static android.app.Notification.CallStyle.USE_NEW_ACTION_LAYOUT;
+
 import android.annotation.DimenRes;
 import android.app.Notification;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.RemotableViewMethod;
 import android.view.View;
@@ -41,13 +45,13 @@
  */
 @RemoteViews.RemoteView
 public class NotificationActionListLayout extends LinearLayout {
-
     private final int mGravity;
     private int mTotalWidth = 0;
     private int mExtraStartPadding = 0;
     private ArrayList<TextViewInfo> mMeasureOrderTextViews = new ArrayList<>();
     private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
     private boolean mEmphasizedMode;
+    private boolean mEvenlyDividedMode;
     private int mDefaultPaddingBottom;
     private int mDefaultPaddingTop;
     private int mEmphasizedPaddingTop;
@@ -124,6 +128,42 @@
         }
     }
 
+    private int measureAndReturnEvenlyDividedWidth(int heightMeasureSpec, int innerWidth) {
+        final int numChildren = getChildCount();
+        int childMarginSum = 0;
+        for (int i = 0; i < numChildren; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+                childMarginSum += lp.leftMargin + lp.rightMargin;
+            }
+        }
+
+        final int innerWidthMinusChildMargins = innerWidth - childMarginSum;
+        final int childWidth = innerWidthMinusChildMargins / mNumNotGoneChildren;
+        final int childWidthMeasureSpec =
+                MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
+
+        if (DEBUG_NEW_ACTION_LAYOUT) {
+            Log.v(TAG, "measuring evenly divided width: "
+                    + "numChildren = " + numChildren + ", "
+                    + "innerWidth = " + innerWidth + "px, "
+                    + "childMarginSum = " + childMarginSum + "px, "
+                    + "innerWidthMinusChildMargins = " + innerWidthMinusChildMargins + "px, "
+                    + "childWidth = " + childWidth + "px, "
+                    + "childWidthMeasureSpec = " + MeasureSpec.toString(childWidthMeasureSpec));
+        }
+
+        for (int i = 0; i < numChildren; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                child.measure(childWidthMeasureSpec, heightMeasureSpec);
+            }
+        }
+
+        return innerWidth;
+    }
+
     private int measureAndGetUsedWidth(int widthMeasureSpec, int heightMeasureSpec, int innerWidth,
             boolean collapsePriorityActions) {
         final int numChildren = getChildCount();
@@ -208,11 +248,16 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         countAndRebuildMeasureOrder();
         final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
-        int usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth,
-                false /* collapsePriorityButtons */);
-        if (mNumPriorityChildren != 0 && usedWidth >= innerWidth) {
+        int usedWidth;
+        if (mEvenlyDividedMode) {
+            usedWidth = measureAndReturnEvenlyDividedWidth(heightMeasureSpec, innerWidth);
+        } else {
             usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth,
-                    true /* collapsePriorityButtons */);
+                    false /* collapsePriorityButtons */);
+            if (mNumPriorityChildren != 0 && usedWidth >= innerWidth) {
+                usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth,
+                        true /* collapsePriorityButtons */);
+            }
         }
 
         mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding;
@@ -352,6 +397,38 @@
     }
 
     /**
+     * Sets whether the available width should be distributed evenly among the action buttons.
+     *
+     * When enabled, the available width (after subtracting this layout's padding and all of the
+     * buttons' margins) is divided by the number of (not-GONE) buttons, and each button is forced
+     * to that exact width, even if it is less <em>or more</em> width than they need.
+     *
+     * When disabled, the available width is allocated as buttons need; if that exceeds the
+     * available width, priority buttons are collapsed to just their icon to save space.
+     *
+     * @param evenlyDividedMode whether to enable evenly divided mode
+     */
+    @RemotableViewMethod
+    public void setEvenlyDividedMode(boolean evenlyDividedMode) {
+        if (evenlyDividedMode && !USE_NEW_ACTION_LAYOUT) {
+            Log.e(TAG, "setEvenlyDividedMode(true) called with new action layout disabled; "
+                    + "leaving evenly divided mode disabled");
+            return;
+        }
+
+        if (evenlyDividedMode == mEvenlyDividedMode) {
+            return;
+        }
+
+        if (DEBUG_NEW_ACTION_LAYOUT) {
+            Log.v(TAG, "evenlyDividedMode changed to " + evenlyDividedMode + "; "
+                    + "requesting layout");
+        }
+        mEvenlyDividedMode = evenlyDividedMode;
+        requestLayout();
+    }
+
+    /**
      * Set whether the list is in a mode where some actions are emphasized. This will trigger an
      * equal measuring where all actions are full height and change a few parameters like
      * the padding.
@@ -410,4 +487,5 @@
         }
     }
 
+    private static final String TAG = "NotificationActionListLayout";
 }
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index ae23942..bed7768 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -75,6 +75,7 @@
     jfieldID windowToken;
     jfieldID focusTransferTarget;
     jfieldID alpha;
+    jfieldID canOccludePresentation;
 } gInputWindowHandleClassInfo;
 
 static struct {
@@ -327,6 +328,8 @@
                         javaObjectForIBinder(env, windowInfo.windowToken));
 
     env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.alpha, windowInfo.alpha);
+    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.canOccludePresentation,
+                         windowInfo.canOccludePresentation);
 
     return inputWindowHandle;
 }
@@ -451,6 +454,9 @@
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.alpha, clazz, "alpha", "F");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.canOccludePresentation, clazz,
+                 "canOccludePresentation", "Z");
+
     jclass surfaceControlClazz;
     FIND_CLASS(surfaceControlClazz, "android/view/SurfaceControl");
     GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 2f29cae..917d283 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -17,6 +17,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
index b9c36b9..447b8ec 100644
--- a/core/jni/android_opengl_EGL15.cpp
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -17,6 +17,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index cdc9852..ffd75ea 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -17,6 +17,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
@@ -54,11 +55,11 @@
     jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
     eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
     jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
-    eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal);
+    eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal);
 
     egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
     eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
-    eglsyncGetHandleID = _env->GetMethodID(eglsyncClassLocal, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
 }
 
 static void *
@@ -72,6 +73,14 @@
     return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid));
 }
 
+// TODO: this should be generated from the .spec file, but needs to be renamed and made private
+static jint android_eglDupNativeFenceFDANDROID(JNIEnv *env, jobject, jobject dpy, jobject sync) {
+    EGLDisplay dpy_native = (EGLDisplay)fromEGLHandle(env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync)fromEGLHandle(env, eglsyncGetHandleID, sync);
+
+    return eglDupNativeFenceFDANDROID(dpy_native, sync_native);
+}
+
 // --------------------------------------------------------------------------
 /* EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time ) */
 static jboolean
@@ -89,21 +98,12 @@
     return (jboolean)_returnValue;
 }
 
-static jint android_eglDupNativeFenceFDANDROID(JNIEnv *env, jobject, jobject dpy, jobject sync) {
-    EGLDisplay dpy_native = (EGLDisplay)fromEGLHandle(env, egldisplayGetHandleID, dpy);
-    EGLSync sync_native = (EGLSync)fromEGLHandle(env, eglsyncGetHandleID, sync);
-
-    return eglDupNativeFenceFDANDROID(dpy_native, sync_native);
-}
-
 static const char *classPathName = "android/opengl/EGLExt";
 
 static const JNINativeMethod methods[] = {
-        {"_nativeClassInit", "()V", (void *)nativeClassInit},
-        {"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z",
-         (void *)android_eglPresentationTimeANDROID},
-        {"eglDupNativeFenceFDANDROIDImpl", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)I",
-         (void *)android_eglDupNativeFenceFDANDROID},
+{"_nativeClassInit", "()V", (void*)nativeClassInit },
+{"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z", (void *) android_eglPresentationTimeANDROID },
+{"eglDupNativeFenceFDANDROIDImpl", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)I", (void *)android_eglDupNativeFenceFDANDROID },
 };
 
 int register_android_opengl_jni_EGLExt(JNIEnv *_env)
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index d65b498..2d921ad 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -18,6 +18,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index 3638b87..35a9a68 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -18,6 +18,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 9724e6c..e04b56e 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -18,6 +18,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 1ffa4ec..bccbda6 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -18,6 +18,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index d832558..165262e 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -18,6 +18,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES2/gl2.h>
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 719c6b3..d3fe439 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -18,6 +18,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES3/gl3.h>
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index afe7c63..b123f9d 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -17,6 +17,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <stdint.h>
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 8127433..1e4049b 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -17,6 +17,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES3/gl31.h>
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 7ed7548..e0175f0 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -17,6 +17,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <stdint.h>
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 239c626..aae0da9 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-#include <input/Input.h>
+#include "android_view_InputDevice.h"
 
 #include <android_runtime/AndroidRuntime.h>
+#include <com_android_input_flags.h>
+#include <input/Input.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
-
 #include <nativehelper/ScopedLocalRef.h>
 
-#include "android_view_InputDevice.h"
 #include "android_view_KeyCharacterMap.h"
-
 #include "core_jni_helpers.h"
 
 namespace android {
@@ -34,6 +33,7 @@
 
     jmethodID ctor;
     jmethodID addMotionRange;
+    jmethodID setShouldSmoothScroll;
 } gInputDeviceClassInfo;
 
 jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) {
@@ -103,6 +103,18 @@
         }
     }
 
+    if (com::android::input::flags::input_device_view_behavior_api()) {
+        const InputDeviceViewBehavior& viewBehavior = deviceInfo.getViewBehavior();
+        std::optional<bool> defaultSmoothScroll = viewBehavior.shouldSmoothScroll;
+        if (defaultSmoothScroll.has_value()) {
+            env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.setShouldSmoothScroll,
+                                *defaultSmoothScroll);
+            if (env->ExceptionCheck()) {
+                return NULL;
+            }
+        }
+    }
+
     return env->NewLocalRef(inputDeviceObj.get());
 }
 
@@ -118,6 +130,8 @@
 
     gInputDeviceClassInfo.addMotionRange =
             GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V");
+    gInputDeviceClassInfo.setShouldSmoothScroll =
+            GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "setShouldSmoothScroll", "(Z)V");
     return 0;
 }
 
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index 21de723..ef29c88 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -18,6 +18,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index d8305f0..56ab034 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -19,6 +19,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -42,6 +45,8 @@
 import android.os.Parcel;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.MediaStore.Audio.AudioColumns;
 import android.test.mock.MockContentResolver;
 import android.util.Xml;
@@ -55,6 +60,7 @@
 import com.google.common.base.Strings;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -63,10 +69,15 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NotificationChannelTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private final String CLASS = "android.app.NotificationChannel";
 
     Context mContext;
@@ -294,28 +305,12 @@
         NotificationChannel channel = new NotificationChannel("id", "name", 3);
         channel.setSound(uriToBeRestoredCanonicalized, mAudioAttributes);
 
-        TypedXmlSerializer serializer = Xml.newFastSerializer();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
-        serializer.startDocument(null, true);
-
         // mock the canonicalize in writeXmlForBackup -> getSoundForBackup
         when(mIContentProvider.canonicalize(any(), eq(uriToBeRestoredUncanonicalized)))
                 .thenReturn(uriToBeRestoredCanonicalized);
         when(mIContentProvider.canonicalize(any(), eq(uriToBeRestoredCanonicalized)))
                 .thenReturn(uriToBeRestoredCanonicalized);
 
-        channel.writeXmlForBackup(serializer, mContext);
-        serializer.endDocument();
-        serializer.flush();
-
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        byte[] byteArray = baos.toByteArray();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
-        parser.nextTag();
-
-        NotificationChannel targetChannel = new NotificationChannel("id", "name", 3);
-
         MatrixCursor cursor = new MatrixCursor(new String[] {"_id"});
         cursor.addRow(new Object[] {100L});
 
@@ -350,7 +345,263 @@
         when(mIContentProvider.canonicalize(any(), eq(uriAfterRestoredUncanonicalized)))
                 .thenReturn(uriAfterRestoredCanonicalized);
 
-        targetChannel.populateFromXmlForRestore(parser, true, mContext);
-        assertThat(targetChannel.getSound()).isEqualTo(uriAfterRestoredCanonicalized);
+        NotificationChannel restoredChannel = backUpAndRestore(channel);
+        assertThat(restoredChannel.getSound()).isEqualTo(uriAfterRestoredCanonicalized);
+    }
+
+    @Test
+    public void testVibrationGetters_nonPatternBasedVibrationEffect_waveform() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        // Note that the amplitude used (1) is not the default amplitude, meaning that this effect
+        // does not have an equivalent pattern based effect.
+        VibrationEffect effect = VibrationEffect.createOneShot(123, 1);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern());
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_nonPatternBasedVibrationEffect_nonWaveform() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        VibrationEffect effect =
+                VibrationEffect
+                        .startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose();
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern()); // amplitude not default.
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_patternBasedVibrationEffect_nonRepeating() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2};
+        VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ -1);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertTrue(Arrays.equals(pattern, testedChannel.getVibrationPattern()));
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_patternBasedVibrationEffect_wholeRepeating() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2};
+        VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ 0);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern());
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_patternBasedVibrationEffect_partialRepeating()
+            throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2, 3, 4};
+        VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ 2);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern());
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_nullVibrationEffect() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        channel.setVibrationEffect(null);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertNull(channel.getVibrationEffect());
+            assertNull(channel.getVibrationPattern());
+            assertFalse(channel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_nullPattern() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        channel.setVibrationPattern(null);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isNull();
+            assertNull(testedChannel.getVibrationPattern());
+            assertFalse(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_setEffectOverridesSetPattern() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        VibrationEffect effect =
+                VibrationEffect.createOneShot(123, VibrationEffect.DEFAULT_AMPLITUDE);
+
+        channel.setVibrationPattern(new long[] {60, 80});
+        channel.setVibrationEffect(effect);
+
+        assertThat(channel.getVibrationEffect()).isEqualTo(effect);
+        assertTrue(Arrays.equals(new long[] {0, 123}, channel.getVibrationPattern()));
+        assertTrue(channel.shouldVibrate());
+
+        channel.setVibrationEffect(null);
+
+        assertNull(channel.getVibrationEffect());
+        assertNull(channel.getVibrationPattern());
+        assertFalse(channel.shouldVibrate());
+    }
+
+    @Test
+    public void testVibrationGetters_setPatternOverridesSetEffect() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {0, 123};
+
+        channel.setVibrationEffect(
+                VibrationEffect.createOneShot(123, VibrationEffect.DEFAULT_AMPLITUDE));
+        channel.setVibrationPattern(pattern);
+
+        assertThat(channel.getVibrationEffect())
+                .isEqualTo(VibrationEffect.createWaveform(pattern, -1));
+        assertTrue(Arrays.equals(pattern, channel.getVibrationPattern()));
+        assertTrue(channel.shouldVibrate());
+
+        channel.setVibrationPattern(null);
+
+        assertNull(channel.getVibrationEffect());
+        assertNull(channel.getVibrationPattern());
+        assertFalse(channel.shouldVibrate());
+    }
+
+    @Test
+    public void testEqualityDependsOnVibration() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel1 = new NotificationChannel("id", "name", 3);
+        NotificationChannel channel2 = new NotificationChannel("id", "name", 3);
+        assertThat(channel1).isEqualTo(channel2);
+
+        VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP);
+        long[] pattern = new long[] {1, 2, 3};
+        channel1.setVibrationEffect(effect);
+        channel2.setVibrationEffect(effect);
+        assertThat(channel1).isEqualTo(channel2);
+
+        channel1.setVibrationPattern(pattern);
+        channel2.setVibrationPattern(pattern);
+        assertThat(channel1).isEqualTo(channel2);
+
+        channel1.setVibrationPattern(pattern);
+        channel2.setVibrationEffect(VibrationEffect.createWaveform(pattern, /* repeat= */ -1));
+        // Channels should still be equal, because the pattern and the effect set are equivalent.
+        assertThat(channel1).isEqualTo(channel2);
+
+        channel1.setVibrationEffect(effect);
+        channel2.setVibrationPattern(pattern);
+        assertThat(channel1).isNotEqualTo(channel2);
+    }
+
+    @Test
+    public void testSetVibrationPattern_flagOn_setsEffect() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2, 3};
+
+        channel.setVibrationPattern(pattern);
+
+        assertThat(channel.getVibrationEffect())
+                .isEqualTo(VibrationEffect.createWaveform(pattern, -1));
+    }
+
+    @Test
+    public void testSetVibrationPattern_flagNotOn_doesNotSetEffect() throws Exception {
+        mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        channel.setVibrationPattern(new long[] {1, 2, 3});
+
+        assertNull(channel.getVibrationEffect());
+    }
+
+    /** Backs up a given channel to an XML, and returns the channel read from the XML. */
+    private NotificationChannel backUpAndRestore(NotificationChannel channel) throws Exception {
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+
+        channel.writeXmlForBackup(serializer, mContext);
+        serializer.endDocument();
+        serializer.flush();
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        byte[] byteArray = baos.toByteArray();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
+        parser.nextTag();
+
+        NotificationChannel restoredChannel =
+                new NotificationChannel("default_id", "default_name", 3);
+        restoredChannel.populateFromXmlForRestore(parser, true, mContext);
+
+        return restoredChannel;
+    }
+
+    private NotificationChannel writeToAndReadFromParcel(NotificationChannel channel) {
+        Parcel parcel = Parcel.obtain();
+        channel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        return NotificationChannel.CREATOR.createFromParcel(parcel);
     }
 }
diff --git a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
index bd2f36f..d57f1fc 100644
--- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
+++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
@@ -658,6 +658,52 @@
         Truth.assertThat(matchingPackets).hasSize(1);
     }
 
+    @Test
+    public void canTraceOnFlush() throws InvalidProtocolBufferException, InterruptedException {
+        final int singleIntValue = 101;
+        sInstanceProvider = (ds, idx, config) ->
+                new TestDataSource.TestDataSourceInstance(
+                        ds,
+                        idx,
+                        (args) -> {},
+                        (args) -> sTestDataSource.trace(ctx -> {
+                            final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+                            long forTestingToken = protoOutputStream.start(FOR_TESTING);
+                            long payloadToken = protoOutputStream.start(PAYLOAD);
+                            protoOutputStream.write(SINGLE_INT, singleIntValue);
+                            protoOutputStream.end(payloadToken);
+                            protoOutputStream.end(forTestingToken);
+
+                            ctx.flush();
+                        }),
+                        (args) -> {}
+                );
+
+        final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+                        .setName(sTestDataSource.name).build()).build();
+
+        try {
+            traceMonitor.start();
+        } finally {
+            traceMonitor.stop(mWriter);
+        }
+
+        final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+        final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+        assert rawProtoFromFile != null;
+        final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+                .parseFrom(rawProtoFromFile);
+
+        Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+        final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+                .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+        final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+                .filter(it -> it.getForTesting().getPayload().getSingleInt()
+                        == singleIntValue).toList();
+        Truth.assertThat(matchingPackets).hasSize(1);
+    }
+
     interface RunnableCreator {
         Runnable create(int state, AtomicInteger stateOut);
     }
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index ade20d2..1fd1003 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -66,6 +66,12 @@
     src: "preinstalled-packages-strict-signature.xml",
 }
 
+prebuilt_etc {
+    name: "enhanced-confirmation.xml",
+    sub_dir: "sysconfig",
+    src: "enhanced-confirmation.xml",
+}
+
 // Privapp permission whitelist files
 
 prebuilt_etc {
diff --git a/data/etc/enhanced-confirmation.xml b/data/etc/enhanced-confirmation.xml
new file mode 100644
index 0000000..4a9dd2f
--- /dev/null
+++ b/data/etc/enhanced-confirmation.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+ -->
+
+<!--
+This XML defines an allowlist of packages that should be exempt from ECM (Enhanced Confirmation
+Mode).
+
+Example usage:
+
+    <enhanced-confirmation-trusted-installer
+         package="com.example.app"
+         signature="E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0"/>
+
+This indicates that "com.example.app" should be exempt from ECM, and that, if "com.example.app" is
+an installer, all packages installed via "com.example.app" will also be exempt from ECM.
+-->
+
+<config></config>
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 d0db708..a43a951 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
@@ -259,7 +259,7 @@
     /** One handed mode controller to register transition listener. */
     private final Optional<OneHandedController> mOneHandedOptional;
     /** Drag and drop controller to register listener for onDragStarted. */
-    private final Optional<DragAndDropController> mDragAndDropController;
+    private final DragAndDropController mDragAndDropController;
     /** Used to send bubble events to launcher. */
     private Bubbles.BubbleStateListener mBubbleStateListener;
 
@@ -285,7 +285,7 @@
             BubblePositioner positioner,
             DisplayController displayController,
             Optional<OneHandedController> oneHandedOptional,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler,
             @ShellBackgroundThread ShellExecutor bgExecutor,
@@ -463,7 +463,7 @@
                 });
 
         mOneHandedOptional.ifPresent(this::registerOneHandedState);
-        mDragAndDropController.ifPresent(controller -> controller.addListener(this::collapseStack));
+        mDragAndDropController.addListener(this::collapseStack);
 
         // Clear out any persisted bubbles on disk that no longer have a valid user.
         List<UserInfo> users = mUserManager.getAliveUsers();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index b52a118..d4ed017 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -83,7 +83,6 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -94,8 +93,8 @@
             SystemWindows systemWindows) {
         return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
                 shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
-                displayImeController, displayInsetsController, dragAndDropController, transitions,
-                transactionPool, iconProvider, recentTasks, launchAdjacentController, mainExecutor,
-                mainHandler, systemWindows);
+                displayImeController, displayInsetsController, transitions, transactionPool,
+                iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler,
+                systemWindows);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index fc97c798..0d6a852 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -78,7 +78,6 @@
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
-import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.freeform.FreeformComponents;
 import com.android.wm.shell.fullscreen.FullscreenTaskListener;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
@@ -203,20 +202,6 @@
 
     @WMSingleton
     @Provides
-    static Optional<DragAndDropController> provideDragAndDropController(Context context,
-            ShellInit shellInit,
-            ShellController shellController,
-            ShellCommandHandler shellCommandHandler,
-            DisplayController displayController,
-            UiEventLogger uiEventLogger,
-            IconProvider iconProvider,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return Optional.ofNullable(DragAndDropController.create(context, shellInit, shellController,
-                shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor));
-    }
-
-    @WMSingleton
-    @Provides
     static ShellTaskOrganizer provideShellTaskOrganizer(
             Context context,
             ShellInit shellInit,
@@ -911,7 +896,6 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropControllerOptional,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<BubbleController> bubblesOptional,
             Optional<SplitScreenController> splitScreenOptional,
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 36f06e8..ead5ad2 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
@@ -172,7 +172,7 @@
             BubblePositioner positioner,
             DisplayController displayController,
             @DynamicOverride Optional<OneHandedController> oneHandedOptional,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler,
             @ShellBackgroundThread ShellExecutor bgExecutor,
@@ -338,7 +338,7 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -553,6 +553,24 @@
     }
 
     //
+    // Drag and drop
+    //
+
+    @WMSingleton
+    @Provides
+    static DragAndDropController provideDragAndDropController(Context context,
+            ShellInit shellInit,
+            ShellController shellController,
+            ShellCommandHandler shellCommandHandler,
+            DisplayController displayController,
+            UiEventLogger uiEventLogger,
+            IconProvider iconProvider,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        return new DragAndDropController(context, shellInit, shellController,
+                shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor);
+    }
+
+    //
     // Misc
     //
 
@@ -562,6 +580,7 @@
     @ShellCreateTriggerOverride
     @Provides
     static Object provideIndependentShellComponentsToCreate(
+            DragAndDropController dragAndDropController,
             DefaultMixedHandler defaultMixedHandler) {
         return new Object();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index fdfb6f3..269c369 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -105,25 +105,7 @@
         void onDragStarted();
     }
 
-    /**
-     * Creates {@link DragAndDropController}. Returns {@code null} if the feature is disabled.
-     */
-    public static DragAndDropController create(Context context,
-            ShellInit shellInit,
-            ShellController shellController,
-            ShellCommandHandler shellCommandHandler,
-            DisplayController displayController,
-            UiEventLogger uiEventLogger,
-            IconProvider iconProvider,
-            ShellExecutor mainExecutor) {
-        if (!context.getResources().getBoolean(R.bool.config_enableShellDragDrop)) {
-            return null;
-        }
-        return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
-                displayController, uiEventLogger, iconProvider, mainExecutor);
-    }
-
-    DragAndDropController(Context context,
+    public DragAndDropController(Context context,
             ShellInit shellInit,
             ShellController shellController,
             ShellCommandHandler shellCommandHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 70cb2fc..1b124c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -184,7 +184,7 @@
     private final DisplayController mDisplayController;
     private final DisplayImeController mDisplayImeController;
     private final DisplayInsetsController mDisplayInsetsController;
-    private final Optional<DragAndDropController> mDragAndDropController;
+    private final DragAndDropController mDragAndDropController;
     private final Transitions mTransitions;
     private final TransactionPool mTransactionPool;
     private final IconProvider mIconProvider;
@@ -214,7 +214,7 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -290,7 +290,7 @@
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
         mDisplayInsetsController = displayInsetsController;
-        mDragAndDropController = Optional.of(dragAndDropController);
+        mDragAndDropController = dragAndDropController;
         mTransitions = transitions;
         mTransactionPool = transactionPool;
         mIconProvider = iconProvider;
@@ -328,7 +328,9 @@
             // TODO: Multi-display
             mStageCoordinator = createStageCoordinator();
         }
-        mDragAndDropController.ifPresent(controller -> controller.setSplitScreenController(this));
+        if (mDragAndDropController != null) {
+            mDragAndDropController.setSplitScreenController(this);
+        }
         mWindowDecorViewModel.ifPresent(viewModel -> viewModel.setSplitScreenController(this));
         mDesktopTasksController.ifPresent(controller -> controller.setSplitScreenController(this));
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index e8894a83..b60e361 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -451,6 +451,8 @@
             mPendingResize.onConsumed(aborted);
             mPendingResize = null;
         }
+
+        // TODO: handle transition consumed for active remote handler
     }
 
     void onFinish(WindowContainerTransaction wct) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index c101425..aec4d11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -74,7 +74,6 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -85,7 +84,7 @@
             SystemWindows systemWindows) {
         super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
                 syncQueue, rootTDAOrganizer, displayController, displayImeController,
-                displayInsetsController, dragAndDropController, transitions, transactionPool,
+                displayInsetsController, null, transitions, transactionPool,
                 iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
                 Optional.empty(), mainExecutor);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4355ed2..94519a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -74,6 +74,9 @@
             @Override
             public void onTransitionFinished(WindowContainerTransaction wct,
                     SurfaceControl.Transaction sct) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                        "Finished one-shot remote transition %s for (#%d).", mRemote,
+                        info.getDebugId());
                 if (mRemote.asBinder() != null) {
                     mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
                 }
@@ -82,8 +85,8 @@
                 }
                 mMainExecutor.execute(() -> {
                     finishCallback.onTransitionFinished(wct);
+                    mRemote = null;
                 });
-                mRemote = null;
             }
         };
         Transitions.setRunningRemoteTransitionDelegate(mRemote.getAppThread());
@@ -115,17 +118,24 @@
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Merging registered One-shot remote"
+                + " transition %s for (#%d).", mRemote, info.getDebugId());
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
             public void onTransitionFinished(WindowContainerTransaction wct,
                     SurfaceControl.Transaction sct) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                        "Finished merging one-shot remote transition %s for (#%d).", mRemote,
+                        info.getDebugId());
                 // We have merged, since we sent the transaction over binder, the one in this
                 // process won't be cleared if the remote applied it. We don't actually know if the
                 // remote applied the transaction, but applying twice will break surfaceflinger
                 // so just assume the worst-case and clear the local transaction.
                 t.clear();
-                mMainExecutor.execute(() -> finishCallback.onTransitionFinished(wct));
-                mRemote = null;
+                mMainExecutor.execute(() -> {
+                    finishCallback.onTransitionFinished(wct);
+                    mRemote = null;
+                });
             }
         };
         try {
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 295f4dc..e5bdeee 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -763,41 +763,41 @@
 }; // namespace CanvasJNI
 
 static const JNINativeMethod gMethods[] = {
-    {"nGetNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer},
-    {"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches},
-    {"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches},
-    {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion},
+        {"nGetNativeFinalizer", "()J", (void*)CanvasJNI::getNativeFinalizer},
+        {"nFreeCaches", "()V", (void*)CanvasJNI::freeCaches},
+        {"nFreeTextLayoutCaches", "()V", (void*)CanvasJNI::freeTextLayoutCaches},
+        {"nSetCompatibilityVersion", "(I)V", (void*)CanvasJNI::setCompatibilityVersion},
 
-    // ------------ @FastNative ----------------
-    {"nInitRaster", "(J)J", (void*) CanvasJNI::initRaster},
-    {"nSetBitmap", "(JJ)V", (void*) CanvasJNI::setBitmap},
-    {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+        // ------------ @FastNative ----------------
+        {"nInitRaster", "(J)J", (void*)CanvasJNI::initRaster},
+        {"nSetBitmap", "(JJ)V", (void*)CanvasJNI::setBitmap},
+        {"nGetClipBounds", "(JLandroid/graphics/Rect;)Z", (void*)CanvasJNI::getClipBounds},
 
-    // ------------ @CriticalNative ----------------
-    {"nIsOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
-    {"nGetWidth","(J)I", (void*) CanvasJNI::getWidth},
-    {"nGetHeight","(J)I", (void*) CanvasJNI::getHeight},
-    {"nSave","(JI)I", (void*) CanvasJNI::save},
-    {"nSaveLayer","(JFFFFJ)I", (void*) CanvasJNI::saveLayer},
-    {"nSaveLayerAlpha","(JFFFFI)I", (void*) CanvasJNI::saveLayerAlpha},
-    {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
-    {"nRestoreUnclippedLayer","(JIJ)V", (void*) CanvasJNI::restoreUnclippedLayer},
-    {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
-    {"nRestore","(J)Z", (void*) CanvasJNI::restore},
-    {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
-    {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix},
-    {"nSetMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
-    {"nConcat","(JJ)V", (void*) CanvasJNI::concat},
-    {"nConcat","(J[F)V", (void*) CanvasJNI::concat44},
-    {"nRotate","(JF)V", (void*) CanvasJNI::rotate},
-    {"nScale","(JFF)V", (void*) CanvasJNI::scale},
-    {"nSkew","(JFF)V", (void*) CanvasJNI::skew},
-    {"nTranslate","(JFF)V", (void*) CanvasJNI::translate},
-    {"nQuickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
-    {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
-    {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
-    {"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
-    {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setPaintFilter},
+        // ------------ @CriticalNative ----------------
+        {"nIsOpaque", "(J)Z", (void*)CanvasJNI::isOpaque},
+        {"nGetWidth", "(J)I", (void*)CanvasJNI::getWidth},
+        {"nGetHeight", "(J)I", (void*)CanvasJNI::getHeight},
+        {"nSave", "(JI)I", (void*)CanvasJNI::save},
+        {"nSaveLayer", "(JFFFFJ)I", (void*)CanvasJNI::saveLayer},
+        {"nSaveLayerAlpha", "(JFFFFI)I", (void*)CanvasJNI::saveLayerAlpha},
+        {"nSaveUnclippedLayer", "(JIIII)I", (void*)CanvasJNI::saveUnclippedLayer},
+        {"nRestoreUnclippedLayer", "(JIJ)V", (void*)CanvasJNI::restoreUnclippedLayer},
+        {"nGetSaveCount", "(J)I", (void*)CanvasJNI::getSaveCount},
+        {"nRestore", "(J)Z", (void*)CanvasJNI::restore},
+        {"nRestoreToCount", "(JI)V", (void*)CanvasJNI::restoreToCount},
+        {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix},
+        {"nSetMatrix", "(JJ)V", (void*)CanvasJNI::setMatrix},
+        {"nConcat", "(JJ)V", (void*)CanvasJNI::concat},
+        {"nConcat", "(J[F)V", (void*)CanvasJNI::concat44},
+        {"nRotate", "(JF)V", (void*)CanvasJNI::rotate},
+        {"nScale", "(JFF)V", (void*)CanvasJNI::scale},
+        {"nSkew", "(JFF)V", (void*)CanvasJNI::skew},
+        {"nTranslate", "(JFF)V", (void*)CanvasJNI::translate},
+        {"nQuickReject", "(JJ)Z", (void*)CanvasJNI::quickRejectPath},
+        {"nQuickReject", "(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
+        {"nClipRect", "(JFFFFI)Z", (void*)CanvasJNI::clipRect},
+        {"nClipPath", "(JJI)Z", (void*)CanvasJNI::clipPath},
+        {"nSetDrawFilter", "(JJ)V", (void*)CanvasJNI::setPaintFilter},
 };
 
 // If called from Canvas these are regular JNI
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 84c197d..be93abb 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -111,7 +111,7 @@
     void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
 
     // For playback control
-    void startPlayback(in IBinder sessionToken, int userId);
+    void resumePlayback(in IBinder sessionToken, int userId);
     void stopPlayback(in IBinder sessionToken, int mode, int userId);
 
     // For broadcast info
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 7b20c39..6b247cc 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -63,7 +63,7 @@
     void timeShiftSetMode(int mode);
     void timeShiftEnablePositionTracking(boolean enable);
 
-    void startPlayback();
+    void resumePlayback();
     void stopPlayback(int mode);
 
     // For the recording session
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 999e2cf..d145397 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -80,7 +80,7 @@
     private static final int DO_SET_TV_MESSAGE_ENABLED = 31;
     private static final int DO_NOTIFY_TV_MESSAGE = 32;
     private static final int DO_STOP_PLAYBACK = 33;
-    private static final int DO_START_PLAYBACK = 34;
+    private static final int DO_RESUME_PLAYBACK = 34;
     private static final int DO_SET_VIDEO_FROZEN = 35;
     private static final int DO_NOTIFY_AD_SESSION_DATA = 36;
 
@@ -295,8 +295,8 @@
                 mTvInputSessionImpl.stopPlayback(msg.arg1);
                 break;
             }
-            case DO_START_PLAYBACK: {
-                mTvInputSessionImpl.startPlayback();
+            case DO_RESUME_PLAYBACK: {
+                mTvInputSessionImpl.resumePlayback();
                 break;
             }
             case DO_SET_VIDEO_FROZEN: {
@@ -523,8 +523,8 @@
     }
 
     @Override
-    public void startPlayback() {
-        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK));
+    public void resumePlayback() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RESUME_PLAYBACK));
     }
 
 
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 8720bfe..672f58b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -17,6 +17,7 @@
 package android.media.tv;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -35,6 +36,7 @@
 import android.media.AudioPresentation;
 import android.media.PlaybackParams;
 import android.media.tv.ad.TvAdManager;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppManager;
 import android.net.Uri;
 import android.os.Binder;
@@ -131,7 +133,8 @@
             VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION, VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING,
             VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD, VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE,
             VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID, VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT,
-            VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN})
+            VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN,
+            VIDEO_UNAVAILABLE_REASON_STOPPED})
     public @interface VideoUnavailableReason {}
 
     /** Indicates that this TV message contains watermarking data */
@@ -344,9 +347,9 @@
     /**
      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
-     * it has been stopped by stopPlayback.
-     * @hide
+     * it has been stopped by {@link TvView#stopPlayback(int)}.
      */
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
     public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19;
 
     /** @hide */
@@ -3616,13 +3619,13 @@
             }
         }
 
-        void startPlayback() {
+        void resumePlayback() {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.startPlayback(mToken, mUserId);
+                mService.resumePlayback(mToken, 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 a022b1c..6b03041 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.MainThread;
@@ -35,6 +36,7 @@
 import android.media.AudioPresentation;
 import android.media.PlaybackParams;
 import android.media.tv.ad.TvAdManager;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppService;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -1615,20 +1617,20 @@
          * <p> Note that this is different form {@link #timeShiftPause()} as should release the
          * stream, making it impossible to resume from this position again.
          * @param mode
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
         public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
         }
 
         /**
-         * Starts playback of the Audio, Video, and CC streams.
+         * Resumes playback of the Audio, Video, and CC streams.
          *
          * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
-         * used after stopping playback. This is used to restart playback from the current position
+         * used after stopping playback. This is used to resume playback from the current position
          * in the live broadcast.
-         * @hide
          */
-        public void onStartPlayback() {
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+        public void onResumePlayback() {
         }
 
         /**
@@ -2112,10 +2114,10 @@
         }
 
         /**
-         * Calls {@link #onStartPlayback()}.
+         * Calls {@link #onResumePlayback()}.
          */
-        void startPlayback() {
-            onStartPlayback();
+        void resumePlayback() {
+            onResumePlayback();
         }
 
         /**
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index cb45661..ffc121e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,6 +38,7 @@
 import android.media.tv.TvInputManager.Session;
 import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
 import android.media.tv.TvInputManager.SessionCallback;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppService;
 import android.net.Uri;
 import android.os.Bundle;
@@ -650,10 +652,10 @@
      * <p>The metadata that will continue to be filtered includes the PSI
      * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
      *
-     * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops
+     * <p> Note that this is different from {@link #timeShiftPause()} as this completely drops
      * the stream, making it impossible to resume from this position again.
-     * @hide
      */
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
     public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
         if (mSession != null) {
             mSession.stopPlayback(mode);
@@ -661,16 +663,19 @@
     }
 
     /**
-     * Starts playback of the Audio, Video, and CC streams.
+     * Resumes playback of the Audio, Video, and CC streams.
      *
-     * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
-     * used after stopping playback. This is used to restart playback from the current position
-     * in the live broadcast.
-     * @hide
+     * <p> Note that this is different from {@link #timeShiftResume()} as this is intended to
+     * be used after {@link #stopPlayback(int)} has been called. This is used to resume
+     * playback from the current position in the live broadcast.
+
+     * <p> If this is the first time playback should begin, you will need to use
+     * {@link #tune(String, Uri, Bundle)} to begin playback.
      */
-    public void startPlayback() {
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+    public void resumePlayback() {
         if (mSession != null) {
-            mSession.startPlayback();
+            mSession.resumePlayback();
         }
     }
 
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
index 02c0d75..f373bed 100644
--- a/media/java/android/media/tv/ad/TvAdManager.java
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -437,11 +437,10 @@
     }
 
     /**
-     * Returns the complete list of TV AD service on the system.
+     * Returns the complete list of TV AD services on the system.
      *
      * @return List of {@link TvAdServiceInfo} for each TV AD service that describes its meta
      * information.
-     * @hide
      */
     @NonNull
     public List<TvAdServiceInfo> getTvAdServiceList() {
@@ -1174,8 +1173,7 @@
     }
 
     /**
-     * Callback used to monitor status of the TV AD service.
-     * @hide
+     * Callback used to monitor status of the TV advertisement service.
      */
     public abstract static class TvAdServiceCallback {
         /**
diff --git a/media/java/android/media/tv/ad/TvAdService.java b/media/java/android/media/tv/ad/TvAdService.java
index 4d8f5c8b..953b5cf 100644
--- a/media/java/android/media/tv/ad/TvAdService.java
+++ b/media/java/android/media/tv/ad/TvAdService.java
@@ -17,6 +17,7 @@
 package android.media.tv.ad;
 
 import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,6 +33,7 @@
 import android.media.tv.TvInputManager;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -61,8 +63,8 @@
 
 /**
  * The TvAdService class represents a TV client-side advertisement service.
- * @hide
  */
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
 public abstract class TvAdService extends Service {
     private static final boolean DEBUG = false;
     private static final String TAG = "TvAdService";
@@ -73,7 +75,6 @@
      * Name under which a TvAdService component publishes information about itself. This meta-data
      * must reference an XML resource containing an
      * <code>&lt;{@link android.R.styleable#TvAdService tv-ad-service}&gt;</code> tag.
-     * @hide
      */
     public static final String SERVICE_META_DATA = "android.media.tv.ad.service";
 
@@ -92,7 +93,7 @@
 
     @Override
     @Nullable
-    public final IBinder onBind(@NonNull Intent intent) {
+    public final IBinder onBind(@Nullable Intent intent) {
         ITvAdService.Stub tvAdServiceBinder = new ITvAdService.Stub() {
             @Override
             public void registerCallback(ITvAdServiceCallback cb) {
@@ -398,6 +399,7 @@
          * @param data the original bytes to be signed.
          *
          * @see #onSigningResult(String, byte[])
+         * @hide
          */
         @CallSuper
         public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
@@ -421,22 +423,22 @@
         }
 
         @Override
-        public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
+        public boolean onKeyDown(int keyCode, @Nullable KeyEvent event) {
             return false;
         }
 
         @Override
-        public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
+        public boolean onKeyLongPress(int keyCode, @Nullable KeyEvent event) {
             return false;
         }
 
         @Override
-        public boolean onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event) {
+        public boolean onKeyMultiple(int keyCode, int count, @Nullable KeyEvent event) {
             return false;
         }
 
         @Override
-        public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
+        public boolean onKeyUp(int keyCode, @Nullable KeyEvent event) {
             return false;
         }
 
@@ -484,6 +486,7 @@
          * @param top Top position in pixels, relative to the overlay view.
          * @param right Right position in pixels, relative to the overlay view.
          * @param bottom Bottom position in pixels, relative to the overlay view.
+         *
          */
         @CallSuper
         public void layoutSurface(final int left, final int top, final int right,
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.java b/media/java/android/media/tv/ad/TvAdServiceInfo.java
index 45dc89d..bac14e7 100644
--- a/media/java/android/media/tv/ad/TvAdServiceInfo.java
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.ad;
 
+import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -26,6 +27,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.media.tv.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
@@ -42,8 +44,8 @@
 
 /**
  * This class is used to specify meta information of a TV AD service.
- * @hide
  */
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
 public final class TvAdServiceInfo implements Parcelable {
     private static final boolean DEBUG = false;
     private static final String TAG = "TvAdServiceInfo";
@@ -95,6 +97,7 @@
         in.readStringList(mTypes);
     }
 
+    @NonNull
     public static final Creator<TvAdServiceInfo> CREATOR = new Creator<TvAdServiceInfo>() {
         @Override
         public TvAdServiceInfo createFromParcel(Parcel in) {
diff --git a/media/java/android/media/tv/ad/TvAdView.java b/media/java/android/media/tv/ad/TvAdView.java
index 604dbd5..be88506 100644
--- a/media/java/android/media/tv/ad/TvAdView.java
+++ b/media/java/android/media/tv/ad/TvAdView.java
@@ -17,6 +17,7 @@
 package android.media.tv.ad;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -29,6 +30,7 @@
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
 import android.media.tv.ad.TvAdManager.Session.FinishedInputEventCallback;
+import android.media.tv.flags.Flags;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -48,9 +50,9 @@
 import java.util.concurrent.Executor;
 
 /**
- * Displays contents of TV AD services.
- * @hide
+ * Displays contents of TV advertisement services.
  */
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
 public class TvAdView extends ViewGroup {
     private static final String TAG = "TvAdView";
     private static final boolean DEBUG = false;
@@ -182,14 +184,12 @@
         return true;
     }
 
-    /** @hide */
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         createSessionMediaView();
     }
 
-    /** @hide */
     @Override
     public void onDetachedFromWindow() {
         removeSessionMediaView();
@@ -381,6 +381,7 @@
      * @param event The input event.
      * @return If you handled the event, return {@code true}. If you want to allow the event to be
      *         handled by the next receiver, return {@code false}.
+     * @hide
      */
     public boolean onUnhandledInputEvent(@NonNull InputEvent event) {
         return false;
@@ -422,7 +423,7 @@
     }
 
     @Override
-    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
+    public boolean dispatchKeyEvent(@Nullable KeyEvent event) {
         if (super.dispatchKeyEvent(event)) {
             return true;
         }
@@ -465,6 +466,7 @@
 
     /**
      * Stops the AD service.
+     * @hide
      */
     public void stopAdService() {
         if (DEBUG) {
@@ -479,6 +481,7 @@
      * Resets the AD service.
      *
      * <p>This releases the resources of the corresponding {@link TvAdService.Session}.
+     * @hide
      */
     public void resetAdService() {
         if (DEBUG) {
@@ -493,6 +496,7 @@
      * Sends current video bounds to related TV AD service.
      *
      * @param bounds the rectangle area for rendering the current video.
+     * @hide
      */
     public void sendCurrentVideoBounds(@NonNull Rect bounds) {
         if (DEBUG) {
@@ -508,6 +512,7 @@
      *
      * @param channelUri The current channel URI; {@code null} if there is no currently tuned
      *                   channel.
+     * @hide
      */
     public void sendCurrentChannelUri(@Nullable Uri channelUri) {
         if (DEBUG) {
@@ -520,6 +525,7 @@
 
     /**
      * Sends track info list to related TV AD service.
+     * @hide
      */
     public void sendTrackInfoList(@Nullable List<TvTrackInfo> tracks) {
         if (DEBUG) {
@@ -536,6 +542,7 @@
      * @param inputId The current TV input ID whose channel is tuned. {@code null} if no channel is
      *                tuned.
      * @see android.media.tv.TvInputInfo
+     * @hide
      */
     public void sendCurrentTvInputId(@Nullable String inputId) {
         if (DEBUG) {
@@ -577,6 +584,7 @@
      *     can also be added to the params.
      *
      * @see #ERROR_KEY_METHOD_NAME
+     * @hide
      */
     public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
         if (DEBUG) {
@@ -599,6 +607,7 @@
      *             {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}.
      *             See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on
      *             how to parse this data.
+     * @hide
      */
     public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType int type,
             @NonNull Bundle data) {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 7b6dc38..6b0620c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -17,6 +17,7 @@
 package android.media.tv.interactive;
 
 import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
@@ -44,6 +45,7 @@
 import android.media.tv.TvRecordingInfo;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback;
 import android.net.Uri;
 import android.net.http.SslCertificate;
@@ -959,12 +961,12 @@
 
         /**
          * Called when the TV App sends the selected track info as a response to
-         * requestSelectedTrackInfo.
+         * {@link #requestSelectedTrackInfo()}
          *
-         * @param tracks
-         * @hide
+         * @param tracks A list of {@link TvTrackInfo} that are currently selected
          */
-        public void onSelectedTrackInfo(List<TvTrackInfo> tracks) {
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+        public void onSelectedTrackInfo(@NonNull List<TvTrackInfo> tracks) {
         }
 
         @Override
@@ -1373,13 +1375,13 @@
         }
 
         /**
-         * Requests the currently selected {@link TvTrackInfo} from the TV App.
+         * Requests a list of the currently selected {@link TvTrackInfo} from the TV App.
          *
          * <p> Normally, track info cannot be synchronized until the channel has
-         * been changed. This is used when the session of the TIAS is newly
-         * created and the normal synchronization has not happened yet.
-         * @hide
+         * been changed. This is used when the session of the {@link TvInteractiveAppService}
+         * is newly created and the normal synchronization has not happened yet.
          */
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
         @CallSuper
         public void requestSelectedTrackInfo() {
             executeOrPostRunnableOnMainThread(() -> {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 3b29574..584ea84 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -17,6 +17,7 @@
 package android.media.tv.interactive;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -30,6 +31,7 @@
 import android.media.tv.TvRecordingInfo;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppManager.Session;
 import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
 import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
@@ -585,8 +587,9 @@
     /**
      * Sends the currently selected track info to the TV Interactive App.
      *
-     * @hide
+     * @param tracks list of {@link TvTrackInfo} of the currently selected track(s)
      */
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
     public void sendSelectedTrackInfo(@Nullable List<TvTrackInfo> tracks) {
         if (DEBUG) {
             Log.d(TAG, "sendSelectedTrackInfo");
@@ -1248,8 +1251,8 @@
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
         public void onRequestSelectedTrackInfo(@NonNull String iAppServiceId) {
         }
 
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
index 70202463..c18a2de 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
@@ -41,7 +41,7 @@
 
 
     public EffectsTest() {
-        Log.d(TAG, "contructor");
+        Log.d(TAG, "constructor");
     }
 
     @Override
diff --git a/opengl/java/android/opengl/EGLExt.java b/opengl/java/android/opengl/EGLExt.java
index 1570e0e..31104a0 100644
--- a/opengl/java/android/opengl/EGLExt.java
+++ b/opengl/java/android/opengl/EGLExt.java
@@ -46,14 +46,6 @@
         _nativeClassInit();
     }
 
-    // C function EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time )
-
-    public static native boolean eglPresentationTimeANDROID(
-        EGLDisplay dpy,
-        EGLSurface sur,
-        long time
-    );
-
     /**
      * Retrieves the SyncFence for an EGLSync created with EGL_SYNC_NATIVE_FENCE_ANDROID
      *
@@ -83,4 +75,13 @@
     }
 
     private static native int eglDupNativeFenceFDANDROIDImpl(EGLDisplay display, EGLSync sync);
+
+    // C function EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time )
+
+    public static native boolean eglPresentationTimeANDROID(
+        EGLDisplay dpy,
+        EGLSurface sur,
+        long time
+    );
+
 }
diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp
index 27ddff9..63e6c50 100644
--- a/packages/CrashRecovery/services/Android.bp
+++ b/packages/CrashRecovery/services/Android.bp
@@ -3,7 +3,15 @@
     srcs: [
         "java/**/*.java",
         "java/**/*.aidl",
+        ":statslog-crashrecovery-java-gen",
     ],
-    path: "java",
     visibility: ["//frameworks/base:__subpackages__"],
 }
+
+genrule {
+    name: "statslog-crashrecovery-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module crashrecovery " +
+        "--javaPackage com.android.server.crashrecovery.proto --javaClass CrashRecoveryStatsLog --worksource",
+    out: ["com/android/server/crashrecovery/proto/CrashRecoveryStatsLog.java"],
+}
diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
index dd54334..dffe4e2 100644
--- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
@@ -46,11 +46,11 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.PackageWatchdog.FailureReasons;
 import com.android.server.PackageWatchdog.PackageHealthObserver;
 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
 import com.android.server.am.SettingsToPropertiesMapper;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -390,7 +390,7 @@
             return;
         }
 
-        FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
+        CrashRecoveryStatsLog.write(CrashRecoveryStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
         // Try our best to reset all settings possible, and once finished
         // rethrow any exception that we encountered
         Exception res = null;
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 2007079..50322f0 100644
--- a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -39,13 +39,13 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Preconditions;
 import com.android.server.PackageWatchdog;
 import com.android.server.PackageWatchdog.FailureReasons;
 import com.android.server.PackageWatchdog.PackageHealthObserver;
 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
 import com.android.server.SystemConfig;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
 import com.android.server.pm.ApexManager;
 
 import java.io.BufferedReader;
@@ -418,7 +418,7 @@
 
         final VersionedPackage logPackage = logPackageTemp;
         WatchdogRollbackLogger.logEvent(logPackage,
-                FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+                CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
                 reasonToLog, failedPackageToLog);
 
         Consumer<Intent> onResult = result -> {
@@ -430,19 +430,19 @@
                     int rollbackId = rollback.getRollbackId();
                     saveStagedRollbackId(rollbackId, logPackage);
                     WatchdogRollbackLogger.logEvent(logPackage,
-                            FrameworkStatsLog
+                            CrashRecoveryStatsLog
                             .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
                             reasonToLog, failedPackageToLog);
 
                 } else {
                     WatchdogRollbackLogger.logEvent(logPackage,
-                            FrameworkStatsLog
+                            CrashRecoveryStatsLog
                                     .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
                             reasonToLog, failedPackageToLog);
                 }
             } else {
                 WatchdogRollbackLogger.logEvent(logPackage,
-                        FrameworkStatsLog
+                        CrashRecoveryStatsLog
                                 .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                         reasonToLog, failedPackageToLog);
             }
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
index f9ef994..898c543 100644
--- a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -16,16 +16,16 @@
 
 package com.android.server.rollback;
 
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -42,8 +42,8 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.PackageWatchdog;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
 
 import java.util.List;
 import java.util.Set;
@@ -197,8 +197,8 @@
                 + " rollbackReason: " + rollbackReasonToString(rollbackReason)
                 + " failedPackageName: " + failingPackageName);
         if (logPackage != null) {
-            FrameworkStatsLog.write(
-                    FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+            CrashRecoveryStatsLog.write(
+                    CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
                     type,
                     logPackage.getPackageName(),
                     logPackage.getVersionCode(),
@@ -208,8 +208,8 @@
         } else {
             // In the case that the log package is null, still log an empty string as an
             // indication that retrieving the logging parent failed.
-            FrameworkStatsLog.write(
-                    FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+            CrashRecoveryStatsLog.write(
+                    CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
                     type,
                     "",
                     0,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 6cafcf7..bd9d2e6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -32,6 +32,7 @@
 import android.os.Bundle
 import android.os.ResultReceiver
 import android.util.Log
+import android.view.autofill.AutofillManager
 import com.android.credentialmanager.createflow.DisabledProviderInfo
 import com.android.credentialmanager.createflow.EnabledProviderInfo
 import com.android.credentialmanager.createflow.RequestDisplayInfo
@@ -80,9 +81,10 @@
                     CreateCredentialProviderData::class.java
                 ) ?: emptyList()
             RequestInfo.TYPE_GET ->
-                intent.extras?.getParcelableArrayList(
-                    ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
-                    GetCredentialProviderData::class.java
+                getEnabledProviderDataList(
+                    intent
+                ) ?: getEnabledProviderDataListFromAuthExtras(
+                    intent
                 ) ?: emptyList()
             else -> {
                 Log.d(
@@ -238,6 +240,24 @@
         )
     }
 
+    private fun getEnabledProviderDataList(intent: Intent): List<GetCredentialProviderData>? {
+        return intent.extras?.getParcelableArrayList(
+            ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+            GetCredentialProviderData::class.java
+        )
+    }
+
+    private fun getEnabledProviderDataListFromAuthExtras(
+        intent: Intent
+    ): List<GetCredentialProviderData>? {
+        return intent.getBundleExtra(
+            AutofillManager.EXTRA_AUTH_STATE
+        ) ?.getParcelableArrayList(
+            ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+            GetCredentialProviderData::class.java
+        )
+    }
+
     // IMPORTANT: new invocation should be mindful that this method can throw.
     private fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
         return CreateFlowUtils.toEnabledProviderList(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 1f1d236..07f1fa3 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,22 +16,24 @@
 
 package com.android.credentialmanager.autofill
 
-import android.R
+import android.app.PendingIntent
 import android.app.assist.AssistStructure
 import android.content.Context
-import android.app.PendingIntent
-import android.credentials.GetCredentialResponse
-import android.credentials.GetCredentialRequest
-import android.credentials.GetCandidateCredentialsResponse
-import android.credentials.GetCandidateCredentialsException
+import android.credentials.Credential
+import android.credentials.CredentialManager
 import android.credentials.CredentialOption
+import android.credentials.GetCandidateCredentialsException
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCredentialRequest
+import android.credentials.GetCredentialResponse
+import android.credentials.selection.Entry
 import android.credentials.selection.GetCredentialProviderData
+import android.credentials.selection.ProviderData
 import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.os.CancellationSignal
 import android.os.OutcomeReceiver
 import android.provider.Settings
-import android.credentials.Credential
 import android.service.autofill.AutofillService
 import android.service.autofill.Dataset
 import android.service.autofill.Field
@@ -44,12 +46,11 @@
 import android.service.autofill.SaveRequest
 import android.service.credentials.CredentialProviderService
 import android.util.Log
+import android.view.autofill.AutofillId
 import android.view.autofill.AutofillValue
 import android.view.autofill.IAutoFillManagerClient
-import android.view.autofill.AutofillId
-import android.widget.inline.InlinePresentationSpec
-import android.credentials.CredentialManager
 import android.widget.RemoteViews
+import android.widget.inline.InlinePresentationSpec
 import androidx.autofill.inline.v1.InlineSuggestionUi
 import androidx.credentials.provider.CustomCredentialEntry
 import androidx.credentials.provider.PasswordCredentialEntry
@@ -61,10 +62,9 @@
 import com.android.credentialmanager.ktx.credentialEntry
 import com.android.credentialmanager.model.CredentialType
 import com.android.credentialmanager.model.get.CredentialEntryInfo
-import com.android.credentialmanager.model.get.ProviderInfo
+import java.util.concurrent.Executors
 import org.json.JSONException
 import org.json.JSONObject
-import java.util.concurrent.Executors
 
 
 class CredentialAutofillService : AutofillService() {
@@ -153,8 +153,7 @@
                     return
                 }
 
-                val fillResponse = convertToFillResponse(result, request,
-                        this@CredentialAutofillService)
+                val fillResponse = convertToFillResponse(result, request)
                 if (fillResponse != null) {
                     callback.onSuccess(fillResponse)
                 } else {
@@ -231,7 +230,7 @@
     }
 
     private fun getEntryToIconMap(
-            candidateProviderDataList: MutableList<GetCredentialProviderData>
+            candidateProviderDataList: List<GetCredentialProviderData>
     ): Map<String, Icon> {
         val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
         candidateProviderDataList.forEach { provider ->
@@ -261,20 +260,16 @@
 
     private fun convertToFillResponse(
             getCredResponse: GetCandidateCredentialsResponse,
-            filLRequest: FillRequest,
-            context: Context
+            filLRequest: FillRequest
     ): FillResponse? {
-        val providerList = GetFlowUtils.toProviderList(
-                getCredResponse.candidateProviderDataList,
-                context)
-        if (providerList.isEmpty()) {
+        val candidateProviders = getCredResponse.candidateProviderDataList
+        if (candidateProviders.isEmpty()) {
             return null
         }
 
-        val entryIconMap: Map<String, Icon> =
-                getEntryToIconMap(getCredResponse.candidateProviderDataList)
-        val autofillIdToProvidersMap: Map<AutofillId, List<ProviderInfo>> =
-                mapAutofillIdToProviders(providerList)
+        val entryIconMap: Map<String, Icon> = getEntryToIconMap(candidateProviders)
+        val autofillIdToProvidersMap: Map<AutofillId, ArrayList<GetCredentialProviderData>> =
+                mapAutofillIdToProviders(candidateProviders)
         val fillResponseBuilder = FillResponse.Builder()
         var validFillResponse = false
         autofillIdToProvidersMap.forEach { (autofillId, providers) ->
@@ -292,11 +287,14 @@
     private fun processProvidersForAutofillId(
             filLRequest: FillRequest,
             autofillId: AutofillId,
-            providerList: List<ProviderInfo>,
+            providerDataList: ArrayList<GetCredentialProviderData>,
             entryIconMap: Map<String, Icon>,
             fillResponseBuilder: FillResponse.Builder,
             bottomSheetPendingIntent: PendingIntent?
     ): Boolean {
+        val providerList = GetFlowUtils.toProviderList(
+            providerDataList,
+            this@CredentialAutofillService)
         if (providerList.isEmpty()) {
             return false
         }
@@ -340,7 +338,7 @@
                 return@usernameLoop
             }
             if (i >= maxInlineItemCount && i >= lastDropdownDatasetIndex) {
-                return@usernameLoop;
+                return@usernameLoop
             }
             val icon: Icon = if (primaryEntry.icon == null) {
                 // The empty entry icon has non-null icon reference but null drawable reference.
@@ -398,19 +396,20 @@
                 inlinePresentationSpecsCount)
         if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
             addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
-                    fillResponseBuilder)
+                    fillResponseBuilder, providerDataList)
         }
         return datasetAdded
     }
 
-    private fun createInlinePresentation(primaryEntry: CredentialEntryInfo,
-                                         pendingIntent: PendingIntent,
-                                         icon: Icon,
-                                         spec: InlinePresentationSpec,
-                                         duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>):
-            InlinePresentation {
-        val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY
-                && primaryEntry.displayName != null) {
+    private fun createInlinePresentation(
+        primaryEntry: CredentialEntryInfo,
+        pendingIntent: PendingIntent,
+        icon: Icon,
+        spec: InlinePresentationSpec,
+        duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>
+    ): InlinePresentation {
+        val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY &&
+            primaryEntry.displayName != null) {
             primaryEntry.displayName!!
         } else {
             primaryEntry.userName
@@ -430,7 +429,8 @@
     private fun addDropdownMoreOptionsPresentation(
             bottomSheetPendingIntent: PendingIntent,
             autofillId: AutofillId,
-            fillResponseBuilder: FillResponse.Builder) {
+            fillResponseBuilder: FillResponse.Builder
+    ) {
         val presentationBuilder = Presentations.Builder()
                 .setMenuPresentation(RemoteViewsFactory.createMoreSignInOptionsPresentation(this))
 
@@ -460,7 +460,8 @@
             bottomSheetPendingIntent: PendingIntent,
             spec: InlinePresentationSpec,
             autofillId: AutofillId,
-            fillResponseBuilder: FillResponse.Builder
+            fillResponseBuilder: FillResponse.Builder,
+            providerDataList: ArrayList<GetCredentialProviderData>
     ) {
         val dataSetBuilder = Dataset.Builder()
         val sliceBuilder = InlineSuggestionUi
@@ -471,6 +472,10 @@
                 .setInlinePresentation(InlinePresentation(
                         sliceBuilder.build().slice, spec, /* pinned= */ true))
 
+        val extraBundle = Bundle()
+        extraBundle.putParcelableArrayList(
+                        ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
+
         fillResponseBuilder.addDataset(
                 dataSetBuilder
                         .setField(
@@ -479,6 +484,7 @@
                                         presentationBuilder.build())
                                         .build())
                         .setAuthentication(bottomSheetPendingIntent.intentSender)
+                        .setAuthenticationExtras(extraBundle)
                         .build()
         )
     }
@@ -514,16 +520,16 @@
      *     }
      */
     private fun mapAutofillIdToProviders(
-            providerList: List<ProviderInfo>
-    ): Map<AutofillId, List<ProviderInfo>> {
-        val autofillIdToProviders: MutableMap<AutofillId, MutableList<ProviderInfo>> =
-                mutableMapOf()
+        providerList: List<GetCredentialProviderData>
+    ): Map<AutofillId, ArrayList<GetCredentialProviderData>> {
+        val autofillIdToProviders: MutableMap<AutofillId, ArrayList<GetCredentialProviderData>> =
+            mutableMapOf()
         providerList.forEach { provider ->
             val autofillIdToCredentialEntries:
-                    MutableMap<AutofillId, MutableList<CredentialEntryInfo>> =
-                    mapAutofillIdToCredentialEntries(provider.credentialEntryList)
+                    MutableMap<AutofillId, ArrayList<Entry>> =
+                mapAutofillIdToCredentialEntries(provider.credentialEntries)
             autofillIdToCredentialEntries.forEach { (autofillId, entries) ->
-                autofillIdToProviders.getOrPut(autofillId) { mutableListOf() }
+                autofillIdToProviders.getOrPut(autofillId) { ArrayList() }
                         .add(copyProviderInfo(provider, entries))
             }
         }
@@ -531,13 +537,13 @@
     }
 
     private fun mapAutofillIdToCredentialEntries(
-            credentialEntryList: List<CredentialEntryInfo>
-    ): MutableMap<AutofillId, MutableList<CredentialEntryInfo>> {
+            credentialEntryList: List<Entry>
+    ): MutableMap<AutofillId, ArrayList<Entry>> {
         val autofillIdToCredentialEntries:
-                MutableMap<AutofillId, MutableList<CredentialEntryInfo>> = mutableMapOf()
+                MutableMap<AutofillId, ArrayList<Entry>> = mutableMapOf()
         credentialEntryList.forEach entryLoop@{ credentialEntry ->
             val autofillId: AutofillId? = credentialEntry
-                    .fillInIntent
+                    .frameworkExtrasIntent
                     ?.getParcelableExtra(
                             CredentialProviderService.EXTRA_AUTOFILL_ID,
                             AutofillId::class.java)
@@ -546,24 +552,22 @@
                         " Integration might be disabled.")
                 return@entryLoop
             }
-            autofillIdToCredentialEntries.getOrPut(autofillId) { mutableListOf() }
+            autofillIdToCredentialEntries.getOrPut(autofillId) { ArrayList() }
                     .add(credentialEntry)
         }
         return autofillIdToCredentialEntries
     }
 
     private fun copyProviderInfo(
-            providerInfo: ProviderInfo,
-            credentialList: List<CredentialEntryInfo>
-    ): ProviderInfo {
-        return ProviderInfo(
-                providerInfo.id,
-                providerInfo.icon,
-                providerInfo.displayName,
-                credentialList,
-                providerInfo.authenticationEntryList,
-                providerInfo.remoteEntry,
-                providerInfo.actionEntryList
+            providerInfo: GetCredentialProviderData,
+            credentialList: List<Entry>
+    ): GetCredentialProviderData {
+        return GetCredentialProviderData(
+            providerInfo.providerFlattenedComponentName,
+            credentialList,
+            providerInfo.actionChips,
+            providerInfo.authenticationEntries,
+            providerInfo.remoteEntry
         )
     }
 
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 6f9556f..ec519ca 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.6.0-rc01"
+    extra["jetpackComposeVersion"] = "1.7.0-alpha01"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 1f78a9c..f6fbc02 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,11 +15,11 @@
 #
 
 [versions]
-agp = "8.2.1"
-compose-compiler = "1.5.1"
+agp = "8.2.2"
+compose-compiler = "1.5.8"
 dexmaker-mockito = "2.28.3"
 jvm = "17"
-kotlin = "1.9.0"
+kotlin = "1.9.22"
 truth = "1.1.5"
 
 [libraries]
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 618dc37..08a8797 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@
     api("androidx.slice:slice-builders:1.1.0-alpha02")
     api("androidx.slice:slice-core:1.1.0-alpha02")
     api("androidx.slice:slice-view:1.1.0-alpha02")
-    api("androidx.compose.material3:material3:1.2.0-beta02")
+    api("androidx.compose.material3:material3:1.2.0-rc01")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.7.6")
+    api("androidx.navigation:navigation-compose:2.8.0-alpha01")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.7.0-alpha03")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 560bc46..bcdb64d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1745,12 +1745,14 @@
         final BluetoothDevice tmpDevice = mDevice;
         final short tmpRssi = mRssi;
         final boolean tmpJustDiscovered = mJustDiscovered;
+        final HearingAidInfo tmpHearingAidInfo = mHearingAidInfo;
 
         // Set main device from sub device
         release();
         mDevice = newMainDevice.mDevice;
         mRssi = newMainDevice.mRssi;
         mJustDiscovered = newMainDevice.mJustDiscovered;
+        mHearingAidInfo = newMainDevice.mHearingAidInfo;
         fillData();
 
         // Set sub device from backup
@@ -1758,6 +1760,7 @@
         newMainDevice.mDevice = tmpDevice;
         newMainDevice.mRssi = tmpRssi;
         newMainDevice.mJustDiscovered = tmpJustDiscovered;
+        newMainDevice.mHearingAidInfo = tmpHearingAidInfo;
         newMainDevice.fillData();
 
         // Add the sub device back into mMemberDevices with correct hash
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 9db8b47..461ecf5d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1769,8 +1769,10 @@
     public void switchMemberDeviceContent_switchMainDevice_switchesSuccessful() {
         mCachedDevice.mRssi = RSSI_1;
         mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
+        mCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo());
         mSubCachedDevice.mRssi = RSSI_2;
         mSubCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
+        mSubCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo());
         mCachedDevice.addMemberDevice(mSubCachedDevice);
 
         mCachedDevice.switchMemberDeviceContent(mSubCachedDevice);
@@ -1778,10 +1780,12 @@
         assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
         assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
         assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+        assertThat(mCachedDevice.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_RIGHT);
         verify(mCachedDevice).fillData();
         assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
         assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
         assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
+        assertThat(mSubCachedDevice.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_LEFT);
         verify(mSubCachedDevice).fillData();
         assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
     }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a5b9e9e..ba3026e 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -67,6 +67,16 @@
 }
 
 flag {
+    name: "nssl_falsing_fix"
+    namespace: "systemui"
+    description: "Minor touch changes to prevent falsing errors in NSSL"
+    bug: "316551193"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "refactor_get_current_user"
     namespace: "systemui"
     description: "KeyguardUpdateMonitor.getCurrentUser() was providing outdated results."
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index efdbfdb..055252b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -353,7 +353,7 @@
 
         /**
          * Return the first [GradientDrawable] found in [drawable], or null if none is found. If
-         * [drawable] is a [LayerDrawable], this will return the first layer that is a
+         * [drawable] is a [LayerDrawable], this will return the first layer that has a
          * [GradientDrawable].
          */
         fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
@@ -367,8 +367,8 @@
 
             if (drawable is LayerDrawable) {
                 for (i in 0 until drawable.numberOfLayers) {
-                    val maybeGradient = drawable.getDrawable(i)
-                    if (maybeGradient is GradientDrawable) {
+                    val maybeGradient = findGradientDrawable(drawable.getDrawable(i))
+                    if (maybeGradient != null) {
                         return maybeGradient
                     }
                 }
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
index 3c32594..13ee196 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.compose
 
-import android.app.Dialog
 import android.content.Context
 import android.view.View
 import android.view.WindowInsets
@@ -33,7 +32,6 @@
 import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.StateFlow
@@ -90,10 +88,10 @@
         throwComposeUnavailableError()
     }
 
-    override fun createStickyKeysDialog(
-        dialogFactory: SystemUIDialogFactory,
+    override fun createStickyKeysIndicatorContent(
+        context: Context,
         viewModel: StickyKeysIndicatorViewModel
-    ): Dialog {
+    ): View {
         throwComposeUnavailableError()
     }
 
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index afb860e..f05c7f3 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.compose
 
-import android.app.Dialog
 import android.content.Context
 import android.graphics.Point
 import android.view.View
@@ -39,7 +38,7 @@
 import com.android.systemui.communal.ui.compose.CommunalHub
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.keyboard.stickykeys.ui.view.StickyKeysIndicator
+import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
 import com.android.systemui.people.ui.compose.PeopleScreen
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
@@ -50,8 +49,6 @@
 import com.android.systemui.scene.ui.composable.ComposableScene
 import com.android.systemui.scene.ui.composable.SceneContainer
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
-import com.android.systemui.statusbar.phone.create
 import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
 import kotlinx.coroutines.CoroutineScope
@@ -140,11 +137,11 @@
         }
     }
 
-    override fun createStickyKeysDialog(
-        dialogFactory: SystemUIDialogFactory,
+    override fun createStickyKeysIndicatorContent(
+        context: Context,
         viewModel: StickyKeysIndicatorViewModel
-    ): Dialog {
-        return dialogFactory.create { StickyKeysIndicator(viewModel) }
+    ): View {
+        return createStickyKeyIndicatorView(context, viewModel)
     }
 
     override fun createCommunalView(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
index 071433e..dd86646 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
@@ -16,23 +16,42 @@
 
 package com.android.systemui.keyboard.stickykeys.ui.view
 
+import android.content.Context
+import android.view.View
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
 
+fun createStickyKeyIndicatorView(context: Context, viewModel: StickyKeysIndicatorViewModel): View {
+    return ComposeView(context).apply {
+        setContent {
+            PlatformTheme {
+                val defaultContentColor = MaterialTheme.colorScheme.onSurfaceVariant
+                CompositionLocalProvider(LocalContentColor provides defaultContentColor) {
+                    StickyKeysIndicator(viewModel)
+                }
+            }
+        }
+    }
+}
+
 @Composable
 fun StickyKeysIndicator(viewModel: StickyKeysIndicatorViewModel) {
     val stickyKeys by viewModel.indicatorContent.collectAsState(emptyMap())
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index a910bca..e40f6b6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -26,6 +26,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.isUnspecified
 import androidx.compose.ui.geometry.lerp
+import androidx.compose.ui.graphics.CompositingStrategy
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.layout.IntermediateMeasureScope
@@ -473,7 +474,8 @@
             placeable.place(offset)
         } else {
             placeable.placeWithLayer(offset) {
-                this.alpha = elementAlpha(layoutImpl, element, scene)
+                alpha = elementAlpha(layoutImpl, element, scene)
+                compositingStrategy = CompositingStrategy.ModulateAlpha
             }
         }
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 1642e52..45f98be 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -72,7 +72,7 @@
         testScope.runTest {
             val mediaModel = collectLastValue(underTest.mediaModel)
             runCurrent()
-            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
+            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isFalse()
         }
 
     @Test
@@ -84,16 +84,16 @@
             // Initial value is false.
             val mediaModel = collectLastValue(underTest.mediaModel)
             runCurrent()
-            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
+            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isFalse()
 
             // Change to media available and notify the listener.
-            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true)
             whenever(mediaData.createdTimestampMillis).thenReturn(1234L)
             mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
             runCurrent()
 
             // Media active now returns true.
-            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()
+            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isTrue()
             assertThat(mediaModel()?.createdTimestampMillis).isEqualTo(1234L)
         }
 
@@ -104,20 +104,20 @@
             verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
 
             // Change to media available and notify the listener.
-            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true)
             mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
             runCurrent()
 
             // Media active now returns true.
             val mediaModel = collectLastValue(underTest.mediaModel)
-            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()
+            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isTrue()
 
             // Change to media unavailable and notify the listener.
-            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(false)
+            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false)
             mediaDataListenerCaptor.value.onMediaDataRemoved("key")
             runCurrent()
 
             // Media active now returns false.
-            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
+            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isFalse()
         }
 }
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
new file mode 100644
index 0000000..a877853
--- /dev/null
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+xmlns:app="http://schemas.android.com/apk/res-auto"
+xmlns:tools="http://schemas.android.com/tools"
+android:layout_width="match_parent"
+android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/logo"
+        android:layout_width="@dimen/biometric_auth_icon_size"
+        android:layout_height="@dimen/biometric_auth_icon_size"
+        android:layout_gravity="center"
+        android:scaleType="fitXY"
+        android:visibility="gone" />
+
+    <ImageView
+        android:id="@+id/background"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:contentDescription="@string/biometric_dialog_empty_space_description"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <View
+        android:id="@+id/panel"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:background="?android:attr/colorBackgroundFloating"
+        android:clickable="true"
+        android:clipToOutline="true"
+        android:importantForAccessibility="no"
+        android:paddingHorizontal="16dp"
+        android:paddingVertical="16dp"
+        android:visibility="visible"
+        app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
+        app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
+        app:layout_constraintStart_toStartOf="@+id/leftGuideline"
+        app:layout_constraintTop_toTopOf="@+id/title" />
+
+    <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+        android:id="@+id/biometric_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.8"
+        tools:srcCompat="@tools:sample/avatars" />
+
+    <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+        android:id="@+id/biometric_icon_overlay"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_gravity="center"
+        android:contentDescription="@null"
+        android:scaleType="fitXY"
+        app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
+        app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+        app:layout_constraintTop_toTopOf="@+id/biometric_icon"
+        app:layout_constraintVertical_bias="0.0" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="@integer/biometric_dialog_text_gravity"
+        android:singleLine="true"
+        android:marqueeRepeatLimit="1"
+        android:ellipsize="marquee"
+        style="@style/TextAppearance.AuthCredential.Title"
+        app:layout_constraintBottom_toTopOf="@+id/subtitle"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel" />
+
+    <TextView
+        android:id="@+id/subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="@integer/biometric_dialog_text_gravity"
+        android:singleLine="true"
+        android:marqueeRepeatLimit="1"
+        android:ellipsize="marquee"
+        style="@style/TextAppearance.AuthCredential.Subtitle"
+        app:layout_constraintBottom_toTopOf="@+id/description"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel" />
+
+    <Space
+        android:id="@+id/space_above_content"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/biometric_prompt_space_above_content"
+        android:visibility="gone"
+        app:layout_constraintTop_toBottomOf="@+id/subtitle"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel"/>
+
+    <ScrollView
+        android:id="@+id/customized_view_container"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:fillViewport="true"
+        android:fadeScrollbars="false"
+        android:gravity="center_vertical"
+        android:orientation="vertical"
+        android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
+        android:scrollbars="vertical"
+        android:visibility="gone"
+        app:layout_constraintTop_toBottomOf="@+id/space_above_content"
+        app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel"/>
+
+    <TextView
+        android:id="@+id/description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="24dp"
+        android:scrollbars="vertical"
+        android:gravity="@integer/biometric_dialog_text_gravity"
+        style="@style/TextAppearance.AuthCredential.Description"
+        app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel" />
+
+    <TextView
+        android:id="@+id/indicator"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:gravity="center_horizontal"
+        android:textColor="@color/biometric_dialog_gray"
+        android:textSize="12sp"
+        android:accessibilityLiveRegion="polite"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:scrollHorizontally="true"
+        android:fadingEdge="horizontal"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="@+id/panel"
+        app:layout_constraintTop_toBottomOf="@+id/biometric_icon" />
+
+    <!-- Negative Button, reserved for app -->
+    <Button
+        android:id="@+id/button_negative"
+        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginBottom="8dp"
+        android:layout_marginLeft="8dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel" />
+
+    <!-- Cancel Button, replaces negative button when biometric is accepted -->
+    <Button
+        android:id="@+id/button_cancel"
+        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginBottom="8dp"
+        android:layout_marginLeft="8dp"
+        android:text="@string/cancel"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel" />
+
+    <!-- "Use Credential" Button, replaces if device credential is allowed -->
+    <Button
+        android:id="@+id/button_use_credential"
+        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginBottom="8dp"
+        android:layout_marginLeft="8dp"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="@+id/panel"
+        app:layout_constraintStart_toStartOf="@+id/panel" />
+
+    <!-- Positive Button -->
+    <Button
+        android:id="@+id/button_confirm"
+        style="@*android:style/Widget.DeviceDefault.Button.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginBottom="8dp"
+        android:layout_marginRight="8dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:text="@string/biometric_dialog_confirm"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="@+id/panel"
+        app:layout_constraintEnd_toEndOf="@+id/panel"
+        tools:visibility="invisible" />
+
+    <!-- Try Again Button -->
+    <Button
+        android:id="@+id/button_try_again"
+        style="@*android:style/Widget.DeviceDefault.Button.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginBottom="8dp"
+        android:layout_marginRight="8dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:text="@string/biometric_dialog_try_again"
+        android:visibility="invisible"
+        app:layout_constraintBottom_toBottomOf="@+id/panel"
+        app:layout_constraintEnd_toEndOf="@+id/panel" />
+
+    <!-- Guidelines for setting panel border -->
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/leftGuideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/rightGuideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/bottomGuideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/values-xxhdpi/dimens.xml b/packages/SystemUI/res/values-xxhdpi/dimens.xml
index 26c8437..9bff422 100644
--- a/packages/SystemUI/res/values-xxhdpi/dimens.xml
+++ b/packages/SystemUI/res/values-xxhdpi/dimens.xml
@@ -22,4 +22,8 @@
          fraction of a pixel.-->
     <fraction name="battery_subpixel_smoothing_left">33%</fraction>
     <fraction name="battery_subpixel_smoothing_right">33%</fraction>
+
+    <!-- Biometrics fingerprint icon size for full resolution.-->
+    <dimen name="biometric_dialog_fingerprint_icon_width">120dp</dimen>
+    <dimen name="biometric_dialog_fingerprint_icon_height">120dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index dfeb1f3..d821f19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -33,7 +33,7 @@
 public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
     protected View mEcaView;
 
-    // To avoid accidental lockout due to events while the device in in the pocket, ignore
+    // To avoid accidental lockout due to events while the device in the pocket, ignore
     // any passwords with length less than or equal to this length.
     protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
     private KeyDownListener mKeyDownListener;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index bc12aee..ce03072 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -132,7 +132,7 @@
     boolean shouldSubtleWindowAnimationsForUnlock();
 
     /**
-     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+     * Starts the animation before we dismiss Keyguard, i.e. a disappearing animation on the
      * security view of the bouncer.
      *
      * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index 5f5cca8..e8499d3 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -17,15 +17,11 @@
 package com.android.systemui
 
 import android.content.Context
-import android.content.res.Resources
 import android.graphics.Path
 import android.graphics.Rect
-import android.graphics.RectF
 import android.hardware.camera2.CameraManager
-import android.util.PathParser
 import com.android.systemui.res.R
 import java.util.concurrent.Executor
-import kotlin.math.roundToInt
 
 /**
  * Listens for usage of the Camera and controls the ScreenDecorations transition to show extra
@@ -163,89 +159,20 @@
     }
 
     companion object Factory {
-        fun build(context: Context, executor: Executor): CameraAvailabilityListener {
+        fun build(
+            context: Context,
+            executor: Executor,
+            cameraProtectionLoader: CameraProtectionLoader
+        ): CameraAvailabilityListener {
             val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
             val res = context.resources
-            val cameraProtectionInfoList = loadCameraProtectionInfoList(res)
+            val cameraProtectionInfoList = cameraProtectionLoader.loadCameraProtectionInfoList()
             val excluded = res.getString(R.string.config_cameraProtectionExcludedPackages)
 
             return CameraAvailabilityListener(manager, cameraProtectionInfoList, excluded, executor)
         }
-
-        private fun pathFromString(pathString: String): Path {
-            val spec = pathString.trim()
-            val p: Path
-            try {
-                p = PathParser.createPathFromPathData(spec)
-            } catch (e: Throwable) {
-                throw IllegalArgumentException("Invalid protection path", e)
-            }
-
-            return p
-        }
-
-        private fun loadCameraProtectionInfoList(res: Resources): List<CameraProtectionInfo> {
-            val list = mutableListOf<CameraProtectionInfo>()
-            val front =
-                loadCameraProtectionInfo(
-                    res,
-                    R.string.config_protectedCameraId,
-                    R.string.config_protectedPhysicalCameraId,
-                    R.string.config_frontBuiltInDisplayCutoutProtection
-                )
-            if (front != null) {
-                list.add(front)
-            }
-            val inner =
-                loadCameraProtectionInfo(
-                    res,
-                    R.string.config_protectedInnerCameraId,
-                    R.string.config_protectedInnerPhysicalCameraId,
-                    R.string.config_innerBuiltInDisplayCutoutProtection
-                )
-            if (inner != null) {
-                list.add(inner)
-            }
-            return list
-        }
-
-        private fun loadCameraProtectionInfo(
-            res: Resources,
-            cameraIdRes: Int,
-            physicalCameraIdRes: Int,
-            pathRes: Int
-        ): CameraProtectionInfo? {
-            val logicalCameraId = res.getString(cameraIdRes)
-            if (logicalCameraId.isNullOrEmpty()) {
-                return null
-            }
-            val physicalCameraId = res.getString(physicalCameraIdRes)
-            val protectionPath = pathFromString(res.getString(pathRes))
-            val computed = RectF()
-            protectionPath.computeBounds(computed)
-            val protectionBounds =
-                Rect(
-                    computed.left.roundToInt(),
-                    computed.top.roundToInt(),
-                    computed.right.roundToInt(),
-                    computed.bottom.roundToInt()
-                )
-            return CameraProtectionInfo(
-                logicalCameraId,
-                physicalCameraId,
-                protectionPath,
-                protectionBounds
-            )
-        }
     }
 
-    data class CameraProtectionInfo(
-        val logicalCameraId: String,
-        val physicalCameraId: String?,
-        val cutoutProtectionPath: Path,
-        val cutoutBounds: Rect,
-    )
-
     private data class OpenCameraInfo(
         val logicalCameraId: String,
         val packageId: String,
diff --git a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
new file mode 100644
index 0000000..bbab4de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 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
+
+import android.graphics.Path
+import android.graphics.Rect
+
+data class CameraProtectionInfo(
+    val logicalCameraId: String,
+    val physicalCameraId: String?,
+    val cutoutProtectionPath: Path,
+    val cutoutBounds: Rect,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/CameraProtectionLoader.kt b/packages/SystemUI/src/com/android/systemui/CameraProtectionLoader.kt
new file mode 100644
index 0000000..8fe9389
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/CameraProtectionLoader.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 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
+
+import android.content.Context
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.util.PathParser
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.math.roundToInt
+
+class CameraProtectionLoader @Inject constructor(private val context: Context) {
+
+    fun loadCameraProtectionInfoList(): List<CameraProtectionInfo> {
+        val list = mutableListOf<CameraProtectionInfo>()
+        val front =
+            loadCameraProtectionInfo(
+                R.string.config_protectedCameraId,
+                R.string.config_protectedPhysicalCameraId,
+                R.string.config_frontBuiltInDisplayCutoutProtection
+            )
+        if (front != null) {
+            list.add(front)
+        }
+        val inner =
+            loadCameraProtectionInfo(
+                R.string.config_protectedInnerCameraId,
+                R.string.config_protectedInnerPhysicalCameraId,
+                R.string.config_innerBuiltInDisplayCutoutProtection
+            )
+        if (inner != null) {
+            list.add(inner)
+        }
+        return list
+    }
+
+    private fun loadCameraProtectionInfo(
+        cameraIdRes: Int,
+        physicalCameraIdRes: Int,
+        pathRes: Int
+    ): CameraProtectionInfo? {
+        val logicalCameraId = context.getString(cameraIdRes)
+        if (logicalCameraId.isNullOrEmpty()) {
+            return null
+        }
+        val physicalCameraId = context.getString(physicalCameraIdRes)
+        val protectionPath = pathFromString(context.getString(pathRes))
+        val computed = RectF()
+        protectionPath.computeBounds(computed)
+        val protectionBounds =
+            Rect(
+                computed.left.roundToInt(),
+                computed.top.roundToInt(),
+                computed.right.roundToInt(),
+                computed.bottom.roundToInt()
+            )
+        return CameraProtectionInfo(
+            logicalCameraId,
+            physicalCameraId,
+            protectionPath,
+            protectionBounds
+        )
+    }
+
+    private fun pathFromString(pathString: String): Path {
+        return try {
+            PathParser.createPathFromPathData(pathString.trim())
+        } catch (e: Throwable) {
+            throw IllegalArgumentException("Invalid protection path", e)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index d6d5c26..3e03fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -146,6 +146,7 @@
     private final ThreadFactory mThreadFactory;
     private final DecorProviderFactory mDotFactory;
     private final FaceScanningProviderFactory mFaceScanningFactory;
+    private final CameraProtectionLoader mCameraProtectionLoader;
     public final int mFaceScanningViewId;
 
     @VisibleForTesting
@@ -333,7 +334,8 @@
             FaceScanningProviderFactory faceScanningFactory,
             ScreenDecorationsLogger logger,
             FacePropertyRepository facePropertyRepository,
-            JavaAdapter javaAdapter) {
+            JavaAdapter javaAdapter,
+            CameraProtectionLoader cameraProtectionLoader) {
         mContext = context;
         mSecureSettings = secureSettings;
         mCommandRegistry = commandRegistry;
@@ -343,6 +345,7 @@
         mThreadFactory = threadFactory;
         mDotFactory = dotFactory;
         mFaceScanningFactory = faceScanningFactory;
+        mCameraProtectionLoader = cameraProtectionLoader;
         mFaceScanningViewId = com.android.systemui.res.R.id.face_scanning_anim;
         mLogger = logger;
         mFacePropertyRepository = facePropertyRepository;
@@ -981,7 +984,9 @@
         Resources res = mContext.getResources();
         boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
         if (enabled) {
-            mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mExecutor);
+            mCameraListener =
+                    CameraAvailabilityListener.Factory.build(
+                            mContext, mExecutor, mCameraProtectionLoader);
             mCameraListener.addTransitionCallback(mCameraTransitionCallback);
             mCameraListener.startListening();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 57e308f..3397906 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
+import static com.android.systemui.Flags.constraintBp;
 
 import android.animation.Animator;
 import android.annotation.IntDef;
@@ -57,6 +58,7 @@
 import android.window.OnBackInvokedCallback;
 import android.window.OnBackInvokedDispatcher;
 
+import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.core.view.AccessibilityDelegateCompat;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -153,7 +155,7 @@
     @Nullable private Spaghetti mBiometricView;
     @Nullable private View mCredentialView;
     private final AuthPanelController mPanelController;
-    private final FrameLayout mFrameLayout;
+    private final ViewGroup mLayout;
     private final ImageView mBackgroundView;
     private final ScrollView mBiometricScrollView;
     private final View mPanelView;
@@ -339,11 +341,16 @@
         mBiometricCallback = new BiometricCallback();
 
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        mFrameLayout = (FrameLayout) layoutInflater.inflate(
-                R.layout.auth_container_view, this, false /* attachToRoot */);
-        addView(mFrameLayout);
-        mBiometricScrollView = mFrameLayout.findViewById(R.id.biometric_scrollview);
-        mBackgroundView = mFrameLayout.findViewById(R.id.background);
+        if (constraintBp()) {
+            mLayout = (ConstraintLayout) layoutInflater.inflate(
+                    R.layout.biometric_prompt_constraint_layout, this, false /* attachToRoot */);
+        } else {
+            mLayout = (FrameLayout) layoutInflater.inflate(
+                    R.layout.auth_container_view, this, false /* attachToRoot */);
+        }
+        mBiometricScrollView = mLayout.findViewById(R.id.biometric_scrollview);
+        addView(mLayout);
+        mBackgroundView = mLayout.findViewById(R.id.background);
         ViewCompat.setAccessibilityDelegate(mBackgroundView, new AccessibilityDelegateCompat() {
             @Override
             public void onInitializeAccessibilityNodeInfo(View host,
@@ -358,7 +365,7 @@
             }
         });
 
-        mPanelView = mFrameLayout.findViewById(R.id.panel);
+        mPanelView = mLayout.findViewById(R.id.panel);
         mPanelController = new AuthPanelController(mContext, mPanelView);
         mBackgroundExecutor = bgExecutor;
         mInteractionJankMonitor = jankMonitor;
@@ -402,20 +409,31 @@
                     new BiometricModalities(fpProps, faceProps),
                     config.mOpPackageName);
 
-            final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
-                    R.layout.biometric_prompt_layout, null, false);
-            mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
-                    // TODO(b/201510778): This uses the wrong timeout in some cases
-                    getJankListener(view, TRANSIT,
-                            BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
-                    mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
-                    vibratorHelper);
+            if (constraintBp()) {
+                mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null,
+                        // TODO(b/201510778): This uses the wrong timeout in some cases
+                        getJankListener(mLayout, TRANSIT,
+                                BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
+                        mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
+                        vibratorHelper);
+            } else {
+                final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
+                        R.layout.biometric_prompt_layout, null, false);
+                mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
+                        // TODO(b/201510778): This uses the wrong timeout in some cases
+                        getJankListener(view, TRANSIT,
+                                BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
+                        mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
+                        vibratorHelper);
 
-            // TODO(b/251476085): migrate these dependencies
-            if (fpProps != null && fpProps.isAnyUdfpsType()) {
-                view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps),
-                        config.mScaleProvider);
+                // TODO(b/251476085): migrate these dependencies
+                if (fpProps != null && fpProps.isAnyUdfpsType()) {
+                    view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps),
+                            config.mScaleProvider);
+                }
             }
+        } else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
+            addCredentialView(true, false);
         } else {
             mPromptSelectorInteractorProvider.get().resetPrompt();
         }
@@ -477,7 +495,7 @@
         vm.setAnimateContents(animateContents);
         ((CredentialView) mCredentialView).init(vm, this, mPanelController, animatePanel);
 
-        mFrameLayout.addView(mCredentialView);
+        mLayout.addView(mCredentialView);
     }
 
     @Override
@@ -488,7 +506,9 @@
 
     @Override
     public void onOrientationChanged() {
-        maybeUpdatePositionForUdfps(true /* invalidate */);
+        if (!constraintBp()) {
+            maybeUpdatePositionForUdfps(true /* invalidate */);
+        }
     }
 
     @Override
@@ -502,8 +522,9 @@
         mWakefulnessLifecycle.addObserver(this);
         mPanelInteractionDetector.enable(
                 () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
-
-        if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
+        if (constraintBp()) {
+            // Do nothing on attachment with constraintLayout
+        } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
             mBiometricScrollView.addView(mBiometricView.asView());
         } else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
             addCredentialView(true /* animatePanel */, false /* animateContents */);
@@ -512,7 +533,9 @@
                     + mConfig.mPromptInfo.getAuthenticators());
         }
 
-        maybeUpdatePositionForUdfps(false /* invalidate */);
+        if (!constraintBp()) {
+            maybeUpdatePositionForUdfps(false /* invalidate */);
+        }
 
         if (mConfig.mSkipIntro) {
             mContainerState = STATE_SHOWING;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 285ab4a..efad21b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -41,6 +41,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.airbnb.lottie.LottieAnimationView
 import com.airbnb.lottie.LottieCompositionFactory
+import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
@@ -70,9 +71,9 @@
     @SuppressLint("ClickableViewAccessibility")
     @JvmStatic
     fun bind(
-        view: BiometricPromptLayout,
+        view: View,
         viewModel: PromptViewModel,
-        panelViewController: AuthPanelController,
+        panelViewController: AuthPanelController?,
         jankListener: BiometricJankListener,
         backgroundView: View,
         legacyCallback: Spaghetti.Callback,
@@ -112,11 +113,18 @@
         val iconOverlayView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
         val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
 
+        val iconSizeOverride =
+            if (constraintBp()) {
+                viewModel.fingerprintAffordanceSize
+            } else {
+                (view as BiometricPromptLayout).updatedFingerprintAffordanceSize
+            }
+
         PromptIconViewBinder.bind(
             iconView,
             iconOverlayView,
-            view.getUpdatedFingerprintAffordanceSize(),
-            viewModel
+            iconSizeOverride,
+            viewModel,
         )
 
         val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index d5695f3..2417fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -19,29 +19,45 @@
 import android.animation.Animator
 import android.animation.AnimatorSet
 import android.animation.ValueAnimator
+import android.graphics.Outline
+import android.graphics.Rect
+import android.transition.AutoTransition
+import android.transition.TransitionManager
 import android.view.Surface
 import android.view.View
 import android.view.ViewGroup
+import android.view.ViewOutlineProvider
 import android.view.WindowInsets
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.Guideline
 import androidx.core.animation.addListener
+import androidx.core.view.doOnAttach
 import androidx.core.view.doOnLayout
 import androidx.core.view.isGone
 import androidx.lifecycle.lifecycleScope
+import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.Utils
-import com.android.systemui.biometrics.ui.BiometricPromptLayout
+import com.android.systemui.biometrics.ui.viewmodel.PromptPosition
 import com.android.systemui.biometrics.ui.viewmodel.PromptSize
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
+import com.android.systemui.biometrics.ui.viewmodel.isBottom
 import com.android.systemui.biometrics.ui.viewmodel.isLarge
+import com.android.systemui.biometrics.ui.viewmodel.isLeft
 import com.android.systemui.biometrics.ui.viewmodel.isMedium
 import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
+import com.android.systemui.biometrics.ui.viewmodel.isRight
 import com.android.systemui.biometrics.ui.viewmodel.isSmall
+import com.android.systemui.biometrics.ui.viewmodel.isTop
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
+import kotlin.math.abs
+import kotlin.math.min
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
@@ -54,18 +70,19 @@
 
     /** Resizes [BiometricPromptLayout] and the [panelViewController] via the [PromptViewModel]. */
     fun bind(
-        view: BiometricPromptLayout,
+        view: View,
         viewModel: PromptViewModel,
         viewsToHideWhenSmall: List<View>,
         viewsToFadeInOnSizeChange: List<View>,
-        panelViewController: AuthPanelController,
+        panelViewController: AuthPanelController?,
         jankListener: BiometricJankListener,
     ) {
         val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java))
         val accessibilityManager =
             requireNotNull(view.context.getSystemService(AccessibilityManager::class.java))
+
         fun notifyAccessibilityChanged() {
-            Utils.notifyAccessibilityContentChanged(accessibilityManager, view)
+            Utils.notifyAccessibilityContentChanged(accessibilityManager, view as ViewGroup)
         }
 
         fun startMonitoredAnimation(animators: List<Animator>) {
@@ -77,149 +94,342 @@
             }
         }
 
-        val iconHolderView = view.requireViewById<View>(R.id.biometric_icon_frame)
-        val iconPadding = view.resources.getDimension(R.dimen.biometric_dialog_icon_padding)
-        val fullSizeYOffset =
-            view.resources.getDimension(R.dimen.biometric_dialog_medium_to_large_translation_offset)
+        if (constraintBp()) {
+            val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline)
+            val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline)
+            val bottomGuideline = view.requireViewById<Guideline>(R.id.bottomGuideline)
 
-        // cache the original position of the icon view (as done in legacy view)
-        // this must happen before any size changes can be made
-        view.doOnLayout {
-            // TODO(b/251476085): this old way of positioning has proven itself unreliable
-            // remove this and associated thing like (UdfpsDialogMeasureAdapter) and
-            // pin to the physical sensor
-            val iconHolderOriginalY = iconHolderView.y
+            val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
+            val panelView = view.requireViewById<View>(R.id.panel)
+            val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
 
-            // bind to prompt
-            // TODO(b/251476085): migrate the legacy panel controller and simplify this
-            view.repeatWhenAttached {
-                var currentSize: PromptSize? = null
-                lifecycleScope.launch {
-                    /**
-                     * View is only set visible in BiometricViewSizeBinder once PromptSize is
-                     * determined that accounts for iconView size, to prevent prompt resizing being
-                     * visible to the user.
-                     *
-                     * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint
-                     *   layout is implemented
-                     */
-                    combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect {
-                        (isIconViewLoaded, size) ->
-                        if (!isIconViewLoaded) {
-                            return@collect
+            // ConstraintSets for animating between prompt sizes
+            val mediumConstraintSet = ConstraintSet()
+            mediumConstraintSet.clone(view as ConstraintLayout)
+
+            val smallConstraintSet = ConstraintSet()
+            smallConstraintSet.clone(mediumConstraintSet)
+            viewsToHideWhenSmall.forEach { smallConstraintSet.setVisibility(it.id, View.GONE) }
+
+            val largeConstraintSet = ConstraintSet()
+            largeConstraintSet.clone(mediumConstraintSet)
+            viewsToHideWhenSmall.forEach { largeConstraintSet.setVisibility(it.id, View.GONE) }
+            largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+            largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+            largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+            largeConstraintSet.setGuidelineBegin(leftGuideline.id, 0)
+            largeConstraintSet.setGuidelineEnd(rightGuideline.id, 0)
+            largeConstraintSet.setGuidelineEnd(bottomGuideline.id, 0)
+
+            // Round the panel outline
+            panelView.outlineProvider =
+                object : ViewOutlineProvider() {
+                    override fun getOutline(view: View, outline: Outline) {
+                        outline.setRoundRect(0, 0, view.width, view.height, cornerRadius)
+                    }
+                }
+
+            view.doOnLayout {
+                val windowBounds = windowManager.maximumWindowMetrics.bounds
+                val bottomInset =
+                    windowManager.maximumWindowMetrics.windowInsets
+                        .getInsets(WindowInsets.Type.navigationBars())
+                        .bottom
+
+                fun measureBounds(position: PromptPosition) {
+                    val width = min(windowBounds.height(), windowBounds.width())
+
+                    var left = -1
+                    var top = -1
+                    var right = -1
+                    var bottom = -1
+
+                    when {
+                        position.isTop -> {
+                            left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+                            top = viewModel.promptMargin
+                            right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+                            bottom = iconHolderView.centerY() * 2 - iconHolderView.centerY() / 4
+                        }
+                        position.isBottom -> {
+                            if (view.isLandscape()) {
+                                left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+                                top = iconHolderView.centerY()
+                                right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+                                bottom = bottomInset + viewModel.promptMargin
+                            } else {
+                                left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+                                top =
+                                    windowBounds.height() -
+                                        (windowBounds.height() - iconHolderView.centerY()) * 2 +
+                                        viewModel.promptMargin
+                                right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
+                                bottom = viewModel.promptMargin
+                            }
                         }
 
-                        // prepare for animated size transitions
-                        for (v in viewsToHideWhenSmall) {
-                            v.showContentOrHide(forceHide = size.isSmall)
+                        // For Udfps exclusive left and right, measure guideline to center
+                        // icon in BP
+                        position.isLeft -> {
+                            left = viewModel.promptMargin
+                            top =
+                                windowBounds.height() -
+                                    (windowBounds.height() - iconHolderView.centerY()) * 2 +
+                                    viewModel.promptMargin
+                            right =
+                                abs(
+                                    windowBounds.width() - iconHolderView.centerX() * 2 +
+                                        viewModel.promptMargin
+                                )
+                            bottom = bottomInset + viewModel.promptMargin
                         }
-                        if (currentSize == null && size.isSmall) {
-                            iconHolderView.alpha = 0f
+                        position.isRight -> {
+                            left =
+                                abs(
+                                    iconHolderView.centerX() -
+                                        (windowBounds.width() - iconHolderView.centerX()) -
+                                        viewModel.promptMargin
+                                )
+                            top =
+                                windowBounds.height() -
+                                    (windowBounds.height() - iconHolderView.centerY()) * 2 +
+                                    viewModel.promptMargin
+                            right = viewModel.promptMargin
+                            bottom = bottomInset + viewModel.promptMargin
                         }
-                        if ((currentSize.isSmall && size.isMedium) || size.isSmall) {
-                            viewsToFadeInOnSizeChange.forEach { it.alpha = 0f }
-                        }
+                    }
 
-                        // TODO(b/302735104): Fix wrong height due to the delay of
-                        // PromptContentView. addOnLayoutChangeListener() will cause crash when
-                        // showing credential view, since |PromptIconViewModel| won't release the
-                        // flow.
-                        // propagate size changes to legacy panel controller and animate transitions
-                        view.doOnLayout {
-                            val width = view.measuredWidth
-                            val height = view.measuredHeight
+                    val bounds = Rect(left, top, right, bottom)
+                    if (bounds.shouldAdjustLeftGuideline()) {
+                        leftGuideline.setGuidelineBegin(bounds.left)
+                        smallConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
+                        mediumConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
+                    }
+                    if (bounds.shouldAdjustRightGuideline()) {
+                        rightGuideline.setGuidelineEnd(bounds.right)
+                        smallConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
+                        mediumConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
+                    }
+                    if (bounds.shouldAdjustBottomGuideline()) {
+                        bottomGuideline.setGuidelineEnd(bounds.bottom)
+                        smallConstraintSet.setGuidelineEnd(bottomGuideline.id, bounds.bottom)
+                        mediumConstraintSet.setGuidelineEnd(bottomGuideline.id, bounds.bottom)
+                    }
+                }
 
-                            when {
-                                size.isSmall -> {
-                                    iconHolderView.alpha = 1f
-                                    val bottomInset =
-                                        windowManager.maximumWindowMetrics.windowInsets
-                                            .getInsets(WindowInsets.Type.navigationBars())
-                                            .bottom
-                                    iconHolderView.y =
-                                        if (view.isLandscape()) {
-                                            (view.height - iconHolderView.height - bottomInset) / 2f
-                                        } else {
-                                            view.height -
-                                                iconHolderView.height -
-                                                iconPadding -
-                                                bottomInset
-                                        }
-                                    val newHeight =
-                                        iconHolderView.height + (2 * iconPadding.toInt()) -
-                                            iconHolderView.paddingTop -
-                                            iconHolderView.paddingBottom
-                                    panelViewController.updateForContentDimensions(
-                                        width,
-                                        newHeight + bottomInset,
-                                        0, /* animateDurationMs */
-                                    )
-                                }
-                                size.isMedium && currentSize.isSmall -> {
-                                    val duration = ANIMATE_SMALL_TO_MEDIUM_DURATION_MS
-                                    panelViewController.updateForContentDimensions(
-                                        width,
-                                        height,
-                                        duration,
-                                    )
-                                    startMonitoredAnimation(
-                                        listOf(
-                                            iconHolderView.asVerticalAnimator(
-                                                duration = duration.toLong(),
-                                                toY =
-                                                    iconHolderOriginalY -
-                                                        viewsToHideWhenSmall
-                                                            .filter { it.isGone }
-                                                            .sumOf { it.height },
-                                            ),
-                                            viewsToFadeInOnSizeChange.asFadeInAnimator(
-                                                duration = duration.toLong(),
-                                                delay = duration.toLong(),
-                                            ),
+                view.repeatWhenAttached {
+                    var currentSize: PromptSize? = null
+                    lifecycleScope.launch {
+                        combine(viewModel.position, viewModel.size, ::Pair).collect {
+                            (position, size) ->
+                            view.doOnAttach {
+                                measureBounds(position)
+
+                                when {
+                                    size.isSmall -> {
+                                        val ratio =
+                                            if (view.isLandscape()) {
+                                                (windowBounds.height() -
+                                                        bottomInset -
+                                                        viewModel.promptMargin)
+                                                    .toFloat() / windowBounds.height()
+                                            } else {
+                                                (windowBounds.height() - viewModel.promptMargin)
+                                                    .toFloat() / windowBounds.height()
+                                            }
+                                        smallConstraintSet.setVerticalBias(iconHolderView.id, ratio)
+
+                                        smallConstraintSet.applyTo(view as ConstraintLayout?)
+                                    }
+                                    size.isMedium && currentSize.isSmall -> {
+                                        val autoTransition = AutoTransition()
+                                        autoTransition.setDuration(
+                                            ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
                                         )
-                                    )
-                                }
-                                size.isMedium && currentSize.isNullOrNotSmall -> {
-                                    panelViewController.updateForContentDimensions(
-                                        width,
-                                        height,
-                                        0, /* animateDurationMs */
-                                    )
-                                }
-                                size.isLarge -> {
-                                    val duration = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS
-                                    panelViewController.setUseFullScreen(true)
-                                    panelViewController.updateForContentDimensions(
-                                        panelViewController.containerWidth,
-                                        panelViewController.containerHeight,
-                                        duration,
-                                    )
 
-                                    startMonitoredAnimation(
-                                        listOf(
-                                            view.asVerticalAnimator(
-                                                duration.toLong() * 2 / 3,
-                                                toY = view.y - fullSizeYOffset
-                                            ),
-                                            listOf(view)
-                                                .asFadeInAnimator(
-                                                    duration = duration.toLong() / 2,
-                                                    delay = duration.toLong(),
-                                                ),
+                                        TransitionManager.beginDelayedTransition(
+                                            view,
+                                            autoTransition
                                         )
-                                    )
-                                    // TODO(b/251476085): clean up (copied from legacy)
-                                    if (view.isAttachedToWindow) {
-                                        val parent = view.parent as? ViewGroup
-                                        parent?.removeView(view)
+                                        mediumConstraintSet.applyTo(view)
+                                    }
+                                    size.isLarge -> {
+                                        val autoTransition = AutoTransition()
+                                        autoTransition.setDuration(
+                                            ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
+                                        )
+
+                                        TransitionManager.beginDelayedTransition(
+                                            view,
+                                            autoTransition
+                                        )
+                                        largeConstraintSet.applyTo(view)
                                     }
                                 }
+
+                                currentSize = size
+                                view.visibility = View.VISIBLE
+                                viewModel.setIsIconViewLoaded(false)
+                                notifyAccessibilityChanged()
+
+                                view.invalidate()
+                                view.requestLayout()
+                            }
+                        }
+                    }
+                }
+            }
+        } else if (panelViewController != null) {
+            val iconHolderView = view.requireViewById<View>(R.id.biometric_icon_frame)
+            val iconPadding = view.resources.getDimension(R.dimen.biometric_dialog_icon_padding)
+            val fullSizeYOffset =
+                view.resources.getDimension(
+                    R.dimen.biometric_dialog_medium_to_large_translation_offset
+                )
+
+            // cache the original position of the icon view (as done in legacy view)
+            // this must happen before any size changes can be made
+            view.doOnLayout {
+                // TODO(b/251476085): this old way of positioning has proven itself unreliable
+                // remove this and associated thing like (UdfpsDialogMeasureAdapter) and
+                // pin to the physical sensor
+                val iconHolderOriginalY = iconHolderView.y
+
+                // bind to prompt
+                // TODO(b/251476085): migrate the legacy panel controller and simplify this
+                view.repeatWhenAttached {
+                    var currentSize: PromptSize? = null
+                    lifecycleScope.launch {
+                        /**
+                         * View is only set visible in BiometricViewSizeBinder once PromptSize is
+                         * determined that accounts for iconView size, to prevent prompt resizing
+                         * being visible to the user.
+                         *
+                         * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint
+                         *   layout is implemented
+                         */
+                        combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect {
+                            (isIconViewLoaded, size) ->
+                            if (!isIconViewLoaded) {
+                                return@collect
                             }
 
-                            currentSize = size
-                            view.visibility = View.VISIBLE
-                            viewModel.setIsIconViewLoaded(false)
-                            notifyAccessibilityChanged()
+                            // prepare for animated size transitions
+                            for (v in viewsToHideWhenSmall) {
+                                v.showContentOrHide(forceHide = size.isSmall)
+                            }
+                            if (currentSize == null && size.isSmall) {
+                                iconHolderView.alpha = 0f
+                            }
+                            if ((currentSize.isSmall && size.isMedium) || size.isSmall) {
+                                viewsToFadeInOnSizeChange.forEach { it.alpha = 0f }
+                            }
+
+                            // TODO(b/302735104): Fix wrong height due to the delay of
+                            // PromptContentView. addOnLayoutChangeListener() will cause crash when
+                            // showing credential view, since |PromptIconViewModel| won't release
+                            // the
+                            // flow.
+                            // propagate size changes to legacy panel controller and animate
+                            // transitions
+                            view.doOnLayout {
+                                val width = view.measuredWidth
+                                val height = view.measuredHeight
+
+                                when {
+                                    size.isSmall -> {
+                                        iconHolderView.alpha = 1f
+                                        val bottomInset =
+                                            windowManager.maximumWindowMetrics.windowInsets
+                                                .getInsets(WindowInsets.Type.navigationBars())
+                                                .bottom
+                                        iconHolderView.y =
+                                            if (view.isLandscape()) {
+                                                (view.height -
+                                                    iconHolderView.height -
+                                                    bottomInset) / 2f
+                                            } else {
+                                                view.height -
+                                                    iconHolderView.height -
+                                                    iconPadding -
+                                                    bottomInset
+                                            }
+                                        val newHeight =
+                                            iconHolderView.height + (2 * iconPadding.toInt()) -
+                                                iconHolderView.paddingTop -
+                                                iconHolderView.paddingBottom
+                                        panelViewController.updateForContentDimensions(
+                                            width,
+                                            newHeight + bottomInset,
+                                            0, /* animateDurationMs */
+                                        )
+                                    }
+                                    size.isMedium && currentSize.isSmall -> {
+                                        val duration = ANIMATE_SMALL_TO_MEDIUM_DURATION_MS
+                                        panelViewController.updateForContentDimensions(
+                                            width,
+                                            height,
+                                            duration,
+                                        )
+                                        startMonitoredAnimation(
+                                            listOf(
+                                                iconHolderView.asVerticalAnimator(
+                                                    duration = duration.toLong(),
+                                                    toY =
+                                                        iconHolderOriginalY -
+                                                            viewsToHideWhenSmall
+                                                                .filter { it.isGone }
+                                                                .sumOf { it.height },
+                                                ),
+                                                viewsToFadeInOnSizeChange.asFadeInAnimator(
+                                                    duration = duration.toLong(),
+                                                    delay = duration.toLong(),
+                                                ),
+                                            )
+                                        )
+                                    }
+                                    size.isMedium && currentSize.isNullOrNotSmall -> {
+                                        panelViewController.updateForContentDimensions(
+                                            width,
+                                            height,
+                                            0, /* animateDurationMs */
+                                        )
+                                    }
+                                    size.isLarge -> {
+                                        val duration = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS
+                                        panelViewController.setUseFullScreen(true)
+                                        panelViewController.updateForContentDimensions(
+                                            panelViewController.containerWidth,
+                                            panelViewController.containerHeight,
+                                            duration,
+                                        )
+
+                                        startMonitoredAnimation(
+                                            listOf(
+                                                view.asVerticalAnimator(
+                                                    duration.toLong() * 2 / 3,
+                                                    toY = view.y - fullSizeYOffset
+                                                ),
+                                                listOf(view)
+                                                    .asFadeInAnimator(
+                                                        duration = duration.toLong() / 2,
+                                                        delay = duration.toLong(),
+                                                    ),
+                                            )
+                                        )
+                                        // TODO(b/251476085): clean up (copied from legacy)
+                                        if (view.isAttachedToWindow) {
+                                            val parent = view.parent as? ViewGroup
+                                            parent?.removeView(view)
+                                        }
+                                    }
+                                }
+
+                                currentSize = size
+                                view.visibility = View.VISIBLE
+                                viewModel.setIsIconViewLoaded(false)
+                                notifyAccessibilityChanged()
+                            }
                         }
                     }
                 }
@@ -244,6 +454,20 @@
         }
 }
 
+private fun View.centerX(): Int {
+    return (x + width / 2).toInt()
+}
+
+private fun View.centerY(): Int {
+    return (y + height / 2).toInt()
+}
+
+private fun Rect.shouldAdjustLeftGuideline(): Boolean = left != -1
+
+private fun Rect.shouldAdjustRightGuideline(): Boolean = right != -1
+
+private fun Rect.shouldAdjustBottomGuideline(): Boolean = bottom != -1
+
 private fun View.asVerticalAnimator(
     duration: Long,
     toY: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 6e3bcf5..2e47375 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -17,13 +17,17 @@
 
 package com.android.systemui.biometrics.ui.binder
 
+import android.graphics.Rect
 import android.graphics.drawable.Animatable2
 import android.graphics.drawable.AnimatedVectorDrawable
 import android.graphics.drawable.Drawable
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.airbnb.lottie.LottieAnimationView
 import com.android.settingslib.widget.LottieColorUtils
+import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel.AuthType
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
@@ -119,6 +123,24 @@
                 }
 
                 launch {
+                    viewModel.iconPosition.collect { position ->
+                        if (constraintBp() && position != Rect()) {
+                            val iconParams = iconView.layoutParams as ConstraintLayout.LayoutParams
+
+                            if (position.left != -1) {
+                                iconParams.endToEnd = ConstraintSet.UNSET
+                                iconParams.leftMargin = position.left
+                            }
+                            if (position.top != -1) {
+                                iconParams.bottomToBottom = ConstraintSet.UNSET
+                                iconParams.topMargin = position.top
+                            }
+                            iconView.layoutParams = iconParams
+                        }
+                    }
+                }
+
+                launch {
                     viewModel.iconAsset
                         .sample(
                             combine(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 3defec5..b7cffaf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -20,8 +20,11 @@
 import android.annotation.DrawableRes
 import android.annotation.RawRes
 import android.content.res.Configuration
+import android.graphics.Rect
+import android.util.RotationUtils
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.res.R
@@ -42,7 +45,8 @@
 constructor(
     promptViewModel: PromptViewModel,
     private val displayStateInteractor: DisplayStateInteractor,
-    promptSelectorInteractor: PromptSelectorInteractor
+    promptSelectorInteractor: PromptSelectorInteractor,
+    udfpsOverlayInteractor: UdfpsOverlayInteractor,
 ) {
 
     /** Auth types for the UI to display. */
@@ -71,7 +75,40 @@
             } else if (modalities.hasFingerprintOnly) {
                 AuthType.Fingerprint
             } else {
-                throw IllegalStateException("unexpected modality: $modalities")
+                // TODO(b/288175072): Remove, currently needed for transition to credential view
+                AuthType.Fingerprint
+            }
+        }
+
+    val udfpsSensorBounds: Flow<Rect> =
+        combine(
+                udfpsOverlayInteractor.udfpsOverlayParams,
+                displayStateInteractor.currentRotation
+            ) { params, rotation ->
+                val rotatedBounds = Rect(params.sensorBounds)
+                RotationUtils.rotateBounds(
+                    rotatedBounds,
+                    params.naturalDisplayWidth,
+                    params.naturalDisplayHeight,
+                    rotation.ordinal
+                )
+                rotatedBounds
+            }
+            .distinctUntilChanged()
+
+    val iconPosition: Flow<Rect> =
+        combine(udfpsSensorBounds, promptViewModel.size, promptViewModel.modalities) {
+            sensorBounds,
+            size,
+            modalities ->
+            // If not Udfps, icon does not change from default layout position
+            if (!modalities.hasUdfps) {
+                Rect() // Empty rect, don't offset from default position
+            } else if (size.isSmall) {
+                // When small with Udfps, only set horizontal position
+                Rect(sensorBounds.left, -1, sensorBounds.right, -1)
+            } else {
+                sensorBounds
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptPosition.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptPosition.kt
new file mode 100644
index 0000000..d45dad6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptPosition.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+/** The position of a biometric prompt */
+enum class PromptPosition {
+    Top,
+    Bottom,
+    Left,
+    Right,
+}
+
+val PromptPosition?.isBottom: Boolean
+    get() = this != null && this == PromptPosition.Bottom
+
+val PromptPosition?.isLeft: Boolean
+    get() = this != null && this == PromptPosition.Left
+
+val PromptPosition?.isRight: Boolean
+    get() = this != null && this == PromptPosition.Right
+
+val PromptPosition?.isTop: Boolean
+    get() = this != null && this == PromptPosition.Top
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index a39aabf..ef5c37ea 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -82,11 +82,23 @@
     val faceIconHeight: Int =
         context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
 
+    val fingerprintSensorDiameter: Int =
+        (udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds.width() *
+                udfpsOverlayInteractor.udfpsOverlayParams.value.scaleFactor)
+            .toInt()
+    val fingerprintAffordanceSize: Pair<Int, Int>? =
+        if (fingerprintSensorDiameter != 0)
+            Pair(fingerprintSensorDiameter, fingerprintSensorDiameter)
+        else null
+
     private val _accessibilityHint = MutableSharedFlow<String>()
 
     /** Hint for talkback directional guidance */
     val accessibilityHint: Flow<String> = _accessibilityHint.asSharedFlow()
 
+    val promptMargin: Int =
+        context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_border_padding)
+
     private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
     /** If the user is currently authenticating (i.e. at least one biometric is scanning). */
@@ -136,6 +148,22 @@
     /** Event fired to the view indicating a [HapticFeedbackConstants] to be played */
     val hapticsToPlay = _hapticsToPlay.asStateFlow()
 
+    /** The current position of the prompt */
+    val position: Flow<PromptPosition> =
+        combine(_forceLargeSize, modalities, displayStateInteractor.currentRotation) {
+                forceLarge,
+                modalities,
+                rotation ->
+                when {
+                    forceLarge || !modalities.hasUdfps -> PromptPosition.Bottom
+                    rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right
+                    rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left
+                    rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top
+                    else -> PromptPosition.Bottom
+                }
+            }
+            .distinctUntilChanged()
+
     /** The size of the prompt. */
     val size: Flow<PromptSize> =
         combine(
@@ -196,7 +224,12 @@
             .distinctUntilChanged()
 
     val iconViewModel: PromptIconViewModel =
-        PromptIconViewModel(this, displayStateInteractor, promptSelectorInteractor)
+        PromptIconViewModel(
+            this,
+            displayStateInteractor,
+            promptSelectorInteractor,
+            udfpsOverlayInteractor
+        )
 
     private val _isIconViewLoaded = MutableStateFlow(false)
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
index c46f0d1..33edb80 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
@@ -21,21 +21,21 @@
 
 /** Data model of media on the communal hub. */
 data class CommunalMediaModel(
-    val hasAnyMediaOrRecommendation: Boolean,
+    val hasActiveMediaOrRecommendation: Boolean,
     val createdTimestampMillis: Long = 0L,
 ) : Diffable<CommunalMediaModel> {
     companion object {
         val INACTIVE =
             CommunalMediaModel(
-                hasAnyMediaOrRecommendation = false,
+                hasActiveMediaOrRecommendation = false,
             )
     }
 
     override fun logDiffs(prevVal: CommunalMediaModel, row: TableRowLogger) {
-        if (hasAnyMediaOrRecommendation != prevVal.hasAnyMediaOrRecommendation) {
+        if (hasActiveMediaOrRecommendation != prevVal.hasActiveMediaOrRecommendation) {
             row.logChange(
                 columnName = "isMediaActive",
-                value = hasAnyMediaOrRecommendation,
+                value = hasActiveMediaOrRecommendation,
             )
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index 2b66491..201be51 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -73,10 +73,10 @@
         )
 
     private fun updateMediaModel(data: MediaData? = null) {
-        if (mediaDataManager.hasAnyMediaOrRecommendation()) {
+        if (mediaDataManager.hasActiveMediaOrRecommendation()) {
             _mediaModel.value =
                 CommunalMediaModel(
-                    hasAnyMediaOrRecommendation = true,
+                    hasActiveMediaOrRecommendation = true,
                     createdTimestampMillis = data?.createdTimestampMillis ?: 0L,
                 )
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 75a27a2..950ac3c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -318,7 +318,7 @@
             )
 
             // Add UMO
-            if (media.hasAnyMediaOrRecommendation) {
+            if (media.hasActiveMediaOrRecommendation) {
                 ongoingContent.add(
                     CommunalContentModel.Umo(
                         createdTimestampMillis = media.createdTimestampMillis,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 0c12841..40d2d16 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -87,7 +87,7 @@
         with(mediaHost) {
             expansion = MediaHostState.EXPANDED
             expandedMatchesParentHeight = true
-            showsOnlyActiveMedia = false
+            showsOnlyActiveMedia = true
             falsingProtectionNeeded = false
             init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 947cb02..8df7e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.compose
 
-import android.app.Dialog
 import android.content.Context
 import android.view.View
 import android.view.WindowInsets
@@ -33,7 +32,6 @@
 import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.StateFlow
@@ -96,11 +94,11 @@
         sceneByKey: Map<SceneKey, Scene>,
     ): View
 
-    /** Creates sticky key dialog presenting provided [viewModel] */
-    fun createStickyKeysDialog(
-        dialogFactory: SystemUIDialogFactory,
+    /** Creates sticky key indicator content presenting provided [viewModel] */
+    fun createStickyKeysIndicatorContent(
+        context: Context,
         viewModel: StickyKeysIndicatorViewModel
-    ): Dialog
+    ): View
 
     /** Create a [View] to represent [viewModel] on screen. */
     fun createCommunalView(
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index c93b8e1..1230156 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -292,7 +292,7 @@
 
     private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
         return if (DEBUG) {
-            traceEach(flowName, logcat = true)
+            traceEach(flowName, logcat = true, traceEmissionCount = true)
         } else {
             this
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index c331164..537cacd 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -93,7 +93,11 @@
             }
 
             mDockState = dockState;
-            if (isPulsing()) {
+            if (mMachine.isExecutingTransition() || isPulsing()) {
+                // If the device is in the middle of executing a transition or is pulsing,
+                // exit early instead of requesting a new state. DozeMachine
+                // will check the docked state and resolveIntermediateState in the next
+                // transition after pulse done.
                 return;
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
new file mode 100644
index 0000000..3ed58a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.stickykeys.ui
+
+import android.app.Dialog
+import android.content.Context
+import android.view.Gravity
+import android.view.Window
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
+import androidx.activity.ComponentDialog
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+@SysUISingleton
+class StickyKeyDialogFactory
+@Inject
+constructor(
+    @Application val context: Context,
+) {
+
+    fun create(viewModel: StickyKeysIndicatorViewModel): Dialog {
+        return createStickyKeyIndicator(viewModel)
+    }
+
+    private fun createStickyKeyIndicator(viewModel: StickyKeysIndicatorViewModel): Dialog {
+        return ComponentDialog(context, R.style.Theme_SystemUI_Dialog).apply {
+            // because we're requesting window feature it must be called before setting content
+            window?.setStickyKeyWindowAttributes()
+            setContentView(ComposeFacade.createStickyKeysIndicatorContent(context, viewModel))
+        }
+    }
+
+    private fun Window.setStickyKeyWindowAttributes() {
+        requestFeature(Window.FEATURE_NO_TITLE)
+        setType(TYPE_STATUS_BAR_SUB_PANEL)
+        addFlags(FLAG_NOT_FOCUSABLE or FLAG_NOT_TOUCHABLE)
+        clearFlags(FLAG_DIM_BEHIND)
+        setGravity(Gravity.TOP or Gravity.END)
+        attributes =
+            WindowManager.LayoutParams().apply {
+                copyFrom(attributes)
+                title = "StickyKeysIndicator"
+            }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
index c3a618d..842fd04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
@@ -18,16 +18,11 @@
 
 import android.app.Dialog
 import android.util.Log
-import android.view.Gravity
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.view.Window
-import android.view.WindowManager
 import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import javax.inject.Inject
@@ -37,7 +32,7 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val dialogFactory: SystemUIDialogFactory,
+    private val stickyKeyDialogFactory: StickyKeyDialogFactory,
     private val viewModel: StickyKeysIndicatorViewModel,
     private val stickyKeysLogger: StickyKeysLogger,
 ) {
@@ -57,25 +52,10 @@
                     dialog?.dismiss()
                     dialog = null
                 } else if (dialog == null) {
-                    dialog = ComposeFacade.createStickyKeysDialog(dialogFactory, viewModel).apply {
-                        window?.setAttributes()
-                        show()
-                    }
+                    dialog = stickyKeyDialogFactory.create(viewModel)
+                    dialog?.show()
                 }
             }
         }
     }
-
-    private fun Window.setAttributes() {
-        setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
-        addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
-        addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
-        clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
-        setGravity(Gravity.TOP or Gravity.END)
-        attributes = WindowManager.LayoutParams().apply {
-            copyFrom(attributes)
-            width = WRAP_CONTENT
-            title = "StickyKeysIndicator"
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
index 9b83b75..ee3706a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
@@ -80,7 +80,7 @@
     }
 
     /**
-     * Click listener for messsage.
+     * Click listener for message.
      */
     public @Nullable View.OnClickListener getClickListener() {
         return mOnClickListener;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f085e88..4766a84 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -444,7 +444,7 @@
     /**
      * Whether a hide is pending and we are just waiting for #startKeyguardExitAnimation to be
      * called.
-     * */
+     */
     private boolean mHiding;
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 7f8be1c..ef50265 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -14,6 +14,7 @@
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
 import com.android.systemui.ExpandHelper
+import com.android.systemui.Flags.nsslFalsingFix
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy
 import com.android.systemui.classifier.Classifier
@@ -889,7 +890,7 @@
                     isDraggingDown = false
                     isTrackpadReverseScroll = false
                     shadeRepository.setLegacyLockscreenShadeTracking(false)
-                    if (KeyguardShadeMigrationNssl.isEnabled) {
+                    if (nsslFalsingFix() || KeyguardShadeMigrationNssl.isEnabled) {
                         return true
                     }
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index eaff8a0..a2ff406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -21,8 +21,9 @@
 
 import static com.android.app.animation.Interpolators.STANDARD;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.server.notification.Flags.screenshareNotificationHiding;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Flags.nsslFalsingFix;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
@@ -2054,7 +2055,7 @@
             }
             boolean horizontalSwipeWantsIt = false;
             boolean scrollerWantsIt = false;
-            if (KeyguardShadeMigrationNssl.isEnabled()) {
+            if (nsslFalsingFix() || KeyguardShadeMigrationNssl.isEnabled()) {
                 // Reverse the order relative to the else statement. onScrollTouch will reset on an
                 // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes.
                 if (mLongPressedView == null && !mView.isBeingDragged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
index e921a59..64cd526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
@@ -344,10 +344,15 @@
     }
 
     private fun createAndStartSut(): CameraAvailabilityListener {
-        return CameraAvailabilityListener.build(context, context.mainExecutor).also {
-            it.addTransitionCallback(cameraTransitionCallback)
-            it.startListening()
-        }
+        return CameraAvailabilityListener.build(
+                context,
+                context.mainExecutor,
+                CameraProtectionLoader((context))
+            )
+            .also {
+                it.addTransitionCallback(cameraTransitionCallback)
+                it.startListening()
+            }
     }
 
     private class TestCameraTransitionCallback :
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderTest.kt
new file mode 100644
index 0000000..238e5e9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 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
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CameraProtectionLoaderTest : SysuiTestCase() {
+
+    private val loader = CameraProtectionLoader(context)
+
+    @Before
+    fun setUp() {
+        overrideResource(R.string.config_protectedCameraId, OUTER_CAMERA_LOGICAL_ID)
+        overrideResource(R.string.config_protectedPhysicalCameraId, OUTER_CAMERA_PHYSICAL_ID)
+        overrideResource(
+            R.string.config_frontBuiltInDisplayCutoutProtection,
+            OUTER_CAMERA_PROTECTION_PATH
+        )
+        overrideResource(R.string.config_protectedInnerCameraId, INNER_CAMERA_LOGICAL_ID)
+        overrideResource(R.string.config_protectedInnerPhysicalCameraId, INNER_CAMERA_PHYSICAL_ID)
+        overrideResource(
+            R.string.config_innerBuiltInDisplayCutoutProtection,
+            INNER_CAMERA_PROTECTION_PATH
+        )
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList() {
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos)
+            .containsExactly(OUTER_CAMERA_PROTECTION_INFO, INNER_CAMERA_PROTECTION_INFO)
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList_outerCameraIdEmpty_onlyReturnsInnerInfo() {
+        overrideResource(R.string.config_protectedCameraId, "")
+
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos).containsExactly(INNER_CAMERA_PROTECTION_INFO)
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList_innerCameraIdEmpty_onlyReturnsOuterInfo() {
+        overrideResource(R.string.config_protectedInnerCameraId, "")
+
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos).containsExactly(OUTER_CAMERA_PROTECTION_INFO)
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList_innerAndOuterCameraIdsEmpty_returnsEmpty() {
+        overrideResource(R.string.config_protectedCameraId, "")
+        overrideResource(R.string.config_protectedInnerCameraId, "")
+
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos).isEmpty()
+    }
+
+    private fun CameraProtectionInfo.toTestableVersion() =
+        TestableProtectionInfo(logicalCameraId, physicalCameraId, cutoutBounds)
+
+    /**
+     * "Testable" version, because the original version contains a Path property, which doesn't
+     * implement equals.
+     */
+    private data class TestableProtectionInfo(
+        val logicalCameraId: String,
+        val physicalCameraId: String?,
+        val cutoutBounds: Rect,
+    )
+
+    companion object {
+        private const val OUTER_CAMERA_LOGICAL_ID = "1"
+        private const val OUTER_CAMERA_PHYSICAL_ID = "11"
+        private const val OUTER_CAMERA_PROTECTION_PATH = "M 0,0 H 10,10 V 10,10 H 0,10 Z"
+        private val OUTER_CAMERA_PROTECTION_BOUNDS =
+            Rect(/* left = */ 0, /* top = */ 0, /* right = */ 10, /* bottom = */ 10)
+        private val OUTER_CAMERA_PROTECTION_INFO =
+            TestableProtectionInfo(
+                OUTER_CAMERA_LOGICAL_ID,
+                OUTER_CAMERA_PHYSICAL_ID,
+                OUTER_CAMERA_PROTECTION_BOUNDS
+            )
+
+        private const val INNER_CAMERA_LOGICAL_ID = "2"
+        private const val INNER_CAMERA_PHYSICAL_ID = "22"
+        private const val INNER_CAMERA_PROTECTION_PATH = "M 0,0 H 20,20 V 20,20 H 0,20 Z"
+        private val INNER_CAMERA_PROTECTION_BOUNDS =
+            Rect(/* left = */ 0, /* top = */ 0, /* right = */ 20, /* bottom = */ 20)
+        private val INNER_CAMERA_PROTECTION_INFO =
+            TestableProtectionInfo(
+                INNER_CAMERA_LOGICAL_ID,
+                INNER_CAMERA_PHYSICAL_ID,
+                INNER_CAMERA_PROTECTION_BOUNDS
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index c07148b..1f1fa72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -176,6 +176,8 @@
     private FakeFacePropertyRepository mFakeFacePropertyRepository =
             new FakeFacePropertyRepository();
     private List<DecorProvider> mMockCutoutList;
+    private final CameraProtectionLoader mCameraProtectionLoader =
+            new CameraProtectionLoader(mContext);
 
     @Before
     public void setup() {
@@ -247,7 +249,7 @@
                 mThreadFactory,
                 mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
-                mFakeFacePropertyRepository, mJavaAdapter) {
+                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader) {
             @Override
             public void start() {
                 super.start();
@@ -1243,7 +1245,7 @@
                 mDotViewController,
                 mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
-                mFakeFacePropertyRepository, mJavaAdapter);
+                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader);
         screenDecorations.start();
         when(mContext.getDisplay()).thenReturn(mDisplay);
         when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index af027e8..6d2df19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -63,6 +63,7 @@
         mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake, mUserTracker);
         mDockHandler.setDozeMachine(mMachine);
 
+        when(mMachine.isExecutingTransition()).thenReturn(false);
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
         when(mMachine.getState()).thenReturn(State.DOZE_AOD);
         doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
@@ -148,4 +149,13 @@
 
         verify(mMachine, never()).requestState(any(State.class));
     }
+
+    @Test
+    public void onEvent_dockedWhileTransitioning_wontRequestStateChange() {
+        when(mMachine.isExecutingTransition()).thenReturn(true);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
+
+        verify(mMachine, never()).requestState(any(State.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index df73cc8..a992956 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyboard.stickykeys.ui
 
+import android.app.Dialog
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.compose.ComposeFacade
@@ -26,8 +27,6 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -40,8 +39,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 
@@ -53,15 +50,13 @@
     private lateinit var coordinator: StickyKeysIndicatorCoordinator
     private val testScope = TestScope(StandardTestDispatcher())
     private val stickyKeysRepository = FakeStickyKeysRepository()
-    private val dialog = mock<ComponentSystemUIDialog>()
+    private val dialog = mock<Dialog>()
 
     @Before
     fun setup() {
         Assume.assumeTrue(ComposeFacade.isComposeAvailable())
-        val dialogFactory = mock<SystemUIDialogFactory> {
-            whenever(applicationContext).thenReturn(context)
-            whenever(create(any(), anyInt(), anyBoolean())).thenReturn(dialog)
-        }
+        val dialogFactory = mock<StickyKeyDialogFactory>()
+        whenever(dialogFactory.create(any())).thenReturn(dialog)
         val keyboardRepository = Kosmos().keyboardRepository
         val viewModel = StickyKeysIndicatorViewModel(
                 stickyKeysRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 0831971..25a7eb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -16,12 +16,14 @@
 
 package com.android.systemui.shade
 
+import android.content.Context
 import android.os.PowerManager
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.ViewUtils
 import android.view.MotionEvent
 import android.view.View
+import android.view.WindowManager
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -33,18 +35,21 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import org.junit.Assert.assertThrows
 import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.BeforeClass
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -52,12 +57,17 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@Ignore("b/323053208")
+@ExperimentalCoroutinesApi
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class GlanceableHubContainerControllerTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
+    private val kosmos: Kosmos =
+        testKosmos().apply {
+            // UnconfinedTestDispatcher makes testing simpler due to CommunalInteractor flows using
+            // SharedFlow
+            testDispatcher = UnconfinedTestDispatcher()
+        }
 
     @Mock private lateinit var communalViewModel: CommunalViewModel
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@@ -104,17 +114,19 @@
             R.dimen.communal_bottom_edge_swipe_region_height,
             BOTTOM_SWIPE_REGION_WIDTH
         )
+
+        initAndAttachContainerView()
     }
 
     @Test
-    fun isEnabled_interactorEnabled_interceptsTouches() {
+    fun isEnabled_communalEnabled_returnsTrue() {
         communalRepository.setIsCommunalEnabled(true)
 
         assertThat(underTest.isEnabled()).isTrue()
     }
 
     @Test
-    fun isEnabled_interactorDisabled_doesNotIntercept() {
+    fun isEnabled_communalDisabled_returnsFalse() {
         communalRepository.setIsCommunalEnabled(false)
 
         assertThat(underTest.isEnabled()).isFalse()
@@ -124,11 +136,29 @@
     fun initView_notEnabled_throwsException() {
         communalRepository.setIsCommunalEnabled(false)
 
+        underTest =
+            GlanceableHubContainerController(
+                communalInteractor,
+                communalViewModel,
+                keyguardTransitionInteractor,
+                shadeInteractor,
+                powerManager,
+            )
+
         assertThrows(RuntimeException::class.java) { underTest.initView(context) }
     }
 
     @Test
     fun initView_calledTwice_throwsException() {
+        underTest =
+            GlanceableHubContainerController(
+                communalInteractor,
+                communalViewModel,
+                keyguardTransitionInteractor,
+                shadeInteractor,
+                powerManager,
+            )
+
         // First call succeeds.
         underTest.initView(context)
 
@@ -137,25 +167,20 @@
     }
 
     @Test
-    fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() {
-        // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+    fun onTouchEvent_communalClosed_doesNotIntercept() {
+        // Communal is closed.
+        goToScene(CommunalSceneKey.Blank)
 
-        initAndAttachContainerView()
-
-        // Touch events are intercepted.
-        assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
+        assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
     }
 
     @Test
-    fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() {
-        // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+    fun onTouchEvent_openGesture_interceptsTouches() {
+        // Communal is closed.
+        goToScene(CommunalSceneKey.Blank)
 
-        initAndAttachContainerView()
-
-        // Initial touch down is intercepted, and so are touches outside of the region, until an up
-        // event is received.
+        // Initial touch down is intercepted, and so are touches outside of the region, until an
+        // up event is received.
         assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
         assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
         assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
@@ -165,34 +190,27 @@
     @Test
     fun onTouchEvent_communalOpen_interceptsTouches() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+        goToScene(CommunalSceneKey.Communal)
 
-        initAndAttachContainerView()
-        testableLooper.processAllMessages()
-
-        // Touch events are intercepted.
+        // Touch events are intercepted outside of any gesture areas.
         assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
         // User activity sent to PowerManager.
         verify(powerManager).userActivity(any(), any(), any())
     }
 
     @Test
-    fun onTouchEvent_topSwipeWhenHubOpen_returnsFalse() {
+    fun onTouchEvent_topSwipeWhenCommunalOpen_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         // Touch event in the top swipe reqgion is not intercepted.
         assertThat(underTest.onTouchEvent(DOWN_IN_TOP_SWIPE_REGION_EVENT)).isFalse()
     }
 
     @Test
-    fun onTouchEvent_bottomSwipeWhenHubOpen_returnsFalse() {
+    fun onTouchEvent_bottomSwipeWhenCommunalOpen_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         // Touch event in the bottom swipe reqgion is not intercepted.
         assertThat(underTest.onTouchEvent(DOWN_IN_BOTTOM_SWIPE_REGION_EVENT)).isFalse()
@@ -201,9 +219,7 @@
     @Test
     fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         // Bouncer is visible.
         bouncerShowingFlow.value = true
@@ -218,9 +234,7 @@
     @Test
     fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         shadeShowingFlow.value = true
         testableLooper.processAllMessages()
@@ -232,10 +246,7 @@
     @Test
     fun onTouchEvent_containerViewDisposed_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
-        testableLooper.processAllMessages()
+        goToScene(CommunalSceneKey.Communal)
 
         // Touch events are intercepted.
         assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
@@ -253,15 +264,24 @@
         parentView = FrameLayout(context)
         parentView.addView(containerView)
 
-        // Make view clickable so that dispatchTouchEvent returns true.
-        containerView.isClickable = true
-
         underTest.initView(containerView)
+
         // Attach the view so that flows start collecting.
         ViewUtils.attachView(parentView)
-        // Give the view a size so that determining if a touch starts at the right edge works.
-        parentView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT)
-        containerView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT)
+
+        // Give the view a fixed size to simplify testing for edge swipes.
+        val lp =
+            parentView.layoutParams.apply {
+                width = CONTAINER_WIDTH
+                height = CONTAINER_HEIGHT
+            }
+        val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+        wm.updateViewLayout(parentView, lp)
+    }
+
+    private fun goToScene(scene: CommunalSceneKey) {
+        communalRepository.setDesiredScene(scene)
+        testableLooper.processAllMessages()
     }
 
     companion object {
@@ -271,13 +291,17 @@
         private const val TOP_SWIPE_REGION_WIDTH = 20
         private const val BOTTOM_SWIPE_REGION_WIDTH = 20
 
+        /**
+         * A touch down event right in the middle of the screen, to avoid being in any of the swipe
+         * regions.
+         */
         private val DOWN_EVENT =
             MotionEvent.obtain(
                 0L,
                 0L,
                 MotionEvent.ACTION_DOWN,
-                CONTAINER_WIDTH.toFloat(),
-                CONTAINER_HEIGHT.toFloat(),
+                CONTAINER_WIDTH.toFloat() / 2,
+                CONTAINER_HEIGHT.toFloat() / 2,
                 0
             )
         private val DOWN_IN_RIGHT_SWIPE_REGION_EVENT =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 9ea4142..f3e9203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -616,7 +616,7 @@
                 mPositioner,
                 mock(DisplayController.class),
                 mOneHandedOptional,
-                Optional.of(mock(DragAndDropController.class)),
+                mock(DragAndDropController.class),
                 syncExecutor,
                 mock(Handler.class),
                 mTaskViewTransitions,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 9ad234e1..4a5ebd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -71,7 +71,7 @@
             BubblePositioner positioner,
             DisplayController displayController,
             Optional<OneHandedController> oneHandedOptional,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             ShellExecutor shellMainExecutor,
             Handler shellMainHandler,
             TaskViewTransitions taskViewTransitions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
index 3ea3ccf..1884a32 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
@@ -28,7 +28,7 @@
     fun mediaActive(timestamp: Long = 0L) {
         _mediaModel.value =
             CommunalMediaModel(
-                hasAnyMediaOrRecommendation = true,
+                hasActiveMediaOrRecommendation = true,
                 createdTimestampMillis = timestamp,
             )
     }
diff --git a/ravenwood/bulk_enable.py b/ravenwood/bulk_enable.py
index 83fda9e..aafaaff 100644
--- a/ravenwood/bulk_enable.py
+++ b/ravenwood/bulk_enable.py
@@ -21,7 +21,7 @@
 classes that have partial success.
 
 Typical usage:
-$ ENABLE_PROBE_IGNORED=1 atest MyTestsRavenwood
+$ RAVENWOOD_RUN_DISABLED_TESTS=1 atest MyTestsRavenwood
 $ cd /path/to/tests/root
 $ python bulk_enable.py /path/to/atest/output/host_log.txt
 """
diff --git a/ravenwood/coretest/Android.bp b/ravenwood/coretest/Android.bp
new file mode 100644
index 0000000..9b7f8f7
--- /dev/null
+++ b/ravenwood/coretest/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_ravenwood_test {
+    name: "RavenwoodCoreTest",
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+    ],
+
+    srcs: [
+        "test/**/*.java",
+    ],
+    sdk_version: "test_current",
+    auto_gen_config: true,
+}
diff --git a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
new file mode 100644
index 0000000..e58c282
--- /dev/null
+++ b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 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.platform.test.ravenwood.coretest;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for the test runner validator in RavenwoodRule.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodTestRunnerValidationTest {
+    // Note the following rules don't have a @Rule, because they need to be applied in a specific
+    // order. So we use a RuleChain instead.
+    private ExpectedException mThrown = ExpectedException.none();
+    private final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Rule
+    public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
+
+    public RavenwoodTestRunnerValidationTest() {
+        Assume.assumeTrue(mRavenwood._ravenwood_private$isOptionalValidationEnabled());
+        // Because RavenwoodRule will throw this error before executing the test method,
+        // we can't do it in the test method itself.
+        // So instead, we initialize it here.
+        mThrown.expectMessage("Switch to androidx.test.ext.junit.runners.AndroidJUnit4");
+    }
+
+    @Test
+    public void testValidateTestRunner() {
+    }
+}
diff --git a/ravenwood/fix_test_runner.py b/ravenwood/fix_test_runner.py
new file mode 100755
index 0000000..99b7a1f
--- /dev/null
+++ b/ravenwood/fix_test_runner.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 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.
+
+"""
+Tool switch the deprecated jetpack test runner to the correct one.
+
+Typical usage:
+$ RAVENWOOD_OPTIONAL_VALIDATION=1 atest MyTestsRavenwood # Prepend RAVENWOOD_RUN_DISABLED_TESTS=1 as needed
+$ cd /path/to/tests/root
+$ python bulk_enable.py /path/to/atest/output/host_log.txt
+"""
+
+import collections
+import os
+import re
+import subprocess
+import sys
+
+re_result = re.compile("I/ModuleListener.+?null-device-0 (.+?)#(.+?) ([A-Z_]+)(.*)$")
+
+OLD_RUNNER = "androidx.test.runner.AndroidJUnit4"
+NEW_RUNNER = "androidx.test.ext.junit.runners.AndroidJUnit4"
+SED_ARG = r"s/%s/%s/g" % (OLD_RUNNER, NEW_RUNNER)
+
+target = collections.defaultdict()
+
+with open(sys.argv[1]) as f:
+    for line in f.readlines():
+        result = re_result.search(line)
+        if result:
+            clazz, method, state, msg = result.groups()
+            if NEW_RUNNER in msg:
+                target[clazz] = 1
+
+if len(target) == 0:
+    print("No tests need updating.")
+    sys.exit(0)
+
+num_fixed = 0
+for clazz in target.keys():
+    print("Fixing test runner", clazz)
+    clazz_match = re.compile("%s\.(kt|java)" % (clazz.split(".")[-1]))
+    found = False
+    for root, dirs, files in os.walk("."):
+        for f in files:
+            if clazz_match.match(f):
+                found = True
+                num_fixed += 1
+                path = os.path.join(root, f)
+                subprocess.run(["sed", "-i", "-E", SED_ARG, path])
+    if not found:
+        print(f"  Warining: tests {clazz} not found")
+
+
+print("Tests fixed", num_fixed)
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 8af561f..85fa65a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -28,7 +28,10 @@
 
 import com.android.internal.os.RuntimeInit;
 
+import org.junit.Assert;
 import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
 
 import java.io.PrintStream;
 import java.util.Map;
@@ -167,4 +170,31 @@
             }
         }
     }
+
+    public static void validate(Statement base, Description description,
+            boolean enableOptionalValidation) {
+        validateTestRunner(base, description, enableOptionalValidation);
+    }
+
+    private static void validateTestRunner(Statement base, Description description,
+            boolean shouldFail) {
+        final var testClass = description.getTestClass();
+        final var runWith = testClass.getAnnotation(RunWith.class);
+        if (runWith == null) {
+            return;
+        }
+
+        // Due to build dependencies, we can't directly refer to androidx classes here,
+        // so just check the class name instead.
+        if (runWith.value().getCanonicalName().equals("androidx.test.runner.AndroidJUnit4")) {
+            var message = "Test " + testClass.getCanonicalName() + " uses deprecated"
+                    + " test runner androidx.test.runner.AndroidJUnit4."
+                    + " Switch to androidx.test.ext.junit.runners.AndroidJUnit4.";
+            if (shouldFail) {
+                Assert.fail(message);
+            } else {
+                System.err.println("Warning: " + message);
+            }
+        }
+    }
 }
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index daed457..764573d 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -89,6 +89,12 @@
     private static final boolean ENABLE_REALLY_DISABLE_PATTERN =
             !REALLY_DISABLE_PATTERN.pattern().isEmpty();
 
+    /**
+     * If true, enable optional validation on running tests.
+     */
+    private static final boolean ENABLE_OPTIONAL_VALIDATION = "1".equals(
+            System.getenv("RAVENWOOD_OPTIONAL_VALIDATION"));
+
     static {
         if (ENABLE_PROBE_IGNORED) {
             System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests");
@@ -275,6 +281,12 @@
         }
     }
 
+    private void commonPrologue(Statement base, Description description) {
+        RavenwoodRuleImpl.logTestRunner("started", description);
+        RavenwoodRuleImpl.validate(base, description, ENABLE_OPTIONAL_VALIDATION);
+        RavenwoodRuleImpl.init(RavenwoodRule.this);
+    }
+
     /**
      * Run the given {@link Statement} with no special treatment.
      */
@@ -284,8 +296,7 @@
             public void evaluate() throws Throwable {
                 Assume.assumeTrue(shouldEnableOnRavenwood(description));
 
-                RavenwoodRuleImpl.logTestRunner("started", description);
-                RavenwoodRuleImpl.init(RavenwoodRule.this);
+                commonPrologue(base, description);
                 try {
                     base.evaluate();
                     RavenwoodRuleImpl.logTestRunner("finished", description);
@@ -310,8 +321,7 @@
             public void evaluate() throws Throwable {
                 Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
 
-                RavenwoodRuleImpl.logTestRunner("started", description);
-                RavenwoodRuleImpl.init(RavenwoodRule.this);
+                commonPrologue(base, description);
                 try {
                     base.evaluate();
                 } catch (Throwable t) {
@@ -331,4 +341,11 @@
             }
         };
     }
+
+    /**
+     * Do not use it outside ravenwood core classes.
+     */
+    public boolean _ravenwood_private$isOptionalValidationEnabled() {
+        return ENABLE_OPTIONAL_VALIDATION;
+    }
 }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 7d172f2..e951351b 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -17,6 +17,7 @@
 package android.platform.test.ravenwood;
 
 import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
 public class RavenwoodRuleImpl {
     public static boolean isOnRavenwood() {
@@ -34,4 +35,8 @@
     public static void logTestRunner(String label, Description description) {
         // No-op when running on a real device
     }
+
+    public static void validate(Statement base, Description description,
+            boolean enableOptionalValidation) {
+    }
 }
diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/minimum-test/Android.bp
index bf3583c..e4ed3d5 100644
--- a/ravenwood/minimum-test/Android.bp
+++ b/ravenwood/minimum-test/Android.bp
@@ -13,6 +13,7 @@
 
     static_libs: [
         "androidx.annotation_annotation",
+        "androidx.test.ext.junit",
         "androidx.test.rules",
     ],
 
diff --git a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
index 7abfecf..03cfad5 100644
--- a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
+++ b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
@@ -18,7 +18,7 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Assert;
 import org.junit.Rule;
diff --git a/services/contextualsearch/OWNERS b/services/contextualsearch/OWNERS
new file mode 100644
index 0000000..0c2612c
--- /dev/null
+++ b/services/contextualsearch/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/contextualsearch/OWNERS
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index a493d7a..797a2e6 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -24,6 +24,8 @@
 import android.content.ComponentName;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SignedPackage;
 import android.os.Build;
 import android.os.CarrierAssociatedAppEntry;
 import android.os.Environment;
@@ -350,6 +352,16 @@
     // updated to avoid cached/potentially tampered results.
     private final Set<String> mPreinstallPackagesWithStrictSignatureCheck = new ArraySet<>();
 
+    // A set of packages that should be considered "trusted packages" by ECM (Enhanced
+    // Confirmation Mode). "Trusted packages" are exempt from ECM (i.e., they will never be
+    // considered "restricted").
+    private final ArraySet<SignedPackage> mEnhancedConfirmationTrustedPackages = new ArraySet<>();
+
+    // A set of packages that should be considered "trusted installers" by ECM (Enhanced
+    // Confirmation Mode). "Trusted installers", and all apps installed by a trusted installer, are
+    // exempt from ECM (i.e., they will never be considered "restricted").
+    private final ArraySet<SignedPackage> mEnhancedConfirmationTrustedInstallers = new ArraySet<>();
+
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
@@ -560,6 +572,14 @@
         return mPreinstallPackagesWithStrictSignatureCheck;
     }
 
+    public ArraySet<SignedPackage> getEnhancedConfirmationTrustedPackages() {
+        return mEnhancedConfirmationTrustedPackages;
+    }
+
+    public ArraySet<SignedPackage> getEnhancedConfirmationTrustedInstallers() {
+        return mEnhancedConfirmationTrustedInstallers;
+    }
+
     /**
      * Only use for testing. Do NOT use in production code.
      * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1558,6 +1578,26 @@
                             }
                         }
                     } break;
+                    case "enhanced-confirmation-trusted-package": {
+                        if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
+                            SignedPackage signedPackage = parseEnhancedConfirmationTrustedPackage(
+                                    parser, permFile, name);
+                            if (signedPackage != null) {
+                                mEnhancedConfirmationTrustedPackages.add(signedPackage);
+                            }
+                            break;
+                        }
+                    } // fall through if flag is not enabled
+                    case "enhanced-confirmation-trusted-installer": {
+                        if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
+                            SignedPackage signedPackage = parseEnhancedConfirmationTrustedPackage(
+                                    parser, permFile, name);
+                            if (signedPackage != null) {
+                                mEnhancedConfirmationTrustedInstallers.add(signedPackage);
+                            }
+                            break;
+                        }
+                    } // fall through if flag is not enabled
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());
@@ -1619,6 +1659,33 @@
         }
     }
 
+    private @Nullable SignedPackage parseEnhancedConfirmationTrustedPackage(XmlPullParser parser,
+            File permFile, String elementName) {
+        String pkgName = parser.getAttributeValue(null, "package");
+        if (TextUtils.isEmpty(pkgName)) {
+            Slog.w(TAG, "<" + elementName + "> without package " + permFile + " at "
+                    + parser.getPositionDescription());
+            return null;
+        }
+
+        String certificateDigestStr = parser.getAttributeValue(null, "sha256-cert-digest");
+        if (TextUtils.isEmpty(certificateDigestStr)) {
+            Slog.w(TAG, "<" + elementName + "> without sha256-cert-digest in " + permFile
+                    + " at " + parser.getPositionDescription());
+            return null;
+        }
+        byte[] certificateDigest = null;
+        try {
+            certificateDigest = new Signature(certificateDigestStr).toByteArray();
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "<" + elementName + "> with invalid sha256-cert-digest in "
+                    + permFile + " at " + parser.getPositionDescription());
+            return null;
+        }
+
+        return new SignedPackage(pkgName, certificateDigest);
+    }
+
     // This method only enables a new Android feature added in U and will not have impact on app
     // compatibility
     @SuppressWarnings("AndroidFrameworkCompatChange")
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 77cb08b..cb15abc 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -914,6 +914,9 @@
             }
 
             mOverrideRequestController.dumpInternal(pw);
+            pw.println();
+
+            mDeviceStatePolicy.dump(pw, /* args= */ null);
         }
     }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
index 5c4e2f3..65e5085 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.text.TextUtils;
+import android.util.Dumpable;
 
 import com.android.server.policy.DeviceStatePolicyImpl;
 
@@ -29,7 +30,7 @@
  *
  * @see DeviceStateManagerService
  */
-public abstract class DeviceStatePolicy {
+public abstract class DeviceStatePolicy implements Dumpable {
     protected final Context mContext;
 
     protected DeviceStatePolicy(@NonNull Context context) {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 50ab3f8..d5945f4 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -21,6 +21,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.util.Dumpable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -31,7 +32,7 @@
  *
  * @see DeviceStatePolicy
  */
-public interface DeviceStateProvider {
+public interface DeviceStateProvider extends Dumpable {
     int SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT = 0;
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index a15cb10..a23c08a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -716,7 +716,10 @@
             // Programmed
             int programedInfo = params[offset] & 0x0F;
             if (isValidProgrammedInfo(programedInfo)) {
-                if (programedInfo == 0x09 || programedInfo == 0x0B) {
+                offset = offset + 1;
+                // Duration Available (2 bytes)
+                if ((programedInfo == 0x09 || programedInfo == 0x0B)
+                        && params.length - offset >= 2) {
                     durationAvailable = true;
                 } else {
                     return true;
@@ -726,16 +729,17 @@
             // Non programmed
             int nonProgramedErrorInfo = params[offset] & 0x0F;
             if (isValidNotProgrammedErrorInfo(nonProgramedErrorInfo)) {
-                if (nonProgramedErrorInfo == 0x0E) {
+                offset = offset + 1;
+                // Duration Available (2 bytes)
+                if (nonProgramedErrorInfo == 0x0E && params.length - offset >= 2) {
                     durationAvailable = true;
                 } else {
                     return true;
                 }
             }
         }
-        offset = offset + 1;
         // Duration Available (2 bytes)
-        if (durationAvailable && params.length - offset >= 2) {
+        if (durationAvailable) {
             return (isValidDurationHours(params[offset]) && isValidMinute(params[offset + 1]));
         }
         return false;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index cb00e44..fff4939 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3185,24 +3185,6 @@
                 }
             }
         }
-
-        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
-            String ime = SecureSettingsWrapper.getString(
-                    Settings.Secure.DEFAULT_INPUT_METHOD, null, mSettings.getUserId());
-            String defaultDeviceIme = SecureSettingsWrapper.getString(
-                    Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, mSettings.getUserId());
-            if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
-                if (DEBUG) {
-                    Slog.v(TAG, "Current input method " + ime + " differs from the stored default"
-                            + " device input method for user " + mSettings.getUserId()
-                            + " - restoring " + defaultDeviceIme);
-                }
-                SecureSettingsWrapper.putString(
-                        Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme,
-                        mSettings.getUserId());
-            }
-        }
-
         // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
         // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
         // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
@@ -5388,11 +5370,7 @@
 
         if (!setSubtypeOnly) {
             // Set InputMethod here
-            final String imeId = imi != null ? imi.getId() : "";
-            mSettings.putSelectedInputMethod(imeId);
-            if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
-                mSettings.putSelectedDefaultDeviceInputMethod(imeId);
-            }
+            mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
         }
     }
 
@@ -5535,9 +5513,6 @@
             return false; // IME is not found or not enabled.
         }
         settings.putSelectedInputMethod(imeId);
-        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
-            settings.putSelectedDefaultDeviceInputMethod(imeId);
-        }
         settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
         return true;
     }
@@ -6584,9 +6559,6 @@
 
                         // Reset selected IME.
                         settings.putSelectedInputMethod(nextIme);
-                        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
-                            settings.putSelectedDefaultDeviceInputMethod(nextIme);
-                        }
                         settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
                     }
                     out.println("Reset current and enabled IMEs for user #" + userId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
index e444db1..a51002b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
@@ -36,6 +36,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Predicate;
 
 /**
@@ -87,6 +88,12 @@
         mMethodMap = methodMap;
         mMethodList = methodMap.values();
         mUserId = userId;
+        String ime = getSelectedInputMethod();
+        String defaultDeviceIme = getSelectedDefaultDeviceInputMethod();
+        if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
+            putSelectedInputMethod(defaultDeviceIme);
+            putSelectedDefaultDeviceInputMethod(null);
+        }
     }
 
     @AnyThread
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1c9bbab..638382e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7955,8 +7955,8 @@
                     && mTelecomManager != null) {
                 try {
                     return mTelecomManager.isInManagedCall()
-                            || mTelecomManager.isInSelfManagedCall(
-                            pkg, UserHandle.getUserHandleForUid(uid));
+                            || mTelecomManager.isInSelfManagedCall(pkg,
+                            UserHandle.getUserHandleForUid(uid), /* hasCrossUserAccess */ true);
                 } catch (IllegalStateException ise) {
                     // Telecom is not ready (this is likely early boot), so there are no calls.
                     return false;
@@ -8844,19 +8844,26 @@
         }
     }
 
+    private PendingIntent getNotificationTimeoutPendingIntent(NotificationRecord record,
+            int flags) {
+        flags |= PendingIntent.FLAG_IMMUTABLE;
+        return PendingIntent.getBroadcast(getContext(),
+                REQUEST_CODE_TIMEOUT,
+                new Intent(ACTION_NOTIFICATION_TIMEOUT)
+                        .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+                        .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+                                .appendPath(record.getKey()).build())
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                        .putExtra(EXTRA_KEY, record.getKey()),
+                flags);
+    }
+
     @VisibleForTesting
     @GuardedBy("mNotificationLock")
     void scheduleTimeoutLocked(NotificationRecord record) {
         if (record.getNotification().getTimeoutAfter() > 0) {
-            final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                    REQUEST_CODE_TIMEOUT,
-                    new Intent(ACTION_NOTIFICATION_TIMEOUT)
-                            .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
-                            .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
-                                    .appendPath(record.getKey()).build())
-                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                            .putExtra(EXTRA_KEY, record.getKey()),
-                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+            final PendingIntent pi = getNotificationTimeoutPendingIntent(
+                    record, PendingIntent.FLAG_UPDATE_CURRENT);
             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
         }
@@ -8864,6 +8871,16 @@
 
     @VisibleForTesting
     @GuardedBy("mNotificationLock")
+    void cancelScheduledTimeoutLocked(NotificationRecord record) {
+        final PendingIntent pi = getNotificationTimeoutPendingIntent(
+                record, PendingIntent.FLAG_CANCEL_CURRENT);
+        if (pi != null) {
+            mAlarmManager.cancel(pi);
+        }
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mNotificationLock")
     /**
      * Determine whether this notification should attempt to make noise, vibrate, or flash the LED
      * @return buzzBeepBlink - bitfield (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)
@@ -9894,21 +9911,7 @@
             int rank, int count, boolean wasPosted, String listenerName,
             @ElapsedRealtimeLong long cancellationElapsedTimeMs) {
         final String canceledKey = r.getKey();
-
-        // Get pending intent used to create alarm, use FLAG_NO_CREATE if PendingIntent
-        // does not already exist, then null will be returned.
-        final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                REQUEST_CODE_TIMEOUT,
-                new Intent(ACTION_NOTIFICATION_TIMEOUT)
-                        .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
-                                .appendPath(r.getKey()).build())
-                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
-                PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
-
-        // Cancel alarm corresponding to pi.
-        if (pi != null) {
-            mAlarmManager.cancel(pi);
-        }
+        cancelScheduledTimeoutLocked(r);
 
         // Record caller.
         recordCallerLocked(r);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1786ac5..6ab4b99 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -308,31 +308,43 @@
         return light;
     }
 
+    private VibrationEffect getVibrationForChannel(
+            NotificationChannel channel, VibratorHelper helper, boolean insistent) {
+        if (!channel.shouldVibrate()) {
+            return null;
+        }
+
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            final VibrationEffect vibration = channel.getVibrationEffect();
+            if (vibration != null && helper.areEffectComponentsSupported(vibration)) {
+                // Adjust the vibration's repeat behavior based on the `insistent` property.
+                return vibration.applyRepeatingIndefinitely(insistent, /* loopDelayMs= */ 0);
+            }
+        }
+
+        final long[] vibrationPattern = channel.getVibrationPattern();
+        if (vibrationPattern == null) {
+            return helper.createDefaultVibration(insistent);
+        }
+        return helper.createWaveformVibration(vibrationPattern, insistent);
+    }
+
     private VibrationEffect calculateVibration() {
         VibratorHelper helper = new VibratorHelper(mContext);
         final Notification notification = getSbn().getNotification();
         final boolean insistent = (notification.flags & Notification.FLAG_INSISTENT) != 0;
-        VibrationEffect defaultVibration = helper.createDefaultVibration(insistent);
-        VibrationEffect vibration;
-        if (getChannel().shouldVibrate()) {
-            vibration = getChannel().getVibrationPattern() == null
-                    ? defaultVibration
-                    : helper.createWaveformVibration(getChannel().getVibrationPattern(), insistent);
-        } else {
-            vibration = null;
-        }
+
         if (mPreChannelsNotification
                 && (getChannel().getUserLockedFields()
                 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
             final boolean useDefaultVibrate =
                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
             if (useDefaultVibrate) {
-                vibration = defaultVibration;
-            } else {
-                vibration = helper.createWaveformVibration(notification.vibrate, insistent);
+                return helper.createDefaultVibration(insistent);
             }
+            return  helper.createWaveformVibration(notification.vibrate, insistent);
         }
-        return vibration;
+        return getVibrationForChannel(getChannel(), helper, insistent);
     }
 
     private @NonNull AudioAttributes calculateAttributes() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 1bafcfe..4f3cdbc 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1212,6 +1212,8 @@
         if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0
                 && (!Arrays.equals(oldParent.getVibrationPattern(),
                 updatedParent.getVibrationPattern())
+                || !Objects.equals(
+                        oldParent.getVibrationEffect(), updatedParent.getVibrationEffect())
                 || oldParent.shouldVibrate() != updatedParent.shouldVibrate())) {
             // enableVibration must be 2nd because setVibrationPattern may toggle it.
             conversation.setVibrationPattern(updatedParent.getVibrationPattern());
@@ -1972,6 +1974,7 @@
             update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
         }
         if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
+                || !Objects.equals(original.getVibrationEffect(), update.getVibrationEffect())
                 || original.shouldVibrate() != update.shouldVibrate()) {
             update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
         }
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index 7204d05..8a0e595 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -193,6 +193,11 @@
         return createWaveformVibration(mDefaultPattern, insistent);
     }
 
+    /** Returns if a given vibration can be played by the vibrator that does notification buzz. */
+    public boolean areEffectComponentsSupported(VibrationEffect effect) {
+        return mVibrator.areVibrationFeaturesSupported(effect);
+    }
+
     @Nullable
     private static float[] getFloatArray(Resources resources, int resId) {
         TypedArray array = resources.obtainTypedArray(resId);
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 339b1e7..0bbad85 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -28,6 +28,7 @@
 import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
+import static android.content.pm.PackageManager.DELETE_ALL_USERS;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
@@ -182,62 +183,76 @@
         return Flags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false);
     }
 
+    @VisibleForTesting
     void requestArchive(
             @NonNull String packageName,
             @NonNull String callerPackageName,
             @NonNull IntentSender intentSender,
             @NonNull UserHandle userHandle) {
+        requestArchive(packageName, callerPackageName, /*flags=*/ 0, intentSender, userHandle);
+    }
+
+    void requestArchive(
+            @NonNull String packageName,
+            @NonNull String callerPackageName,
+            int flags,
+            @NonNull IntentSender intentSender,
+            @NonNull UserHandle userHandle) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(callerPackageName);
         Objects.requireNonNull(intentSender);
         Objects.requireNonNull(userHandle);
 
         Computer snapshot = mPm.snapshotComputer();
-        int userId = userHandle.getIdentifier();
+        int binderUserId = userHandle.getIdentifier();
         int binderUid = Binder.getCallingUid();
         int binderPid = Binder.getCallingPid();
         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(binderUid)) {
-            verifyCaller(snapshot.getPackageUid(callerPackageName, 0, userId), binderUid);
+            verifyCaller(snapshot.getPackageUid(callerPackageName, 0, binderUserId), binderUid);
         }
-        snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
-                "archiveApp");
+
+        final boolean deleteAllUsers = (flags & PackageManager.DELETE_ALL_USERS) != 0;
+        final int[] users = deleteAllUsers ? mPm.mInjector.getUserManagerInternal().getUserIds()
+                : new int[]{binderUserId};
+        for (int userId : users) {
+            snapshot.enforceCrossUserPermission(binderUid, userId,
+                    /*requireFullPermission=*/ true, /*checkShell=*/ true,
+                    "archiveApp");
+        }
         verifyUninstallPermissions();
 
-        CompletableFuture<ArchiveState> archiveStateFuture;
+        CompletableFuture<Void>[] archiveStateStored = new CompletableFuture[users.length];
         try {
-            archiveStateFuture = createArchiveState(packageName, userId);
+            for (int i = 0, size = users.length; i < size; ++i) {
+                archiveStateStored[i] = createAndStoreArchiveState(packageName, users[i]);
+            }
         } catch (PackageManager.NameNotFoundException e) {
             Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
                     packageName, e.getMessage()));
             throw new ParcelableException(e);
         }
 
-        archiveStateFuture
-                .thenAccept(
-                        archiveState -> {
-                            // TODO(b/282952870) Should be reverted if uninstall fails/cancels
-                            try {
-                                storeArchiveState(packageName, archiveState, userId);
-                            } catch (PackageManager.NameNotFoundException e) {
-                                sendFailureStatus(intentSender, packageName, e.getMessage());
-                                return;
-                            }
+        final int deleteFlags = DELETE_ARCHIVE | DELETE_KEEP_DATA
+                | (deleteAllUsers ? DELETE_ALL_USERS : 0);
 
-                            mPm.mInstallerService.uninstall(
-                                    new VersionedPackage(packageName,
-                                            PackageManager.VERSION_CODE_HIGHEST),
-                                    callerPackageName,
-                                    DELETE_ARCHIVE | DELETE_KEEP_DATA,
-                                    intentSender,
-                                    userId,
-                                    binderUid,
-                                    binderPid);
-                        })
-                .exceptionally(
-                        e -> {
-                            sendFailureStatus(intentSender, packageName, e.getMessage());
-                            return null;
-                        });
+        CompletableFuture.allOf(archiveStateStored).thenAccept(ignored ->
+                mPm.mInstallerService.uninstall(
+                        new VersionedPackage(packageName,
+                                PackageManager.VERSION_CODE_HIGHEST),
+                        callerPackageName,
+                        deleteFlags,
+                        intentSender,
+                        binderUserId,
+                        binderUid,
+                        binderPid)
+        ).exceptionally(
+                e -> {
+                    Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
+                            packageName, e.getMessage()));
+                    sendFailureStatus(intentSender, packageName, e.getMessage());
+                    return null;
+                }
+        );
     }
 
     /**
@@ -384,7 +399,7 @@
     }
 
     /** Creates archived state for the package and user. */
-    private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
+    private CompletableFuture<Void> createAndStoreArchiveState(String packageName, int userId)
             throws PackageManager.NameNotFoundException {
         Computer snapshot = mPm.snapshotComputer();
         PackageStateInternal ps = getPackageState(packageName, snapshot,
@@ -399,17 +414,18 @@
 
         List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(),
                 userId);
-        final CompletableFuture<ArchiveState> archiveState = new CompletableFuture<>();
+        final CompletableFuture<Void> archiveStateStored = new CompletableFuture<>();
         mPm.mHandler.post(() -> {
             try {
-                archiveState.complete(
-                        createArchiveStateInternal(packageName, userId, mainActivities,
-                                installerInfo.loadLabel(mContext.getPackageManager()).toString()));
-            } catch (IOException e) {
-                archiveState.completeExceptionally(e);
+                var archiveState = createArchiveStateInternal(packageName, userId, mainActivities,
+                        installerInfo.loadLabel(mContext.getPackageManager()).toString());
+                storeArchiveState(packageName, archiveState, userId);
+                archiveStateStored.complete(null);
+            } catch (IOException | PackageManager.NameNotFoundException e) {
+                archiveStateStored.completeExceptionally(e);
             }
         });
-        return archiveState;
+        return archiveStateStored;
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c6d448d..abea56b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1672,9 +1672,11 @@
     public void requestArchive(
             @NonNull String packageName,
             @NonNull String callerPackageName,
+            int flags,
             @NonNull IntentSender intentSender,
             @NonNull UserHandle userHandle) {
-        mPackageArchiver.requestArchive(packageName, callerPackageName, intentSender, userHandle);
+        mPackageArchiver.requestArchive(packageName, callerPackageName, flags, intentSender,
+                userHandle);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 5c9c8c6..81f9d1b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4651,6 +4651,7 @@
 
     private int runArchive() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
+        int flags = 0;
         int userId = UserHandle.USER_ALL;
 
         String opt;
@@ -4678,13 +4679,16 @@
             return 1;
         }
 
+        if (userId == UserHandle.USER_ALL) {
+            flags |= PackageManager.DELETE_ALL_USERS;
+        }
         final int translatedUserId =
                 translateUserId(userId, UserHandle.USER_SYSTEM, "runArchive");
         final LocalIntentReceiver receiver = new LocalIntentReceiver();
 
         try {
             mInterface.getPackageInstaller().requestArchive(packageName,
-                    /* callerPackageName= */ "", receiver.getIntentSender(),
+                    /* callerPackageName= */ "", flags, receiver.getIntentSender(),
                     new UserHandle(translatedUserId));
         } catch (Exception e) {
             pw.println("Failure [" + e.getMessage() + "]");
diff --git a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
index 7754944..07cc775 100644
--- a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
@@ -17,11 +17,14 @@
 package com.android.server.policy;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 
 import com.android.server.devicestate.DeviceStatePolicy;
 import com.android.server.devicestate.DeviceStateProvider;
 
+import java.io.PrintWriter;
+
 /**
  * Default empty implementation of {@link DeviceStatePolicy}.
  *
@@ -43,4 +46,9 @@
     public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
         onComplete.run();
     }
+
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        mProvider.dump(writer, args);
+    }
 }
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 3644054..afcf5a0 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -58,6 +58,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.PrintWriter;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -503,6 +504,24 @@
         // Do nothing.
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("DeviceStateProviderImpl");
+
+        synchronized (mLock) {
+            writer.println("  mLastReportedState = " + mLastReportedState);
+            writer.println("  mPowerSaveModeEnabled = " + mPowerSaveModeEnabled);
+            writer.println("  mThermalStatus = " + mThermalStatus);
+            writer.println("  mIsLidOpen = " + mIsLidOpen);
+            writer.println("  Sensor values:");
+
+            for (Sensor sensor : mLatestSensorEvent.keySet()) {
+                SensorEvent sensorEvent = mLatestSensorEvent.get(sensor);
+                writer.println("   - " + toSensorValueString(sensor, sensorEvent));
+            }
+        }
+    }
+
     /**
      * Implementation of {@link BooleanSupplier} that returns {@code true} if the expected lid
      * switch open state matches {@link #mIsLidOpen}.
@@ -669,14 +688,16 @@
         Slog.i(TAG, "Sensor values:");
         for (Sensor sensor : mLatestSensorEvent.keySet()) {
             SensorEvent sensorEvent = mLatestSensorEvent.get(sensor);
-            if (sensorEvent != null) {
-                Slog.i(TAG, sensor.getName() + ": " + Arrays.toString(sensorEvent.values));
-            } else {
-                Slog.i(TAG, sensor.getName() + ": null");
-            }
+            Slog.i(TAG, toSensorValueString(sensor, sensorEvent));
         }
     }
 
+    private String toSensorValueString(Sensor sensor, @Nullable SensorEvent event) {
+        String sensorString = sensor == null ? "null" : sensor.getName();
+        String eventValues = event == null ? "null" : Arrays.toString(event.values);
+        return sensorString + " : " + eventValues;
+    }
+
     /**
      * Tries to parse the provided file into a {@link DeviceStateConfig} object. Returns
      * {@code null} if the file could not be successfully parsed.
diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
index 906da2f..b05a421 100644
--- a/services/core/java/com/android/server/policy/TalkbackShortcutController.java
+++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
@@ -59,6 +59,10 @@
         final Set<ComponentName> enabledServices =
                 AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId);
         ComponentName componentName = getTalkbackComponent();
+        if (componentName == null) {
+            return false;
+        }
+
         boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName);
 
         if (isTalkBackShortcutGestureEnabled()) {
@@ -67,7 +71,7 @@
                     isTalkbackAlreadyEnabled);
 
             // log stem triple press telemetry if it's a talkback enabled event.
-            if (componentName != null && isTalkbackAlreadyEnabled) {
+            if (isTalkbackAlreadyEnabled) {
                 logStemTriplePressAccessibilityTelemetry(componentName);
             }
         }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 089a886..e0a8226 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2095,17 +2095,17 @@
         }
 
         @Override
-        public void startPlayback(IBinder sessionToken, int userId) {
+        public void resumePlayback(IBinder sessionToken, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "stopPlayback");
+                    userId, "resumePlayback");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback();
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).resumePlayback();
                     } catch (RemoteException | SessionNotFoundException e) {
-                        Slog.e(TAG, "error in startPlayback()", e);
+                        Slog.e(TAG, "error in resumePlayback()", e);
                     }
                 }
             } finally {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 27cc2d6..7fc61e1 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -66,7 +66,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.WeakHashMap;
-import java.util.function.Consumer;
 
 /**
  * Stores the TaskOrganizers associated with a given windowing mode and
@@ -102,11 +101,8 @@
      */
     private static class TaskOrganizerCallbacks {
         final ITaskOrganizer mTaskOrganizer;
-        final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
 
-        TaskOrganizerCallbacks(ITaskOrganizer taskOrg,
-                Consumer<Runnable> deferTaskOrgCallbacksConsumer) {
-            mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer;
+        TaskOrganizerCallbacks(ITaskOrganizer taskOrg) {
             mTaskOrganizer = taskOrg;
         }
 
@@ -335,11 +331,7 @@
         private final int mUid;
 
         TaskOrganizerState(ITaskOrganizer organizer, int uid) {
-            final Consumer<Runnable> deferTaskOrgCallbacksConsumer =
-                    mDeferTaskOrgCallbacksConsumer != null
-                            ? mDeferTaskOrgCallbacksConsumer
-                            : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;
-            mOrganizer = new TaskOrganizerCallbacks(organizer, deferTaskOrgCallbacksConsumer);
+            mOrganizer = new TaskOrganizerCallbacks(organizer);
             mDeathRecipient = new DeathRecipient(organizer);
             mPendingEventsQueue = new TaskOrganizerPendingEventsQueue(this);
             try {
@@ -484,8 +476,6 @@
     // Set of organized tasks (by taskId) that dispatch back pressed to their organizers
     private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet<>();
 
-    private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
-
     TaskOrganizerController(ActivityTaskManagerService atm) {
         mService = atm;
         mGlobalLock = atm.mGlobalLock;
@@ -502,15 +492,6 @@
     }
 
     /**
-     * Specifies the consumer to run to defer the task org callbacks. Can be overridden while
-     * testing to allow the callbacks to be sent synchronously.
-     */
-    @VisibleForTesting
-    public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) {
-        mDeferTaskOrgCallbacksConsumer = consumer;
-    }
-
-    /**
      * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
      */
     @Override
diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
index 1688a1a..817901f 100644
--- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -32,7 +32,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Size;
@@ -159,10 +158,6 @@
 
     private InputWindowHandle[] mLastWindowHandles;
 
-    private final Object mIgnoredWindowTokensLock = new Object();
-
-    private final ArraySet<IBinder> mIgnoredWindowTokens = new ArraySet<>();
-
     private void startHandlerThreadIfNeeded() {
         synchronized (mHandlerThreadLock) {
             if (mHandler == null) {
@@ -173,18 +168,6 @@
         }
     }
 
-    void addIgnoredWindowTokens(IBinder token) {
-        synchronized (mIgnoredWindowTokensLock) {
-            mIgnoredWindowTokens.add(token);
-        }
-    }
-
-    void removeIgnoredWindowTokens(IBinder token) {
-        synchronized (mIgnoredWindowTokensLock) {
-            mIgnoredWindowTokens.remove(token);
-        }
-    }
-
     void registerListener(IBinder window, ITrustedPresentationListener listener,
             TrustedPresentationThresholds thresholds, int id) {
         startHandlerThreadIfNeeded();
@@ -271,12 +254,8 @@
 
         ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates =
                 new ArrayMap<>();
-        ArraySet<IBinder> ignoredWindowTokens;
-        synchronized (mIgnoredWindowTokensLock) {
-            ignoredWindowTokens = new ArraySet<>(mIgnoredWindowTokens);
-        }
         for (var windowHandle : mLastWindowHandles) {
-            if (ignoredWindowTokens.contains(windowHandle.getWindowToken())) {
+            if (!windowHandle.canOccludePresentation) {
                 ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name);
                 continue;
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 24e50c5..f5806c0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1148,10 +1148,6 @@
             parentWindow.addChild(this, sWindowSubLayerComparator);
         }
 
-        if (token.mRoundedCornerOverlay) {
-            mWmService.mTrustedPresentationListenerController.addIgnoredWindowTokens(
-                    getWindowToken());
-        }
     }
 
     @Override
@@ -1163,6 +1159,9 @@
         if (secureWindowState()) {
             getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
         }
+        // All apps should be considered as occluding when computing TrustedPresentation Thresholds.
+        final boolean canOccludePresentation = !mSession.mCanAddInternalSystemWindow;
+        getPendingTransaction().setCanOccludePresentation(mSurfaceControl, canOccludePresentation);
     }
 
     void updateTrustedOverlay() {
@@ -2344,9 +2343,6 @@
         mSession.onWindowRemoved(this);
         mWmService.postWindowRemoveCleanupLocked(this);
 
-        mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens(
-                getWindowToken());
-
         consumeInsetsChange();
     }
 
diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp
index e892d23..49a3ad3 100644
--- a/services/core/jni/com_android_server_am_OomConnection.cpp
+++ b/services/core/jni/com_android_server_am_OomConnection.cpp
@@ -22,13 +22,15 @@
 
 namespace android {
 
+using namespace ::android::bpf::memevents;
+
 // Used to cache the results of the JNI name lookup
 static struct {
     jclass clazz;
     jmethodID ctor;
 } sOomKillRecordInfo;
 
-static memevents::MemEventListener memevent_listener;
+static MemEventListener memevent_listener(MemEventClient::AMS);
 
 /**
  * Initialize listening and waiting for new out-of-memory (OOM) events to occur.
@@ -42,25 +44,20 @@
  * @throws java.lang.RuntimeException
  */
 static jobjectArray android_server_am_OomConnection_waitOom(JNIEnv* env, jobject) {
-    const memevents::MemEvent oom_event = memevents::MemEvent::OOM_KILL;
-    if (!memevent_listener.registerEvent(oom_event)) {
+    if (!memevent_listener.registerEvent(MEM_EVENT_OOM_KILL)) {
         memevent_listener.deregisterAllEvents();
         jniThrowRuntimeException(env, "listener failed to register to OOM events");
         return nullptr;
     }
 
-    memevents::MemEvent event_received;
-    do {
-        event_received = memevent_listener.listen();
-        if (event_received == memevents::MemEvent::ERROR) {
-            memevent_listener.deregisterAllEvents();
-            jniThrowRuntimeException(env, "listener received error event");
-            return nullptr;
-        }
-    } while (event_received != oom_event);
+    if (!memevent_listener.listen()) {
+        memevent_listener.deregisterAllEvents();
+        jniThrowRuntimeException(env, "listener failed waiting for OOM event");
+        return nullptr;
+    }
 
-    std::vector<memevents::OomKill> oom_events;
-    if (!memevent_listener.getOomEvents(oom_events)) {
+    std::vector<mem_event_t> oom_events;
+    if (!memevent_listener.getMemEvents(oom_events)) {
         memevent_listener.deregisterAllEvents();
         jniThrowRuntimeException(env, "Failed to get OOM events");
         return nullptr;
@@ -75,15 +72,23 @@
     }
 
     for (int i = 0; i < oom_events.size(); i++) {
-        const memevents::OomKill oom_event = oom_events[i];
-        jstring process_name = env->NewStringUTF(oom_event.process_name);
+        const mem_event_t mem_event = oom_events[i];
+        if (mem_event.type != MEM_EVENT_OOM_KILL) {
+            memevent_listener.deregisterAllEvents();
+            jniThrowRuntimeException(env, "Received invalid memory event");
+            return java_oom_array;
+        }
+
+        const auto oom_kill = mem_event.event_data.oom_kill;
+
+        jstring process_name = env->NewStringUTF(oom_kill.process_name);
         if (process_name == NULL) {
             memevent_listener.deregisterAllEvents();
             jniThrowRuntimeException(env, "Failed creating java string for process name");
         }
         jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor,
-                                               oom_event.timestamp_ms, oom_event.pid, oom_event.uid,
-                                               process_name, oom_event.oom_score_adj);
+                                               oom_kill.timestamp_ms, oom_kill.pid, oom_kill.uid,
+                                               process_name, oom_kill.oom_score_adj);
         if (java_oom_kill == NULL) {
             memevent_listener.deregisterAllEvents();
             jniThrowRuntimeException(env, "Failed to create OomKillRecord object");
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 4203576..e8b32cd 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -178,7 +178,7 @@
         // intents
         return PendingIntent.getActivityAsUser(
                 mContext, /*requestCode=*/0, intent,
-                PendingIntent.FLAG_IMMUTABLE, /*options=*/null,
+                PendingIntent.FLAG_MUTABLE, /*options=*/null,
                 UserHandle.of(mUserId));
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 7e709fe..1a9a0e6 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -116,7 +116,7 @@
                         mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
                         PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                 Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                providerDataList,
+                /*providerDataList=*/ null,
                 /*isRequestForAllOptions=*/ true);
 
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index fcaef9f..ca23d62 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -30,6 +30,7 @@
 import android.credentials.selection.Entry;
 import android.credentials.selection.GetCredentialProviderData;
 import android.credentials.selection.ProviderPendingIntentResponse;
+import android.os.Bundle;
 import android.os.ICancellationSignal;
 import android.service.autofill.Flags;
 import android.service.credentials.Action;
@@ -76,6 +77,9 @@
     @NonNull
     private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
 
+    @NonNull
+    private final Map<String, AutofillId> mCredentialEntryKeyToAutofilLIdMap;
+
 
     /** The complete request to be used in the second round. */
     private final android.credentials.GetCredentialRequest mCompleteRequest;
@@ -245,6 +249,7 @@
         mBeginGetOptionToCredentialOptionMap = new HashMap<>(beginGetOptionToCredentialOptionMap);
         mProviderResponseDataHandler = new ProviderResponseDataHandler(
                 ComponentName.unflattenFromString(hybridService));
+        mCredentialEntryKeyToAutofilLIdMap = new HashMap<>();
     }
 
     /** Called when the provider response has been updated by an external source. */
@@ -298,7 +303,7 @@
                     invokeCallbackOnInternalInvalidState();
                     return;
                 }
-                onCredentialEntrySelected(providerPendingIntentResponse);
+                onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
                 break;
             case ACTION_ENTRY_KEY:
                 Action actionEntry = mProviderResponseDataHandler.getActionEntry(entryKey);
@@ -307,7 +312,7 @@
                     invokeCallbackOnInternalInvalidState();
                     return;
                 }
-                onActionEntrySelected(providerPendingIntentResponse);
+                onActionEntrySelected(providerPendingIntentResponse, entryKey);
                 break;
             case AUTHENTICATION_ACTION_ENTRY_KEY:
                 Action authenticationEntry = mProviderResponseDataHandler
@@ -337,7 +342,7 @@
                 break;
             case REMOTE_ENTRY_KEY:
                 if (mProviderResponseDataHandler.getRemoteEntry(entryKey) != null) {
-                    onRemoteEntrySelected(providerPendingIntentResponse);
+                    onRemoteEntrySelected(providerPendingIntentResponse, entryKey);
                 } else {
                     Slog.i(TAG, "Unexpected remote entry key");
                     invokeCallbackOnInternalInvalidState();
@@ -376,7 +381,7 @@
         return null;
     }
 
-    private Intent setUpFillInIntentWithFinalRequest(@NonNull String id) {
+    private Intent setUpFillInIntentWithFinalRequest(@NonNull String id, String entryKey) {
         // TODO: Determine if we should skip this entry if entry id is not set, or is set
         // but does not resolve to a valid option. For now, not skipping it because
         // it may be possible that the provider adds their own extras and expects to receive
@@ -392,6 +397,7 @@
                 .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
         if (autofillId != null && Flags.autofillCredmanIntegration()) {
             intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
+            mCredentialEntryKeyToAutofilLIdMap.put(entryKey, autofillId);
         }
         return intent.putExtra(
                 CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
@@ -408,12 +414,13 @@
     }
 
     private void onRemoteEntrySelected(
-            ProviderPendingIntentResponse providerPendingIntentResponse) {
-        onCredentialEntrySelected(providerPendingIntentResponse);
+            ProviderPendingIntentResponse providerPendingIntentResponse, String entryKey) {
+        onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
     }
 
     private void onCredentialEntrySelected(
-            ProviderPendingIntentResponse providerPendingIntentResponse) {
+            ProviderPendingIntentResponse providerPendingIntentResponse,
+            String entryKey) {
         if (providerPendingIntentResponse == null) {
             invokeCallbackOnInternalInvalidState();
             return;
@@ -430,7 +437,18 @@
         GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
                 .extractGetCredentialResponse(
                         providerPendingIntentResponse.getResultData());
-        if (getCredentialResponse != null) {
+        if (getCredentialResponse != null && getCredentialResponse.getCredential() != null) {
+            Bundle credentialData = getCredentialResponse.getCredential().getData();
+            AutofillId autofillId = mCredentialEntryKeyToAutofilLIdMap.get(entryKey);
+            if (Flags.autofillCredmanIntegration()
+                    && entryKey != null && autofillId != null && credentialData != null
+            ) {
+                Slog.d(TAG, "Adding autofillId to credential response: " + autofillId);
+                credentialData.putParcelable(
+                        CredentialProviderService.EXTRA_AUTOFILL_ID,
+                        mCredentialEntryKeyToAutofilLIdMap.get(entryKey)
+                );
+            }
             mCallbacks.onFinalResponseReceived(mComponentName,
                     getCredentialResponse);
             return;
@@ -514,9 +532,9 @@
 
     /** Returns true if either an exception or a response is found. */
     private void onActionEntrySelected(ProviderPendingIntentResponse
-            providerPendingIntentResponse) {
+            providerPendingIntentResponse, String entryKey) {
         Slog.i(TAG, "onActionEntrySelected");
-        onCredentialEntrySelected(providerPendingIntentResponse);
+        onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
     }
 
 
@@ -614,7 +632,7 @@
             Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
                     id, credentialEntry.getSlice(),
                     setUpFillInIntentWithFinalRequest(credentialEntry
-                            .getBeginGetCredentialOptionId()));
+                            .getBeginGetCredentialOptionId(), id));
             mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
             mCredentialEntryTypes.add(credentialEntry.getType());
         }
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
index d5a3cff..82d5247 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
@@ -35,6 +35,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.util.ArraySet;
+import android.util.Dumpable;
 import android.view.Display;
 import android.view.Surface;
 
@@ -43,7 +44,9 @@
 import com.android.server.policy.BookStylePreferredScreenCalculator.StateTransition;
 import com.android.server.policy.BookStyleClosedStatePredicate.ConditionSensorListener.SensorSubscription;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Predicate;
@@ -56,7 +59,7 @@
  * See {@link BookStyleStateTransitions} for detailed description of the default behavior.
  */
 public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceStateProvider>,
-        DisplayManager.DisplayListener {
+        DisplayManager.DisplayListener, Dumpable {
 
     private final BookStylePreferredScreenCalculator mClosedStateCalculator;
     private final Handler mHandler = new Handler();
@@ -154,6 +157,14 @@
 
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("  " + getDumpableName());
+
+        mPostureEstimator.dump(writer, args);
+        mClosedStateCalculator.dump(writer, args);
+    }
+
     public interface ClosedStateUpdatesListener {
         void onClosedStateUpdated();
     }
@@ -161,7 +172,7 @@
     /**
      * Estimates if the device is going to enter wedge/tent mode based on the sensor data
      */
-    private static class PostureEstimator implements SensorEventListener {
+    private static class PostureEstimator implements SensorEventListener, Dumpable {
 
 
         private static final int FLAT_INCLINATION_THRESHOLD_DEGREES = 8;
@@ -356,6 +367,23 @@
             mDeviceClosed = deviceClosed;
             mConditionedSensorListener.updateListeningState();
         }
+
+        @Override
+        public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+            writer.println("    " + getDumpableName());
+            writer.println("      isLikelyTentOrWedgeMode = " + isLikelyTentOrWedgeMode());
+            writer.println("      mScreenTurnedOn = " + mScreenTurnedOn);
+            writer.println("      mLastScreenRotation = " + mLastScreenRotation);
+            writer.println("      mDeviceClosed = " + mDeviceClosed);
+            writer.println("      mLeftGravityVector = " + Arrays.toString(mLeftGravityVector));
+            writer.println("      mRightGravityVector = " + Arrays.toString(mRightGravityVector));
+        }
+
+        @NonNull
+        @Override
+        public String getDumpableName() {
+            return "PostureEstimator";
+        }
     }
 
     /**
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
index ad938af..8b22718 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
@@ -38,6 +38,7 @@
 import com.android.server.policy.feature.flags.FeatureFlags;
 import com.android.server.policy.feature.flags.FeatureFlagsImpl;
 
+import java.io.PrintWriter;
 import java.util.function.Predicate;
 
 /**
@@ -182,4 +183,9 @@
     public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
         onComplete.run();
     }
+
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        mProvider.dump(writer, args);
+    }
 }
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java
index 8977422..69d793e 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java
@@ -16,8 +16,14 @@
 
 package com.android.server.policy;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
+import android.util.Dumpable;
+import android.util.Slog;
 
+
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.Objects;
 
@@ -31,7 +37,12 @@
  *
  * See {@link BookStyleStateTransitions} for detailed description of the default behavior.
  */
-public class BookStylePreferredScreenCalculator {
+public class BookStylePreferredScreenCalculator implements Dumpable {
+
+    private static final String TAG = "BookStylePreferredScreenCalculator";
+
+    // TODO(b/322137477): disable by default on all builds after flag clean-up
+    private static final boolean DEBUG = Build.IS_USERDEBUG || Build.IS_ENG;
 
     /**
      * When calculating the new state we will re-calculate it until it settles down. We re-calculate
@@ -77,6 +88,9 @@
      */
     public PreferredScreen calculatePreferredScreen(HingeAngle angle, boolean likelyTentOrWedge,
             boolean likelyReverseWedge) {
+
+        final State oldState = mState;
+
         int attempts = 0;
         State newState = calculateNewState(mState, angle, likelyTentOrWedge, likelyReverseWedge);
         while (attempts < MAX_STATE_CHANGES && !Objects.equals(mState, newState)) {
@@ -92,7 +106,6 @@
                             + ", likelyReverseWedge = " + likelyReverseWedge);
         }
 
-        final State oldState = mState;
         mState = newState;
 
         if (mState.mPreferredScreen == PreferredScreen.INVALID) {
@@ -103,6 +116,13 @@
                             + oldState);
         }
 
+        if (DEBUG && !Objects.equals(oldState, newState)) {
+            Slog.d(TAG, "Moving to state " + mState
+                    + " (hingeAngle = " + angle
+                    + ", likelyTentOrWedge = " + likelyTentOrWedge
+                    + ", likelyReverseWedge = " + likelyReverseWedge + ")");
+        }
+
         return mState.mPreferredScreen;
     }
 
@@ -129,6 +149,18 @@
                         + likelyReverseWedge);
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("    " + getDumpableName());
+        writer.println("      mState = " + mState);
+    }
+
+    @NonNull
+    @Override
+    public String getDumpableName() {
+        return TAG;
+    }
+
     /**
      * The angle between two halves of the foldable device in degrees. The angle is '0' when
      * the device is fully closed and '180' when the device is fully open and flat.
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index ba72977..021a667 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -39,6 +39,7 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.Trace;
+import android.util.Dumpable;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -52,6 +53,7 @@
 import com.android.server.policy.feature.flags.FeatureFlags;
 import com.android.server.policy.feature.flags.FeatureFlagsImpl;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -87,6 +89,8 @@
     // the conditions needed for availability.
     private final SparseArray<BooleanSupplier> mStateAvailabilityConditions = new SparseArray<>();
 
+    private final DeviceStateConfiguration[] mConfigurations;
+
     @GuardedBy("mLock")
     private final SparseBooleanArray mExternalDisplaysConnected = new SparseBooleanArray();
 
@@ -142,6 +146,7 @@
         mHingeAngleSensor = hingeAngleSensor;
         mHallSensor = hallSensor;
         mDisplayManager = displayManager;
+        mConfigurations = deviceStateConfigurations;
         mIsDualDisplayBlockingEnabled = featureFlags.enableDualDisplayBlocking();
 
         sensorManager.registerListener(this, mHingeAngleSensor, SENSOR_DELAY_FASTEST);
@@ -350,16 +355,20 @@
     @GuardedBy("mLock")
     private void dumpSensorValues() {
         Slog.i(TAG, "Sensor values:");
-        dumpSensorValues("Hall Sensor", mHallSensor, mLastHallSensorEvent);
-        dumpSensorValues("Hinge Angle Sensor", mHingeAngleSensor, mLastHingeAngleSensorEvent);
+        dumpSensorValues(mHallSensor, mLastHallSensorEvent);
+        dumpSensorValues(mHingeAngleSensor, mLastHingeAngleSensorEvent);
         Slog.i(TAG, "isScreenOn: " + isScreenOn());
     }
 
     @GuardedBy("mLock")
-    private void dumpSensorValues(String sensorType, Sensor sensor, @Nullable SensorEvent event) {
+    private void dumpSensorValues(Sensor sensor, @Nullable SensorEvent event) {
+        Slog.i(TAG, toSensorValueString(sensor, event));
+    }
+
+    private String toSensorValueString(Sensor sensor, @Nullable SensorEvent event) {
         String sensorString = sensor == null ? "null" : sensor.getName();
         String eventValues = event == null ? "null" : Arrays.toString(event.values);
-        Slog.i(TAG, sensorType + " : " + sensorString + " : " + eventValues);
+        return sensorString + " : " + eventValues;
     }
 
     @Override
@@ -414,6 +423,34 @@
         }
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("FoldableDeviceStateProvider");
+
+        synchronized (mLock) {
+            writer.println("  mLastReportedState = " + mLastReportedState);
+            writer.println("  mPowerSaveModeEnabled = " + mPowerSaveModeEnabled);
+            writer.println("  mThermalStatus = " + mThermalStatus);
+            writer.println("  mLastHingeAngleSensorEvent = " +
+                    toSensorValueString(mHingeAngleSensor, mLastHingeAngleSensorEvent));
+            writer.println("  mLastHallSensorEvent = " +
+                    toSensorValueString(mHallSensor, mLastHallSensorEvent));
+        }
+
+        writer.println();
+        writer.println("  Predicates:");
+
+        for (int i = 0; i < mConfigurations.length; i++) {
+            final DeviceStateConfiguration configuration = mConfigurations[i];
+            final Predicate<FoldableDeviceStateProvider> predicate =
+                    configuration.mActiveStatePredicate;
+
+            if (predicate instanceof Dumpable dumpable) {
+                dumpable.dump(writer, /* args= */ null);
+            }
+        }
+    }
+
     /**
      * Configuration for a single device state, contains information about the state like
      * identifier, name, flags and a predicate that should return true if the state should
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index fd21a32..2359422c9 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -22,6 +22,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.SignedPackage;
+import android.content.pm.SignedPackageParcel;
 import android.os.Binder;
 import android.os.ISystemConfig;
 import android.util.ArrayMap;
@@ -119,6 +121,26 @@
                             pmi.canQueryPackage(Binder.getCallingUid(), preventUserDisablePackage))
                     .collect(toList());
         }
+
+        @Override
+        public List<SignedPackageParcel> getEnhancedConfirmationTrustedPackages() {
+            getContext().enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES,
+                    "Caller must hold " + Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES);
+
+            return SystemConfig.getInstance().getEnhancedConfirmationTrustedPackages().stream()
+                    .map(SignedPackage::getData).toList();
+        }
+
+        @Override
+        public List<SignedPackageParcel> getEnhancedConfirmationTrustedInstallers() {
+            getContext().enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES,
+                    "Caller must hold " + Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES);
+
+            return SystemConfig.getInstance().getEnhancedConfirmationTrustedInstallers().stream()
+                    .map(SignedPackage::getData).toList();
+        }
     };
 
     public SystemConfigService(Context context) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2b8bcc7..b79d20a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -269,6 +269,8 @@
             "com.android.server.backup.BackupManagerService$Lifecycle";
     private static final String APPWIDGET_SERVICE_CLASS =
             "com.android.server.appwidget.AppWidgetService";
+    private static final String ARC_NETWORK_SERVICE_CLASS =
+            "com.android.server.arc.net.ArcNetworkService";
     private static final String ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS =
             "com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService";
     private static final String ARC_SYSTEM_HEALTH_SERVICE =
@@ -2069,13 +2071,24 @@
             if (context.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_WIFI)) {
                 // Wifi Service must be started first for wifi-related services.
-                t.traceBegin("StartWifi");
-                mSystemServiceManager.startServiceFromJar(
-                        WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
-                t.traceEnd();
-                t.traceBegin("StartWifiScanning");
-                mSystemServiceManager.startServiceFromJar(
-                        WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+                if (!isArc) {
+                    t.traceBegin("StartWifi");
+                    mSystemServiceManager.startServiceFromJar(
+                            WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+                    t.traceEnd();
+                    t.traceBegin("StartWifiScanning");
+                    mSystemServiceManager.startServiceFromJar(
+                            WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+                    t.traceEnd();
+                }
+            }
+
+            // ARC - ArcNetworkService registers the ARC network stack and replaces the
+            // stock WiFi service in both ARC++ container and ARCVM. Always starts the ARC network
+            // stack regardless of whether FEATURE_WIFI is enabled/disabled (b/254755875).
+            if (isArc) {
+                t.traceBegin("StartArcNetworking");
+                mSystemServiceManager.startService(ARC_NETWORK_SERVICE_CLASS);
                 t.traceEnd();
             }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index bf00b75..4535ece 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -270,7 +270,7 @@
         assertThat(value.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)).isEqualTo(PACKAGE);
         assertThat(value.getIntExtra(PackageInstaller.EXTRA_STATUS, 0)).isEqualTo(
                 PackageInstaller.STATUS_FAILURE);
-        assertThat(value.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)).isEqualTo(
+        assertThat(value.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)).contains(
                 String.format("Package %s not found.", PACKAGE));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 7dcfc88..fa39364 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -30,6 +30,7 @@
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertThrows;
 
+import android.annotation.NonNull;
 import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateRequest;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
@@ -52,6 +53,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Optional;
@@ -959,6 +961,10 @@
             }
             onComplete.run();
         }
+
+        @Override
+        public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        }
     }
 
     private static final class TestDeviceStateProvider implements DeviceStateProvider {
@@ -1001,6 +1007,10 @@
         public void setState(int identifier) {
             mListener.onStateChanged(identifier);
         }
+
+        @Override
+        public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        }
     }
 
     private static final class TestDeviceStateManagerCallback extends
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index aca96ad..03cdbbd 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -21,8 +21,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.testng.Assert.expectThrows;
 
+import android.content.pm.Signature;
+import android.content.pm.SignedPackage;
 import android.os.Build;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -68,6 +73,8 @@
 
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
+    @Rule public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mSysConfig = new SystemConfigTestClass();
@@ -696,6 +703,43 @@
         assertThat(mSysConfig.getSystemAppUpdateOwnerPackageName("com.foo")).isNull();
     }
 
+    /**
+     * Tests that SystemConfig::getEnhancedConfirmationTrustedInstallers correctly parses a list of
+     * SignedPackage objects.
+     */
+    @Test
+    @RequiresFlagsEnabled(
+            android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    public void getEnhancedConfirmationTrustedInstallers_returnsTrustedInstallers()
+            throws IOException {
+        String pkgName = "com.example.app";
+        String certificateDigestStr = "E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:"
+                + "8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0";
+
+        byte[] certificateDigest = new Signature(certificateDigestStr).toByteArray();
+        String contents = "<config>"
+                + "<" + "enhanced-confirmation-trusted-installer" + " "
+                + "package=\"" + pkgName + "\""
+                + " sha256-cert-digest=\"" + certificateDigestStr + "\""
+                + "/>"
+                + "</config>";
+
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "enhanced-confirmation.xml", contents);
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        ArraySet<SignedPackage> actualTrustedInstallers =
+                mSysConfig.getEnhancedConfirmationTrustedInstallers();
+
+        assertThat(actualTrustedInstallers.size()).isEqualTo(1);
+        SignedPackage actual = actualTrustedInstallers.stream().findFirst().orElseThrow();
+        SignedPackage expected = new SignedPackage(pkgName, certificateDigest);
+
+        assertThat(actual.getCertificateDigest()).isEqualTo(expected.getCertificateDigest());
+        assertThat(actual.getPkgName()).isEqualTo(expected.getPkgName());
+        assertThat(actual).isEqualTo(expected);
+    }
+
     private void parseSharedLibraries(String contents) throws IOException {
         File folder = createTempSubfolder("permissions_folder");
         createTempFile(folder, "permissions.xml", contents);
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
index b8dcecd..e27bb4c 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
@@ -320,4 +320,10 @@
         mWindowInfosReportedListeners.add(listener);
         return this;
     }
+
+    @Override
+    public SurfaceControl.Transaction setCanOccludePresentation(SurfaceControl sc,
+                boolean canOccludePresentation) {
+        return this;
+    }
 }
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 ef0ac33..8261dee 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1254,6 +1254,11 @@
         verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
         assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
                 captor.getValue().getIntent().getPackage());
+
+        mService.cancelScheduledTimeoutLocked(r);
+        verify(mAlarmManager).cancel(captor.capture());
+        assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
+                captor.getValue().getIntent().getPackage());
     }
 
     @Test
@@ -11681,7 +11686,7 @@
 
         // style + self managed call - bypasses block
         when(mTelecomManager.isInSelfManagedCall(
-                r.getSbn().getPackageName(), r.getUser())).thenReturn(true);
+                r.getSbn().getPackageName(), r.getUser(), true)).thenReturn(true);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
                 r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
 
@@ -11764,7 +11769,7 @@
         // style + self managed call - bypasses block
         mService.clearNotifications();
         reset(mUsageStats);
-        when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser()))
+        when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser(), true))
                 .thenReturn(true);
 
         mService.addEnqueuedNotification(r);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 670d097..130a8ca 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -66,6 +66,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
@@ -114,11 +115,13 @@
                     NotificationManager.IMPORTANCE_UNSPECIFIED);
     private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
 
-    private static final long[] CUSTOM_VIBRATION = new long[] {
+    private static final long[] CUSTOM_NOTIFICATION_VIBRATION = new long[] {
             300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
             300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
             300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 };
-    private static final long[] CUSTOM_CHANNEL_VIBRATION = new long[] {300, 400, 300, 400 };
+    private static final long[] CUSTOM_CHANNEL_VIBRATION_PATTERN = new long[] {300, 400, 300, 400 };
+    private static final VibrationEffect CUSTOM_CHANNEL_VIBRATION_EFFECT =
+            VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
     private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI;
     private static final AudioAttributes CUSTOM_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -135,6 +138,7 @@
         MockitoAnnotations.initMocks(this);
 
         when(mMockContext.getSystemService(eq(Vibrator.class))).thenReturn(mVibrator);
+        when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(true);
         final Resources res = mContext.getResources();
         when(mMockContext.getResources()).thenReturn(res);
         when(mMockContext.getPackageManager()).thenReturn(mPm);
@@ -168,8 +172,8 @@
             if (defaultVibration) {
                 defaults |= Notification.DEFAULT_VIBRATE;
             } else {
-                builder.setVibrate(CUSTOM_VIBRATION);
-                channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION);
+                builder.setVibrate(CUSTOM_NOTIFICATION_VIBRATION);
+                channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION_PATTERN);
             }
         }
         if (lights) {
@@ -217,26 +221,39 @@
         return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
     }
 
+    private StatusBarNotification getNotification(
+            long[] channelVibrationPattern,
+            VibrationEffect channelVibrationEffect,
+            boolean insistent) {
+        if (channelVibrationPattern != null) {
+            channel.setVibrationPattern(channelVibrationPattern);
+        } else if (channelVibrationEffect != null) {
+            channel.setVibrationEffect(channelVibrationEffect);
+        }
 
-    private StatusBarNotification getInsistentNotification(boolean defaultVibration) {
         final Builder builder = new Builder(mMockContext)
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setPriority(Notification.PRIORITY_HIGH);
-        int defaults = 0;
-        if (defaultVibration) {
-            defaults |= Notification.DEFAULT_VIBRATE;
-        } else {
-            builder.setVibrate(CUSTOM_VIBRATION);
-            channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION);
-        }
-        builder.setDefaults(defaults);
-        builder.setFlag(Notification.FLAG_INSISTENT, true);
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setVibrate(CUSTOM_NOTIFICATION_VIBRATION)
+                .setFlag(Notification.FLAG_INSISTENT, insistent);
 
         Notification n = builder.build();
         return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
     }
 
+    private StatusBarNotification getNotification(
+            VibrationEffect channelVibrationEffect, boolean insistent) {
+        return getNotification(
+                /* channelVibrationPattern= */ null, channelVibrationEffect, insistent);
+    }
+
+    private StatusBarNotification getNotification(
+            long[] channelVibrationPattern, boolean insistent) {
+        return getNotification(
+                channelVibrationPattern, /* channelVibrationEffect= */ null, insistent);
+    }
+
     private StatusBarNotification getMessagingStyleNotification() {
         return getMessagingStyleNotification(mPkg);
     }
@@ -344,7 +361,7 @@
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
+                CUSTOM_NOTIFICATION_VIBRATION, /* insistent= */ false), record.getVibration());
     }
 
     @Test
@@ -358,30 +375,137 @@
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertNotEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
+                CUSTOM_NOTIFICATION_VIBRATION, /* insistent= */ false), record.getVibration());
     }
 
     @Test
-    public void testVibration_custom_upgradeUsesChannel() {
+    public void testVibration_customPattern_nonInsistent_usesCustomPattern() {
         channel.enableVibration(true);
-        // post upgrade, custom vibration.
-        StatusBarNotification sbn = getNotification(PKG_O, false /* noisy */,
-                false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, null /* group */);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false);
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_CHANNEL_VIBRATION, /* insistent= */ false), record.getVibration());
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false), record.getVibration());
     }
 
     @Test
-    public void testVibration_insistent_createsInsistentVibrationEffect() {
+    public void testVibration_customPattern_insistent_createsInsistentEffect() {
         channel.enableVibration(true);
-        StatusBarNotification sbn = getInsistentNotification(false /* defaultBuzz */);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ true);
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_CHANNEL_VIBRATION, /* insistent= */ true), record.getVibration());
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ true), record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customEffect_flagNotEnabled_usesDefaultEffect() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        channel.enableVibration(true);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        VibrationEffect effect = record.getVibration();
+        assertNotEquals(effect, CUSTOM_CHANNEL_VIBRATION_EFFECT);
+        assertNotNull(effect);
+    }
+
+    @Test
+    public void testVibration_customEffect_effectNotSupported_usesDefaultEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(false);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        VibrationEffect effect = record.getVibration();
+        assertNotEquals(effect, CUSTOM_CHANNEL_VIBRATION_EFFECT);
+        assertNotNull(effect);
+    }
+
+    @Test
+    public void testVibration_customNonRepeatingEffect_nonInsistent_usesCustomEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertEquals(CUSTOM_CHANNEL_VIBRATION_EFFECT, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customNonRepeatingEffect_insistent_createsInsistentEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ true);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        VibrationEffect repeatingEffect =
+                CUSTOM_CHANNEL_VIBRATION_EFFECT
+                        .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+        assertEquals(repeatingEffect, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customRepeatingEffect_nonInsistent_createsNonRepeatingCustomEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        VibrationEffect repeatingEffect =
+                CUSTOM_CHANNEL_VIBRATION_EFFECT
+                        .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+        StatusBarNotification sbn = getNotification(repeatingEffect, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertEquals(CUSTOM_CHANNEL_VIBRATION_EFFECT, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customRepeatingEffect_insistent_usesCustomEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        VibrationEffect repeatingEffect =
+                CUSTOM_CHANNEL_VIBRATION_EFFECT
+                        .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+        StatusBarNotification sbn = getNotification(repeatingEffect, /* insistent= */ true);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertEquals(repeatingEffect, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_noCustomVibration_vibrationEnabled_usesDefaultVibration() {
+        channel.enableVibration(true);
+        StatusBarNotification sbn = getNotification(
+                /* channelVibrationPattern= */ null,
+                /* channelVibrationEffect= */ null,
+                /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertNotNull(record.getVibration());
+    }
+
+    @Test
+    public void testVibration_noCustomVibration_vibrationNotEnabled_usesNoVibration() {
+        channel.enableVibration(false);
+        StatusBarNotification sbn = getNotification(
+                /* channelVibrationPattern= */ null,
+                /* channelVibrationEffect= */ null,
+                /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertNull(record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customVibration_vibrationNotEnabled_usesNoVibration() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false);
+        channel.enableVibration(false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertNull(record.getVibration());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 5ddac03..8b55778 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -391,6 +391,7 @@
         assertEquals(expected.getSound(), actual.getSound());
         assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
         assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
+        assertEquals(expected.getVibrationEffect(), actual.getVibrationEffect());
         assertEquals(expected.getGroup(), actual.getGroup());
         assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
         assertEquals(expected.getLightColor(), actual.getLightColor());
@@ -410,6 +411,7 @@
         assertEquals(parent.getSound(), actual.getSound());
         assertEquals(parent.canBypassDnd(), actual.canBypassDnd());
         assertTrue(Arrays.equals(parent.getVibrationPattern(), actual.getVibrationPattern()));
+        assertEquals(parent.getVibrationEffect(), actual.getVibrationEffect());
         assertEquals(parent.getGroup(), actual.getGroup());
         assertEquals(parent.getAudioAttributes(), actual.getAudioAttributes());
         assertEquals(parent.getLightColor(), actual.getLightColor());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 74aabe1..aa9c0c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -94,7 +94,6 @@
 import com.android.server.wm.TaskOrganizerController.PendingTaskEvent;
 import com.android.window.flags.Flags;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -153,13 +152,6 @@
         return createTask(mDisplayContent);
     }
 
-    @Before
-    public void setUp() {
-        // We defer callbacks since we need to adjust task surface visibility, but for these tests,
-        // just run the callbacks synchronously
-        mWm.mAtmService.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer((r) -> r.run());
-    }
-
     @Test
     public void testAppearVanish() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 0ee78e3..28e03bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1791,7 +1791,6 @@
         TestStartingWindowOrganizer(ActivityTaskManagerService service) {
             mAtm = service;
             mWMService = mAtm.mWindowManager;
-            mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
             mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
         }
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index a2105b0..1df6cf7 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -21,6 +21,7 @@
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -56,6 +57,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.telecom.IVideoCallback;
 import com.android.internal.telecom.IVideoProvider;
+import com.android.server.telecom.flags.Flags;
 
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -1015,9 +1017,11 @@
     /**
      * Connection event used to communicate a {@link android.telephony.CallQuality} report from
      * telephony to Telecom for relaying to
-     * {@link DiagnosticCall#onCallQualityReceived(CallQuality)}.
+     * {@link CallDiagnostics#onCallQualityReceived(CallQuality)}.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public static final String EVENT_CALL_QUALITY_REPORT =
             "android.telecom.event.CALL_QUALITY_REPORT";
 
@@ -1026,6 +1030,8 @@
      * {@link android.telephony.CallQuality} data.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public static final String EXTRA_CALL_QUALITY_REPORT =
             "android.telecom.extra.CALL_QUALITY_REPORT";
 
@@ -4019,9 +4025,12 @@
     }
 
     /**
+     * Retrieves the direction of this connection.
      * @return The direction of the call.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public final @Call.Details.CallDirection int getCallDirection() {
         return mCallDirection;
     }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 2c6e1e4..f8e8529 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2752,17 +2752,23 @@
      *
      * @param packageName the package name of the app to check calls for.
      * @param userHandle the user handle on which to check for calls.
+     * @param hasCrossUserAccess indicates if calls should be detected across all users.
      * @return {@code true} if there are ongoing calls, {@code false} otherwise.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.INTERACT_ACROSS_USERS
+    })
     public boolean isInSelfManagedCall(@NonNull String packageName,
-            @NonNull UserHandle userHandle) {
+            @NonNull UserHandle userHandle, boolean hasCrossUserAccess) {
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
                 return service.isInSelfManagedCall(packageName, userHandle,
-                        mContext.getOpPackageName());
+                        mContext.getOpPackageName(), hasCrossUserAccess);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
                 e.rethrowFromSystemServer();
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index f1bfd22..412e827 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -395,7 +395,7 @@
      * @see TelecomServiceImpl#isInSelfManagedCall
      */
     boolean isInSelfManagedCall(String packageName, in UserHandle userHandle,
-        String callingPackage);
+        String callingPackage, boolean hasCrossUserAccess);
 
     /**
      * @see TelecomServiceImpl#addCall
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 287aa65..7607c64 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -15,4 +15,4 @@
 per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
 
 #Domain Selection is jointly owned, add additional owners for domain selection specific files
-per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com
+per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com,jdyou@google.com
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 5434c82..5f1bc87 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -67,8 +67,14 @@
         assertEquals("keyCharacterMap not equal", keyCharacterMap, outKeyCharacterMap);
 
         for (int j = 0; j < device.getMotionRanges().size(); j++) {
-            assertMotionRangeEquals(device.getMotionRanges().get(j),
-                    outDevice.getMotionRanges().get(j));
+            InputDevice.MotionRange motionRange = device.getMotionRanges().get(j);
+            assertMotionRangeEquals(motionRange, outDevice.getMotionRanges().get(j));
+
+            int axis = motionRange.getAxis();
+            int source = motionRange.getSource();
+            assertEquals(
+                    device.getViewBehavior().shouldSmoothScroll(axis, source),
+                    outDevice.getViewBehavior().shouldSmoothScroll(axis, source));
         }
     }
 
@@ -93,7 +99,8 @@
                 .setHasBattery(true)
                 .setKeyboardLanguageTag("en-US")
                 .setKeyboardLayoutType("qwerty")
-                .setUsiVersion(new HostUsiVersion(2, 0));
+                .setUsiVersion(new HostUsiVersion(2, 0))
+                .setShouldSmoothScroll(true);
 
         for (int i = 0; i < 30; i++) {
             deviceBuilder.addMotionRange(
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
index 5d3abc6..9276402 100644
--- a/tools/aapt/ApkBuilder.h
+++ b/tools/aapt/ApkBuilder.h
@@ -44,7 +44,7 @@
     android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs);
 
     /**
-     * Adds a file to be written to the final APK. It's name must not collide
+     * Adds a file to be written to the final APK. Its name must not collide
      * with that of any files previously added. When a Split APK is being
      * generated, duplicates can exist as long as they are in different splits
      * (resources.arsc, AndroidManifest.xml).
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 2f2ef92..66a0510 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -187,7 +187,7 @@
         "       be loaded alongside the base APK at runtime.\n"
         "   --feature-of\n"
         "       Builds a split APK that is a feature of the apk specified here. Resources\n"
-        "       in the base APK can be referenced from the the feature APK.\n"
+        "       in the base APK can be referenced from the feature APK.\n"
         "   --feature-after\n"
         "       An app can have multiple Feature Split APKs which must be totally ordered.\n"
         "       If --feature-of is specified, this flag specifies which Feature Split APK\n"