Merge "Disable uncaught exception handling." into main
diff --git a/MEMORY_OWNERS b/MEMORY_OWNERS
new file mode 100644
index 0000000..89ce5140
--- /dev/null
+++ b/MEMORY_OWNERS
@@ -0,0 +1,6 @@
+surenb@google.com
+tjmercier@google.com
+kaleshsingh@google.com
+jyescas@google.com
+carlosgalo@google.com
+jji@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index d7a99d3..a3775b0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9514,8 +9514,8 @@
     method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
     method @FlaggedApi("android.appwidget.flags.generated_previews") @Nullable public android.widget.RemoteViews getWidgetPreview(@NonNull android.content.ComponentName, @Nullable android.os.UserHandle, int);
     method public boolean isRequestPinAppWidgetSupported();
-    method @Deprecated public void notifyAppWidgetViewDataChanged(int[], int);
-    method @Deprecated public void notifyAppWidgetViewDataChanged(int, int);
+    method public void notifyAppWidgetViewDataChanged(int[], int);
+    method public void notifyAppWidgetViewDataChanged(int, int);
     method public void partiallyUpdateAppWidget(int[], android.widget.RemoteViews);
     method public void partiallyUpdateAppWidget(int, android.widget.RemoteViews);
     method @FlaggedApi("android.appwidget.flags.generated_previews") public void removeWidgetPreview(@NonNull android.content.ComponentName, int);
@@ -45225,13 +45225,13 @@
     method @FlaggedApi("com.android.internal.telephony.flags.enforce_subscription_user_filter") @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) public android.telephony.SubscriptionManager createForAllUserProfiles();
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
     method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
-    method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
+    method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
     method public static int getActiveDataSubscriptionId();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount();
     method public int getActiveSubscriptionInfoCountMax();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public java.util.List<android.telephony.SubscriptionInfo> getAllSubscriptionInfoList();
     method @NonNull public java.util.List<android.telephony.SubscriptionInfo> getCompleteActiveSubscriptionInfoList();
     method public static int getDefaultDataSubscriptionId();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d0fdf69..fe97349 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14590,7 +14590,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
-    method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
+    method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
     method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 77add41..850f149 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1745,6 +1745,7 @@
     method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice);
     method @NonNull public String getKeyboardLayoutTypeForLayoutDescriptor(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public java.util.Map<java.lang.Integer,java.lang.Integer> getModifierKeyRemapping();
+    method public int getMousePointerSpeed();
     method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void remapModifierKey(int, int);
     method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
     method public void removeUniqueIdAssociation(@NonNull String);
@@ -1754,6 +1755,7 @@
 
   public class InputSettings {
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float);
+    field public static final int DEFAULT_POINTER_SPEED = 0; // 0x0
   }
 
 }
@@ -2882,6 +2884,10 @@
     field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
   }
 
+  public static final class Settings.System extends android.provider.Settings.NameValueTable {
+    field public static final String POINTER_SPEED = "pointer_speed";
+  }
+
   public static final class Telephony.Sms.Intents {
     field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION";
   }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 6204edc..eb82e1f 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -822,18 +822,7 @@
      *
      * @param appWidgetIds  The AppWidget instances to notify of view data changes.
      * @param viewId        The collection view id.
-     * @deprecated The corresponding API
-     * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been
-     * deprecated. Moving forward please use
-     * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)}
-     * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote
-     * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)},
-     * {@link #updateAppWidget(int, RemoteViews)},
-     * {@link #updateAppWidget(ComponentName, RemoteViews)},
-     * {@link #partiallyUpdateAppWidget(int[], RemoteViews)},
-     * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable.
      */
-    @Deprecated
     public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
         if (mService == null) {
             return;
@@ -884,18 +873,7 @@
      *
      * @param appWidgetId  The AppWidget instance to notify of view data changes.
      * @param viewId       The collection view id.
-     * @deprecated The corresponding API
-     * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been
-     * deprecated. Moving forward please use
-     * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)}
-     * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote
-     * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)},
-     * {@link #updateAppWidget(int, RemoteViews)},
-     * {@link #updateAppWidget(ComponentName, RemoteViews)},
-     * {@link #partiallyUpdateAppWidget(int[], RemoteViews)},
-     * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable.
      */
-    @Deprecated
     public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
         if (mService == null) {
             return;
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index 53dd3bf..fb95608895 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -10,3 +10,4 @@
 per-file UserInfo* = file:/MULTIUSER_OWNERS
 per-file *UserProperties* = file:/MULTIUSER_OWNERS
 per-file *multiuser* = file:/MULTIUSER_OWNERS
+per-file IBackgroundInstallControlService.aidl = file:/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS
diff --git a/core/java/android/hardware/camera2/extension/AdvancedExtender.java b/core/java/android/hardware/camera2/extension/AdvancedExtender.java
index a06f017..6653577 100644
--- a/core/java/android/hardware/camera2/extension/AdvancedExtender.java
+++ b/core/java/android/hardware/camera2/extension/AdvancedExtender.java
@@ -57,6 +57,7 @@
     private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
     private final CameraManager mCameraManager;
 
+    private CameraUsageTracker mCameraUsageTracker;
     private static final String TAG = "AdvancedExtender";
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
@@ -82,6 +83,10 @@
         }
     }
 
+    void setCameraUsageTracker(CameraUsageTracker tracker) {
+        mCameraUsageTracker = tracker;
+    }
+
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public long getMetadataVendorId(@NonNull String cameraId) {
         long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ?
@@ -282,7 +287,9 @@
 
         @Override
         public ISessionProcessorImpl getSessionProcessor() {
-            return AdvancedExtender.this.getSessionProcessor().getSessionProcessorBinder();
+            SessionProcessor processor =AdvancedExtender.this.getSessionProcessor();
+            processor.setCameraUsageTracker(mCameraUsageTracker);
+            return processor.getSessionProcessorBinder();
         }
 
         @Override
diff --git a/core/java/android/hardware/camera2/extension/CameraExtensionService.java b/core/java/android/hardware/camera2/extension/CameraExtensionService.java
index 1426d7b..fa0d14a 100644
--- a/core/java/android/hardware/camera2/extension/CameraExtensionService.java
+++ b/core/java/android/hardware/camera2/extension/CameraExtensionService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.AppOpsManager;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
@@ -29,6 +30,11 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.camera.flags.Flags;
 
+interface CameraUsageTracker {
+    void startCameraOperation();
+    void finishCameraOperation();
+}
+
 /**
  * Base service class that extension service implementations must extend.
  *
@@ -38,8 +44,33 @@
 @FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public abstract class CameraExtensionService extends Service {
     private static final String TAG = "CameraExtensionService";
+    private CameraUsageTracker mCameraUsageTracker;
     private static Object mLock = new Object();
 
+    private final class CameraTracker implements CameraUsageTracker {
+
+        private final AppOpsManager mAppOpsService = getApplicationContext().getSystemService(
+                AppOpsManager.class);
+        private final String mPackageName = getPackageName();
+        private final String mAttributionTag = getAttributionTag();
+        private int mUid = getApplicationInfo().uid;
+
+        @Override
+        public void startCameraOperation() {
+            if (mAppOpsService != null) {
+                mAppOpsService.startOp(AppOpsManager.OPSTR_CAMERA, mUid, mPackageName,
+                        mAttributionTag, "Camera extensions");
+            }
+        }
+
+        @Override
+        public void finishCameraOperation() {
+            if (mAppOpsService != null) {
+                mAppOpsService.finishOp(AppOpsManager.OPSTR_CAMERA, mUid, mPackageName,
+                        mAttributionTag);
+            }
+        }
+    }
     @GuardedBy("mLock")
     private static IInitializeSessionCallback mInitializeCb = null;
 
@@ -49,16 +80,22 @@
             synchronized (mLock) {
                 mInitializeCb = null;
             }
+            if (mCameraUsageTracker != null) {
+                mCameraUsageTracker.finishCameraOperation();
+            }
         }
     };
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
-    protected CameraExtensionService() {}
+    protected CameraExtensionService() { }
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @Override
     @NonNull
     public IBinder onBind(@Nullable Intent intent) {
+        if (mCameraUsageTracker == null) {
+            mCameraUsageTracker = new CameraTracker();
+        }
         return new CameraExtensionServiceImpl();
     }
 
@@ -132,8 +169,10 @@
         @Override
         public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType)
                 throws RemoteException {
-            return CameraExtensionService.this.onInitializeAdvancedExtension(
-                    extensionType).getAdvancedExtenderBinder();
+            AdvancedExtender extender =  CameraExtensionService.this.onInitializeAdvancedExtension(
+                    extensionType);
+            extender.setCameraUsageTracker(mCameraUsageTracker);
+            return extender.getAdvancedExtenderBinder();
         }
     }
 
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index 6ed0c14..9c5136b 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -76,10 +76,15 @@
 @FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public abstract class SessionProcessor {
     private static final String TAG = "SessionProcessor";
+    private CameraUsageTracker mCameraUsageTracker;
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     protected SessionProcessor() {}
 
+    void setCameraUsageTracker(CameraUsageTracker tracker) {
+        mCameraUsageTracker = tracker;
+    }
+
     /**
      * Callback for notifying the status of {@link
      * #startCapture} and {@link #startRepeating}.
@@ -379,12 +384,18 @@
         @Override
         public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor, String statsKey)
                 throws RemoteException {
+            if (mCameraUsageTracker != null) {
+                mCameraUsageTracker.startCameraOperation();
+            }
             SessionProcessor.this.onCaptureSessionStart(
                     new RequestProcessor(requestProcessor, mVendorId), statsKey);
         }
 
         @Override
         public void onCaptureSessionEnd() throws RemoteException {
+            if (mCameraUsageTracker != null) {
+                mCameraUsageTracker.finishCameraOperation();
+            }
             SessionProcessor.this.onCaptureSessionEnd();
         }
 
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 7bea9ae..1f54959 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -67,6 +67,9 @@
 
     KeyCharacterMap getKeyCharacterMap(String layoutDescriptor);
 
+    // Returns the mouse pointer speed.
+    int getMousePointerSpeed();
+
     // Temporarily changes the pointer speed.
     void tryPointerSpeed(int speed);
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index db992cd..744dfae9 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -28,6 +28,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
@@ -857,6 +858,28 @@
     }
 
     /**
+     * Returns the mouse pointer speed.
+     *
+     * <p>The pointer speed is a value between {@link InputSettings#MIN_POINTER_SPEED} and
+     * {@link InputSettings#MAX_POINTER_SPEED}, the default value being
+     * {@link InputSettings#DEFAULT_POINTER_SPEED}.
+     *
+     * <p> Note that while setting the mouse pointer speed, it's possible that the input reader has
+     * only received this value and has not yet completed reconfiguring itself with this value.
+     *
+     * @hide
+     */
+    @SuppressLint("UnflaggedApi") // TestApi without associated feature.
+    @TestApi
+    public int getMousePointerSpeed() {
+        try {
+            return mIm.getMousePointerSpeed();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Changes the mouse pointer speed temporarily, but does not save the setting.
      * <p>
      * Requires {@link android.Manifest.permission#SET_POINTER_SPEED}.
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 89fa5fb..54e34ec 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -54,8 +54,8 @@
 
     /**
      * Pointer Speed: The default pointer speed (0).
-     * @hide
      */
+    @SuppressLint("UnflaggedApi") // TestApi without associated feature.
     public static final int DEFAULT_POINTER_SPEED = 0;
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 524b733..76fda06 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6015,8 +6015,10 @@
          *   +7 = fastest
          * @hide
          */
+        @SuppressLint({"NoSettingsProvider", "UnflaggedApi"}) // TestApi without associated feature.
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @Readable
+        @TestApi
         public static final String POINTER_SPEED = "pointer_speed";
 
         /**
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 163dfa2..021bbf7 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -197,7 +196,6 @@
     private Canvas mCanvas;
     private int mSaveCount;
 
-    @FloatRange(from = 0.0) float mFrameRate;
     @Surface.FrameRateCompatibility int mFrameRateCompatibility;
 
     private final Object[] mNativeWindowLock = new Object[0];
@@ -473,13 +471,13 @@
             mLayer.setSurfaceTexture(mSurface);
             mSurface.setDefaultBufferSize(getWidth(), getHeight());
             mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
-            if (Flags.toolkitSetFrameRate()) {
+            if (Flags.toolkitSetFrameRateReadOnly()) {
                 mSurface.setOnSetFrameRateListener(
                         (surfaceTexture, frameRate, compatibility, strategy) -> {
                             if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                                 Trace.instant(Trace.TRACE_TAG_VIEW, "setFrameRate: " + frameRate);
                             }
-                            mFrameRate = frameRate;
+                            setRequestedFrameRate(frameRate);
                             mFrameRateCompatibility = compatibility;
                         }, mAttachInfo.mHandler);
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c27b2b1..6534354 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -26,6 +26,7 @@
 import static android.view.InputDevice.SOURCE_CLASS_NONE;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -1013,8 +1014,10 @@
     // Used to check if there were any view invalidations in
     // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
     private boolean mHasInvalidation = false;
-    // Used to check if it is in the touch boosting period.
+    // Used to check if it is in the frame rate boosting period.
     private boolean mIsFrameRateBoosting = false;
+    // Used to check if it is in touch boosting period.
+    private boolean mIsTouchBoosting = false;
     // Used to check if there is a message in the message queue
     // for idleness handling.
     private boolean mHasIdledMessage = false;
@@ -1024,6 +1027,9 @@
     private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
     // time for revaluating the idle status before lowering the frame rate.
     private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
+    // time for evaluating the interval between current time and
+    // the time when frame rate was set previously.
+    private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
 
     /*
      * the variables below are used to determine whther a dVRR feature should be enabled
@@ -4080,7 +4086,6 @@
         setPreferredFrameRate(mPreferredFrameRate);
         setPreferredFrameRateCategory(mPreferredFrameRateCategory);
         mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
-        mPreferredFrameRate = 0;
     }
 
     private void createSyncIfNeeded() {
@@ -6134,6 +6139,7 @@
     private static final int MSG_TOUCH_BOOST_TIMEOUT = 39;
     private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
     private static final int MSG_REFRESH_POINTER_ICON = 41;
+    private static final int MSG_FRAME_RATE_SETTING = 42;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -6445,11 +6451,12 @@
                      * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
                      */
                     mIsFrameRateBoosting = false;
+                    mIsTouchBoosting = false;
                     setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
                             mLastPreferredFrameRateCategory));
                     break;
                 case MSG_CHECK_INVALIDATION_IDLE:
-                    if (!mHasInvalidation && !mIsFrameRateBoosting) {
+                    if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) {
                         mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
                         setPreferredFrameRateCategory(mPreferredFrameRateCategory);
                         mHasIdledMessage = false;
@@ -6472,6 +6479,10 @@
                     }
                     updatePointerIcon(mPointerIconEvent);
                     break;
+                case MSG_FRAME_RATE_SETTING:
+                    mPreferredFrameRate = 0;
+                    setPreferredFrameRate(mPreferredFrameRate);
+                    break;
             }
         }
     }
@@ -7482,7 +7493,7 @@
             // For the variable refresh rate project
             if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
                 // set the frame rate to the maximum value.
-                mIsFrameRateBoosting = true;
+                mIsTouchBoosting = true;
                 setPreferredFrameRateCategory(mPreferredFrameRateCategory);
             }
             /**
@@ -7490,7 +7501,7 @@
              * MotionEvent.ACTION_CANCEL is detected.
              * Not using ACTION_MOVE to avoid checking and sending messages too frequently.
              */
-            if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
+            if (mIsTouchBoosting && (action == MotionEvent.ACTION_UP
                     || action == MotionEvent.ACTION_CANCEL)) {
                 mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
                 mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
@@ -12234,17 +12245,32 @@
             return;
         }
 
-        int frameRateCategory = mIsFrameRateBoosting || mInsetsAnimationRunning
-                ? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;
+        int frameRateCategory = mIsTouchBoosting
+                ? FRAME_RATE_CATEGORY_HIGH_HINT : preferredFrameRateCategory;
+
+        // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
+        // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
+        // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction
+        // (e.g., Window Initialization).
+        if (mIsFrameRateBoosting || mInsetsAnimationRunning) {
+            frameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+        }
 
         try {
             if (mLastPreferredFrameRateCategory != frameRateCategory) {
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                    Trace.traceBegin(
+                            Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory "
+                                + frameRateCategory);
+                }
                 mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
                         frameRateCategory, false).applyAsyncUnsafe();
                 mLastPreferredFrameRateCategory = frameRateCategory;
             }
         } catch (Exception e) {
             Log.e(mTag, "Unable to set frame rate category", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
 
         if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
@@ -12263,12 +12289,19 @@
 
         try {
             if (mLastPreferredFrameRate != preferredFrameRate) {
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                    Trace.traceBegin(
+                            Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate "
+                                + preferredFrameRate);
+                }
                 mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
                     Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).applyAsyncUnsafe();
                 mLastPreferredFrameRate = preferredFrameRate;
             }
         } catch (Exception e) {
             Log.e(mTag, "Unable to set frame rate", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
     }
 
@@ -12285,7 +12318,7 @@
 
     private boolean shouldSetFrameRate() {
         // use toolkitSetFrameRate flag to gate the change
-        return mPreferredFrameRate > 0 && sToolkitSetFrameRateReadOnlyFlagValue;
+        return sToolkitSetFrameRateReadOnlyFlagValue;
     }
 
     private boolean shouldTouchBoost(int motionEventAction, int windowType) {
@@ -12336,6 +12369,9 @@
         }
 
         mHasInvalidation = true;
+        mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+        mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+                FRAME_RATE_SETTING_REEVALUATE_TIME);
     }
 
     /**
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 1de77f6..82067de 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -66,3 +66,14 @@
   bug: "314952133"
   is_fixed_read_only: true
 }
+
+flag {
+  name: "app_compat_refactoring"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether the changes about app compat refactoring are enabled./n"
+                 "The goal is to simplify code readability unblocking the implementation of /n"
+                 "app compat feature like reachability, animations and others related to/n"
+                 "freeform windowing mode."
+  bug: "309593314"
+  is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 627e877..e591327 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -171,11 +171,11 @@
      * Register a LockSettingsStateListener
      * @param listener The listener to be registered
      */
-    public abstract void registerLockSettingsStateListener(ILockSettingsStateListener listener);
+    public abstract void registerLockSettingsStateListener(LockSettingsStateListener listener);
 
     /**
      * Unregister a LockSettingsStateListener
      * @param listener The listener to be unregistered
      */
-    public abstract void unregisterLockSettingsStateListener(ILockSettingsStateListener listener);
+    public abstract void unregisterLockSettingsStateListener(LockSettingsStateListener listener);
 }
diff --git a/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl b/core/java/com/android/internal/widget/LockSettingsStateListener.java
similarity index 91%
rename from core/java/com/android/internal/widget/ILockSettingsStateListener.aidl
rename to core/java/com/android/internal/widget/LockSettingsStateListener.java
index 25e3003..869e676 100644
--- a/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl
+++ b/core/java/com/android/internal/widget/LockSettingsStateListener.java
@@ -5,7 +5,7 @@
  * 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
+ *      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,
@@ -21,7 +21,7 @@
  * state of primary authentication (i.e. PIN/pattern/password).
  * @hide
  */
-oneway interface ILockSettingsStateListener {
+public interface LockSettingsStateListener {
     /**
      * Defines behavior in response to a successful authentication
      * @param userId The user Id for the requested authentication
@@ -33,4 +33,4 @@
      * @param userId The user Id for the requested authentication
      */
     void onAuthenticationFailed(int userId);
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 60769c7..52e996c 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -20,6 +20,7 @@
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
@@ -669,8 +670,13 @@
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT);
+            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+                    FRAME_RATE_CATEGORY_HIGH_HINT);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT);
+            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
@@ -796,6 +802,33 @@
     }
 
     /**
+     * Test votePreferredFrameRate_voteFrameRateTimeOut
+     * If no frame rate is voted in 100 milliseconds, the value of
+     * mPreferredFrameRate should be set to 0.
+     */
+    @Test
+    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+    public void votePreferredFrameRate_voteFrameRateTimeOut() throws InterruptedException {
+        final long delay = 200L;
+
+        View view = new View(sContext);
+        attachViewToWindow(view);
+        sInstrumentation.waitForIdleSync();
+        ViewRootImpl viewRootImpl = view.getViewRootImpl();
+
+        sInstrumentation.runOnMainSync(() -> {
+            assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+            viewRootImpl.votePreferredFrameRate(24);
+            assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+            view.invalidate();
+            assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+        });
+
+        Thread.sleep(delay);
+        assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+    }
+
+    /**
      * Test the logic of infrequent layer:
      * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100.
      * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100.
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index dd82fed..50b167e 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -489,7 +489,7 @@
             @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "postOnSetFrameRateEventFromNative");
         try {
-            if (Flags.toolkitSetFrameRate()) {
+            if (Flags.toolkitSetFrameRateReadOnly()) {
                 SurfaceTexture st = weakSelf.get();
                 if (st != null) {
                     Handler handler = st.mOnSetFrameRateHandler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index f82212d..7c8fcbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -248,6 +248,12 @@
 
         // Check if count changed
         if (prevCount != newCount) {
+            KtProtoLog.d(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopTaskRepo: visibleTaskCount has changed from %d to %d",
+                prevCount,
+                newCount
+            )
             notifyVisibleTaskListeners(displayId, newCount)
         }
     }
@@ -262,6 +268,11 @@
      * Get number of tasks that are marked as visible on given [displayId]
      */
     fun getVisibleTaskCount(displayId: Int): Int {
+        KtProtoLog.d(
+            WM_SHELL_DESKTOP_MODE,
+            "DesktopTaskRepo: visibleTaskCount= %d",
+            displayData[displayId]?.visibleTasks?.size ?: 0
+        )
         return displayData[displayId]?.visibleTasks?.size ?: 0
     }
 
@@ -290,6 +301,10 @@
             taskId
         )
         freeformTasksInZOrder.remove(taskId)
+        KtProtoLog.d(
+            WM_SHELL_DESKTOP_MODE,
+            "DesktopTaskRepo: remaining freeform tasks: " + freeformTasksInZOrder.toDumpString()
+        )
     }
 
     /**
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index fd2f9bd..bab6781 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -7,3 +7,13 @@
     bug: "314812750"
 }
 
+flag {
+    name: "enable_cached_bluetooth_device_dedup"
+    namespace: "bluetooth"
+    description: "Enable dedup in CachedBluetoothDevice"
+    bug: "319197962"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index c97445f..647fcb9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.bluetooth;
 
+import static com.android.settingslib.flags.Flags.enableCachedBluetoothDeviceDedup;
+
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothCsipSetCoordinator;
@@ -377,6 +379,10 @@
                 cachedDevice = mDeviceManager.addDevice(device);
             }
 
+            if (enableCachedBluetoothDeviceDedup() && bondState == BluetoothDevice.BOND_BONDED) {
+                mDeviceManager.removeDuplicateInstanceForIdentityAddress(device);
+            }
+
             for (BluetoothCallback callback : mCallbacks) {
                 callback.onDeviceBondStateChanged(cachedDevice, bondState);
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 89fe268..32eec7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -340,6 +340,20 @@
         }
     }
 
+    synchronized void removeDuplicateInstanceForIdentityAddress(BluetoothDevice device) {
+        String identityAddress = device.getIdentityAddress();
+        if (identityAddress == null || identityAddress.equals(device.getAddress())) {
+            return;
+        }
+        mCachedDevices.removeIf(d -> {
+            boolean shouldRemove = d.getDevice().getAddress().equals(identityAddress);
+            if (shouldRemove) {
+                Log.d(TAG, "Remove instance for identity address " + d);
+            }
+            return shouldRemove;
+        });
+    }
+
     public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
             cachedDevice, int state, int profileId) {
         if (profileId == BluetoothProfile.HEARING_AID) {
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 13ee196..9a34d6f 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
@@ -27,6 +27,8 @@
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.scene.shared.model.Scene
@@ -112,6 +114,12 @@
         dialogFactory: BouncerDialogFactory,
     ): View = throwComposeUnavailableError()
 
+    override fun createLockscreen(
+        context: Context,
+        viewModel: LockscreenContentViewModel,
+        blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    ): View = throwComposeUnavailableError()
+
     private fun throwComposeUnavailableError(): Nothing {
         error(
             "Compose is not available. Make sure to check isComposeAvailable() before calling any" +
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index 725aef2..fc3912e 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -16,6 +16,16 @@
 
 package com.android.systemui.scene
 
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import dagger.Module
+import dagger.Provides
 
-@Module interface LockscreenSceneModule
+@Module
+interface LockscreenSceneModule {
+    companion object {
+        @Provides
+        fun providesLockscreenBlueprints(): Set<LockscreenSceneBlueprint> {
+            return emptySet()
+        }
+    }
+}
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 f05c7f3..4cc7332 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
@@ -22,6 +22,8 @@
 import android.view.WindowInsets
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -40,6 +42,10 @@
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.composable.LockscreenContent
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.people.ui.compose.PeopleScreen
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 import com.android.systemui.qs.footer.ui.compose.FooterActions
@@ -211,4 +217,19 @@
             setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
         }
     }
+
+    override fun createLockscreen(
+        context: Context,
+        viewModel: LockscreenContentViewModel,
+        blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    ): View {
+        val sceneBlueprints =
+            blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet()
+        return ComposeView(context).apply {
+            setContent {
+                LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints)
+                    .Content(modifier = Modifier.fillMaxSize())
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index cbf2496..f5dc154 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -20,8 +20,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardViewConfigurator
 import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.composable.LockscreenScene
 import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModule
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.scene.shared.model.Scene
 import dagger.Binds
 import dagger.Module
@@ -51,5 +53,12 @@
         ): () -> View {
             return { configurator.get().getKeyguardRootView() }
         }
+
+        @Provides
+        fun providesLockscreenBlueprints(
+            blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>
+        ): Set<LockscreenSceneBlueprint> {
+            return blueprints
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 2cb0034..b5499b7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -25,7 +25,7 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.animation.scene.transitions
-import com.android.systemui.keyguard.ui.composable.blueprint.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import javax.inject.Inject
 
@@ -39,10 +39,10 @@
 @Inject
 constructor(
     private val viewModel: LockscreenContentViewModel,
-    private val blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    private val blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>,
 ) {
 
-    private val sceneKeyByBlueprint: Map<LockscreenSceneBlueprint, SceneKey> by lazy {
+    private val sceneKeyByBlueprint: Map<ComposableLockscreenSceneBlueprint, SceneKey> by lazy {
         blueprints.associateWith { blueprint -> SceneKey(blueprint.id) }
     }
     private val sceneKeyByBlueprintId: Map<String, SceneKey> by lazy {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
index 86124c6..6b210af 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -36,7 +36,7 @@
 @Inject
 constructor(
     private val viewModel: LockscreenContentViewModel,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "communal"
 
@@ -59,5 +59,5 @@
 
 @Module
 interface CommunalBlueprintModule {
-    @Binds @IntoSet fun blueprint(blueprint: CommunalBlueprint): LockscreenSceneBlueprint
+    @Binds @IntoSet fun blueprint(blueprint: CommunalBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
similarity index 87%
rename from packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
rename to packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
index 6d9cba4..cb73983 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
@@ -19,13 +19,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 
 /** Defines interface for classes that can render the content for a specific blueprint/layout. */
-interface LockscreenSceneBlueprint {
-
-    /** The ID that uniquely identifies this blueprint across all other blueprints. */
-    val id: String
-
+interface ComposableLockscreenSceneBlueprint : LockscreenSceneBlueprint {
     /** Renders the content of this blueprint. */
     @Composable fun SceneScope.Content(modifier: Modifier)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index bf02d8a..a07ab4a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -20,10 +20,12 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
@@ -38,6 +40,7 @@
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
@@ -61,7 +64,7 @@
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
     private val clockInteractor: KeyguardClockInteractor,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "default"
 
@@ -84,6 +87,7 @@
                         with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
                         with(clockSection) {
                             SmallClock(
+                                burnInParams = burnIn.parameters,
                                 onTopChanged = burnIn.onSmallClockTopChanged,
                                 modifier = Modifier.fillMaxWidth(),
                             )
@@ -95,7 +99,13 @@
                                 modifier =
                                     Modifier.fillMaxWidth()
                                         .padding(
-                                            top = { viewModel.getSmartSpacePaddingTop(resources) }
+                                            top = { viewModel.getSmartSpacePaddingTop(resources) },
+                                        )
+                                        .padding(
+                                            bottom =
+                                                dimensionResource(
+                                                    R.dimen.keyguard_status_view_bottom_margin
+                                                ),
                                         ),
                             )
                         }
@@ -214,5 +224,5 @@
 
 @Module
 interface DefaultBlueprintModule {
-    @Binds @IntoSet fun blueprint(blueprint: DefaultBlueprint): LockscreenSceneBlueprint
+    @Binds @IntoSet fun blueprint(blueprint: DefaultBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index d0aa444..b035e42 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -20,10 +20,12 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
@@ -38,6 +40,7 @@
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
@@ -61,7 +64,7 @@
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
     private val clockInteractor: KeyguardClockInteractor,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "shortcuts-besides-udfps"
 
@@ -86,6 +89,7 @@
                             SmallClock(
                                 onTopChanged = burnIn.onSmallClockTopChanged,
                                 modifier = Modifier.fillMaxWidth(),
+                                burnInParams = burnIn.parameters,
                             )
                         }
                         with(smartSpaceSection) {
@@ -96,6 +100,12 @@
                                     Modifier.fillMaxWidth()
                                         .padding(
                                             top = { viewModel.getSmartSpacePaddingTop(resources) }
+                                        )
+                                        .padding(
+                                            bottom =
+                                                dimensionResource(
+                                                    R.dimen.keyguard_status_view_bottom_margin
+                                                )
                                         ),
                             )
                         }
@@ -222,5 +232,5 @@
 interface ShortcutsBesideUdfpsBlueprintModule {
     @Binds
     @IntoSet
-    fun blueprint(blueprint: ShortcutsBesideUdfpsBlueprint): LockscreenSceneBlueprint
+    fun blueprint(blueprint: ShortcutsBesideUdfpsBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index 616a7b4..660fc5a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -72,7 +72,7 @@
     private val settingsMenuSection: SettingsMenuSection,
     private val clockInteractor: KeyguardClockInteractor,
     private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "split-shade"
 
@@ -109,7 +109,14 @@
                                                 .padding(
                                                     top = {
                                                         viewModel.getSmartSpacePaddingTop(resources)
-                                                    }
+                                                    },
+                                                )
+                                                .padding(
+                                                    bottom =
+                                                        dimensionResource(
+                                                            R.dimen
+                                                                .keyguard_status_view_bottom_margin
+                                                        )
                                                 ),
                                     )
                                 }
@@ -237,5 +244,7 @@
 
 @Module
 interface SplitShadeBlueprintModule {
-    @Binds @IntoSet fun blueprint(blueprint: SplitShadeBlueprint): LockscreenSceneBlueprint
+    @Binds
+    @IntoSet
+    fun blueprint(blueprint: SplitShadeBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
index 8f21879..fa07baf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
@@ -33,7 +33,10 @@
 import com.android.keyguard.KeyguardClockSwitch
 import com.android.systemui.customization.R as customizationR
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
 import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
+import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import javax.inject.Inject
 
@@ -42,10 +45,12 @@
 constructor(
     private val viewModel: KeyguardClockViewModel,
     private val clockInteractor: KeyguardClockInteractor,
+    private val aodBurnInViewModel: AodBurnInViewModel,
 ) {
 
     @Composable
     fun SceneScope.SmallClock(
+        burnInParams: BurnInParameters,
         onTopChanged: (top: Float?) -> Unit,
         modifier: Modifier = Modifier,
     ) {
@@ -89,7 +94,11 @@
                                     dimensionResource(customizationR.dimen.clock_padding_start)
                             )
                             .padding(top = { viewModel.getSmallClockTopMargin(view.context) })
-                            .onTopPlacementChanged(onTopChanged),
+                            .onTopPlacementChanged(onTopChanged)
+                            .burnInAware(
+                                viewModel = aodBurnInViewModel,
+                                params = burnInParams,
+                            ),
                     update = {
                         val newClockView = checkNotNull(currentClock).smallClock.view
                         it.removeAllViews()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 5e27d82..6a8da10 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -19,6 +19,11 @@
 import android.content.Context
 import android.view.ViewGroup
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
@@ -40,56 +45,82 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.DisposableHandle
 
 @SysUISingleton
 class NotificationSection
 @Inject
 constructor(
-    @Application context: Context,
+    @Application private val context: Context,
     private val viewModel: NotificationsPlaceholderViewModel,
-    controller: NotificationStackScrollLayoutController,
-    sceneContainerFlags: SceneContainerFlags,
-    sharedNotificationContainer: SharedNotificationContainer,
-    sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
-    stackScrollLayout: NotificationStackScrollLayout,
-    notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
-    ambientState: AmbientState,
-    notificationStackSizeCalculator: NotificationStackSizeCalculator,
-    @Main mainDispatcher: CoroutineDispatcher,
+    private val controller: NotificationStackScrollLayoutController,
+    private val sceneContainerFlags: SceneContainerFlags,
+    private val sharedNotificationContainer: SharedNotificationContainer,
+    private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+    private val stackScrollLayout: NotificationStackScrollLayout,
+    private val notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
+    private val ambientState: AmbientState,
+    private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
+    @Main private val mainDispatcher: CoroutineDispatcher,
 ) {
-    init {
-        if (!KeyguardShadeMigrationNssl.isUnexpectedlyInLegacyMode()) {
-            // This scene container section moves the NSSL to the SharedNotificationContainer. This
-            //  also requires that SharedNotificationContainer gets moved to the SceneWindowRootView
-            //  by the SceneWindowRootViewBinder.
-            // Prior to Scene Container, but when the KeyguardShadeMigrationNssl flag is enabled,
-            //  NSSL is moved into this container by the NotificationStackScrollLayoutSection.
-            (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout)
-            sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout)
+    @Composable
+    fun SceneScope.Notifications(modifier: Modifier = Modifier) {
+        if (KeyguardShadeMigrationNssl.isUnexpectedlyInLegacyMode()) {
+            // This scene container section moves the NSSL to the SharedNotificationContainer.
+            // This also requires that SharedNotificationContainer gets moved to the
+            // SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container,
+            // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this
+            // container by the NotificationStackScrollLayoutSection.
+            return
+        }
 
-            SharedNotificationContainerBinder.bind(
-                sharedNotificationContainer,
-                sharedNotificationContainerViewModel,
-                sceneContainerFlags,
-                controller,
-                notificationStackSizeCalculator,
-                mainDispatcher,
+        var isBound by remember { mutableStateOf(false) }
+
+        DisposableEffect(Unit) {
+            val disposableHandles: MutableList<DisposableHandle> = mutableListOf()
+
+            // Ensure stackScrollLayout is a child of sharedNotificationContainer.
+            if (stackScrollLayout.parent != sharedNotificationContainer) {
+                (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout)
+                sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout)
+            }
+
+            disposableHandles.add(
+                SharedNotificationContainerBinder.bind(
+                    sharedNotificationContainer,
+                    sharedNotificationContainerViewModel,
+                    sceneContainerFlags,
+                    controller,
+                    notificationStackSizeCalculator,
+                    mainDispatcher,
+                )
             )
 
             if (sceneContainerFlags.flexiNotifsEnabled()) {
-                NotificationStackAppearanceViewBinder.bind(
-                    context,
-                    sharedNotificationContainer,
-                    notificationStackAppearanceViewModel,
-                    ambientState,
-                    controller,
+                disposableHandles.add(
+                    NotificationStackAppearanceViewBinder.bind(
+                        context,
+                        sharedNotificationContainer,
+                        notificationStackAppearanceViewModel,
+                        ambientState,
+                        controller,
+                    )
                 )
             }
-        }
-    }
 
-    @Composable
-    fun SceneScope.Notifications(modifier: Modifier = Modifier) {
+            isBound = true
+
+            onDispose {
+                disposableHandles.forEach { it.dispose() }
+                disposableHandles.clear()
+                isBound = false
+            }
+        }
+
+        if (!isBound) {
+            return
+        }
+
         NotificationStack(
             viewModel = viewModel,
             modifier = modifier,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
new file mode 100644
index 0000000..693de55
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.statusbar.notification.stack.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationsPlaceholderViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val underTest = kosmos.notificationsPlaceholderViewModel
+    @Test
+    fun onBoundsChanged_setsNotificationContainerBounds() {
+        underTest.onBoundsChanged(left = 5f, top = 5f, right = 5f, bottom = 5f)
+        assertThat(kosmos.keyguardInteractor.notificationContainerBounds.value)
+            .isEqualTo(NotificationContainerBounds(left = 5f, top = 5f, right = 5f, bottom = 5f))
+        assertThat(kosmos.notificationStackAppearanceInteractor.stackBounds.value)
+            .isEqualTo(NotificationContainerBounds(left = 5f, top = 5f, right = 5f, bottom = 5f))
+    }
+    @Test
+    fun onContentTopChanged_setsContentTop() {
+        underTest.onContentTopChanged(padding = 5f)
+        assertThat(kosmos.notificationStackAppearanceInteractor.contentTop.value).isEqualTo(5f)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
index c5dac77..80db535 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
@@ -19,14 +19,12 @@
 import android.app.smartspace.SmartspaceConfig
 import android.app.smartspace.SmartspaceManager
 import android.app.smartspace.SmartspaceSession
-import android.app.smartspace.SmartspaceTarget
 import android.content.Context
 import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB
 import com.android.systemui.smartspace.SmartspacePrecondition
 import com.android.systemui.smartspace.SmartspaceTargetFilter
@@ -64,11 +62,6 @@
     // A shadow copy of listeners is maintained to track whether the session should remain open.
     private var listeners = mutableSetOf<SmartspaceTargetListener>()
 
-    private var unfilteredListeners = mutableSetOf<SmartspaceTargetListener>()
-
-    // Smartspace can be used on multiple displays, such as when the user casts their screen
-    private var smartspaceViews = mutableSetOf<SmartspaceView>()
-
     var preconditionListener =
         object : SmartspacePrecondition.Listener {
             override fun onCriteriaChanged() {
@@ -101,9 +94,7 @@
         }
 
     private fun hasActiveSessionListeners(): Boolean {
-        return smartspaceViews.isNotEmpty() ||
-            listeners.isNotEmpty() ||
-            unfilteredListeners.isNotEmpty()
+        return listeners.isNotEmpty()
     }
 
     private fun connectSession() {
@@ -188,8 +179,4 @@
     private fun reloadSmartspace() {
         session?.requestSmartspaceUpdate()
     }
-
-    private fun onTargetsAvailableUnfiltered(targets: List<SmartspaceTarget>) {
-        unfilteredListeners.forEach { it.onSmartspaceTargetsUpdated(targets) }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 8df7e8b..9a4dfdd 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.scene.shared.model.Scene
@@ -115,4 +117,11 @@
 
     /** Creates a container that hosts the communal UI and handles gesture transitions. */
     fun createCommunalContainer(context: Context, viewModel: BaseCommunalViewModel): View
+
+    /** Creates a [View] that represents the Lockscreen. */
+    fun createLockscreen(
+        context: Context,
+        viewModel: LockscreenContentViewModel,
+        blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    ): View
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 7f43fac..abe49ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,6 +20,12 @@
 import android.content.Context
 import android.view.LayoutInflater
 import android.view.View
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.keyguard.KeyguardStatusView
 import com.android.keyguard.KeyguardStatusViewController
@@ -29,10 +35,13 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
 import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.keyguard.shared.ComposeLockscreen
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
@@ -44,6 +53,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
@@ -88,6 +98,8 @@
     private val falsingManager: FalsingManager,
     private val aodAlphaViewModel: AodAlphaViewModel,
     private val keyguardClockViewModel: KeyguardClockViewModel,
+    private val lockscreenContentViewModel: LockscreenContentViewModel,
+    private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -115,11 +127,28 @@
         initializeViews()
 
         if (!SceneContainerFlag.isEnabled) {
-            KeyguardBlueprintViewBinder.bind(
-                keyguardRootView,
-                keyguardBlueprintViewModel,
-                keyguardClockViewModel
-            )
+            if (ComposeLockscreen.isEnabled) {
+                val composeView =
+                    ComposeFacade.createLockscreen(
+                        context = context,
+                        viewModel = lockscreenContentViewModel,
+                        blueprints = lockscreenSceneBlueprintsLazy.get(),
+                    )
+                composeView.id = View.generateViewId()
+                val cs = ConstraintSet()
+                cs.clone(keyguardRootView)
+                cs.connect(composeView.id, START, PARENT_ID, START)
+                cs.connect(composeView.id, END, PARENT_ID, END)
+                cs.connect(composeView.id, TOP, PARENT_ID, TOP)
+                cs.connect(composeView.id, BOTTOM, PARENT_ID, BOTTOM)
+                keyguardRootView.addView(composeView)
+            } else {
+                KeyguardBlueprintViewBinder.bind(
+                    keyguardRootView,
+                    keyguardBlueprintViewModel,
+                    keyguardClockViewModel,
+                )
+            }
         }
         keyguardBlueprintCommandListener.start()
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/LockscreenSceneBlueprint.kt
similarity index 60%
copy from packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
copy to packages/SystemUI/src/com/android/systemui/keyguard/shared/model/LockscreenSceneBlueprint.kt
index 6d9cba4..2eafb83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/LockscreenSceneBlueprint.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.ui.composable.blueprint
+package com.android.systemui.keyguard.shared.model
 
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
-
-/** Defines interface for classes that can render the content for a specific blueprint/layout. */
+/**
+ * Defines interface for classes that can render the content for a specific blueprint/layout.
+ *
+ * The actual rendering is done by a compose-aware sub-interface.
+ */
 interface LockscreenSceneBlueprint {
-
     /** The ID that uniquely identifies this blueprint across all other blueprints. */
     val id: String
-
-    /** Renders the content of this blueprint. */
-    @Composable fun SceneScope.Content(modifier: Modifier)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index a651c10..52d94a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -57,7 +57,7 @@
     private val mainDispatcher: CoroutineDispatcher,
 ) : KeyguardSection() {
     private val placeHolderId = R.id.nssl_placeholder
-    private var disposableHandle: DisposableHandle? = null
+    private val disposableHandles: MutableList<DisposableHandle> = mutableListOf()
 
     /**
      * Align the notification placeholder bottom to the top of either the lock icon or the ambient
@@ -102,8 +102,9 @@
         if (!KeyguardShadeMigrationNssl.isEnabled) {
             return
         }
-        disposableHandle?.dispose()
-        disposableHandle =
+
+        disposeHandles()
+        disposableHandles.add(
             SharedNotificationContainerBinder.bind(
                 sharedNotificationContainer,
                 sharedNotificationContainerViewModel,
@@ -112,19 +113,28 @@
                 notificationStackSizeCalculator,
                 mainDispatcher,
             )
+        )
+
         if (sceneContainerFlags.flexiNotifsEnabled()) {
-            NotificationStackAppearanceViewBinder.bind(
-                context,
-                sharedNotificationContainer,
-                notificationStackAppearanceViewModel,
-                ambientState,
-                controller,
+            disposableHandles.add(
+                NotificationStackAppearanceViewBinder.bind(
+                    context,
+                    sharedNotificationContainer,
+                    notificationStackAppearanceViewModel,
+                    ambientState,
+                    controller,
+                )
             )
         }
     }
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
-        disposableHandle?.dispose()
+        disposeHandles()
         constraintLayout.removeView(placeHolderId)
     }
+
+    private fun disposeHandles() {
+        disposableHandles.forEach { it.dispose() }
+        disposableHandles.clear()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index d4ea728..9cf3c95 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -28,7 +28,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onStart
 
 /** Models UI state for the alpha of the AOD (always-on display). */
 @SysUISingleton
@@ -43,15 +42,13 @@
     /** The alpha level for the entire lockscreen while in AOD. */
     val alpha: Flow<Float> =
         combine(
-                keyguardTransitionInteractor.transitionValue(KeyguardState.GONE).onStart {
-                    emit(0f)
-                },
+                keyguardTransitionInteractor.currentKeyguardState,
                 merge(
                     keyguardInteractor.keyguardAlpha,
                     occludedToLockscreenTransitionViewModel.lockscreenAlpha,
                 )
-            ) { transitionToGone, alpha ->
-                if (transitionToGone == 1f) {
+            ) { currentKeyguardState, alpha ->
+                if (currentKeyguardState == KeyguardState.GONE) {
                     // Ensures content is not visible when in GONE state
                     0f
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index ba04fd3..f5e6135 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -67,6 +67,8 @@
             duration = 500.milliseconds,
             onStart = { 0f },
             onStep = { it },
+            onFinish = { 1f },
+            onCancel = { 1f },
         )
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 09e4e75..06ca3af 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2472,11 +2472,9 @@
             return 0;
         }
         if (!mKeyguardBypassController.getBypassEnabled()) {
-            if (migrateClocksToBlueprint()) {
-                View nsslPlaceholder = mView.getRootView().findViewById(R.id.nssl_placeholder);
-                if (!mSplitShadeEnabled && nsslPlaceholder != null) {
-                    return nsslPlaceholder.getTop();
-                }
+            if (migrateClocksToBlueprint() && !mSplitShadeEnabled) {
+                return (int) mKeyguardInteractor.getNotificationContainerBounds()
+                        .getValue().getTop();
             }
 
             return mClockPositionResult.stackScrollerPadding;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
new file mode 100644
index 0000000..c74c396
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.statusbar.notification.interruption
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.util.Log
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+
+// Class to track avalanche trigger event time.
+@SysUISingleton
+class AvalancheProvider
+@Inject
+constructor(
+        private val broadcastDispatcher: BroadcastDispatcher,
+        private val logger: VisualInterruptionDecisionLogger,
+) {
+    val TAG = "AvalancheProvider"
+    val timeoutMs = 120000
+    var startTime: Long = 0L
+
+    private val avalancheTriggerIntents = mutableSetOf(
+            Intent.ACTION_AIRPLANE_MODE_CHANGED,
+            Intent.ACTION_BOOT_COMPLETED,
+            Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
+            Intent.ACTION_USER_SWITCHED
+    )
+
+    private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            if (intent.action in avalancheTriggerIntents) {
+
+                // Ignore when airplane mode turned on
+                if (intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED
+                        && intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)) {
+                    Log.d(TAG, "broadcastReceiver: ignore airplane mode on")
+                    return
+                }
+                Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action)
+                startTime = System.currentTimeMillis()
+            }
+        }
+    }
+
+    fun register() {
+        val intentFilter = IntentFilter()
+        for (intent in avalancheTriggerIntents) {
+            intentFilter.addAction(intent)
+        }
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 8e82442..20c8add 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -16,7 +16,10 @@
 
 package com.android.systemui.statusbar.notification.interruption
 
+import android.app.Notification
 import android.app.Notification.BubbleMetadata
+import android.app.Notification.CATEGORY_EVENT
+import android.app.Notification.CATEGORY_REMINDER
 import android.app.Notification.VISIBILITY_PRIVATE
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_HIGH
@@ -224,3 +227,68 @@
     override fun shouldSuppress(entry: NotificationEntry) =
         keyguardNotificationVisibilityProvider.shouldHideNotification(entry)
 }
+
+
+class AvalancheSuppressor(
+    private val avalancheProvider: AvalancheProvider,
+    private val systemClock: SystemClock,
+) : VisualInterruptionFilter(
+        types = setOf(PEEK, PULSE),
+        reason = "avalanche",
+    ) {
+    val TAG = "AvalancheSuppressor"
+
+    enum class State {
+        ALLOW_CONVERSATION_AFTER_AVALANCHE,
+        ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME,
+        ALLOW_CALLSTYLE,
+        ALLOW_CATEGORY_REMINDER,
+        ALLOW_CATEGORY_EVENT,
+        ALLOW_FSI_WITH_PERMISSION_ON,
+        ALLOW_COLORIZED,
+        SUPPRESS
+    }
+
+    override fun shouldSuppress(entry: NotificationEntry): Boolean {
+        val timeSinceAvalanche = systemClock.currentTimeMillis() - avalancheProvider.startTime
+        val isActive = timeSinceAvalanche < avalancheProvider.timeoutMs
+        val state = allow(entry)
+        val suppress = isActive && state == State.SUPPRESS
+        reason = "avalanche suppress=$suppress isActive=$isActive state=$state"
+        return suppress
+    }
+
+    fun allow(entry: NotificationEntry): State  {
+        if (
+            entry.ranking.isConversation &&
+                entry.sbn.notification.`when` > avalancheProvider.startTime
+        ) {
+            return State.ALLOW_CONVERSATION_AFTER_AVALANCHE
+        }
+
+        if (entry.channel?.isImportantConversation == true) {
+            return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME
+        }
+
+        if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) {
+            return State.ALLOW_CALLSTYLE
+        }
+
+        if (entry.sbn.notification.category == CATEGORY_REMINDER) {
+            return State.ALLOW_CATEGORY_REMINDER
+        }
+
+        if (entry.sbn.notification.category == CATEGORY_EVENT) {
+            return State.ALLOW_CATEGORY_EVENT
+        }
+
+        if (entry.sbn.notification.fullScreenIntent != null) {
+            return State.ALLOW_FSI_WITH_PERMISSION_ON
+        }
+
+        if (entry.sbn.notification.isColorized) {
+            return State.ALLOW_COLORIZED
+        }
+        return State.SUPPRESS
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 6878a1e..dabb18b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
+import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.HeadsUpManager
@@ -45,22 +46,25 @@
 class VisualInterruptionDecisionProviderImpl
 @Inject
 constructor(
-    private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
-    private val batteryController: BatteryController,
-    deviceProvisionedController: DeviceProvisionedController,
-    private val eventLog: EventLog,
-    private val globalSettings: GlobalSettings,
-    private val headsUpManager: HeadsUpManager,
-    private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
-    keyguardStateController: KeyguardStateController,
-    private val logger: VisualInterruptionDecisionLogger,
-    @Main private val mainHandler: Handler,
-    private val powerManager: PowerManager,
-    private val statusBarStateController: StatusBarStateController,
-    private val systemClock: SystemClock,
-    private val uiEventLogger: UiEventLogger,
-    private val userTracker: UserTracker,
+        private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+        private val batteryController: BatteryController,
+        deviceProvisionedController: DeviceProvisionedController,
+        private val eventLog: EventLog,
+        private val globalSettings: GlobalSettings,
+        private val headsUpManager: HeadsUpManager,
+        private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
+        keyguardStateController: KeyguardStateController,
+        private val logger: VisualInterruptionDecisionLogger,
+        @Main private val mainHandler: Handler,
+        private val powerManager: PowerManager,
+        private val statusBarStateController: StatusBarStateController,
+        private val systemClock: SystemClock,
+        private val uiEventLogger: UiEventLogger,
+        private val userTracker: UserTracker,
+        private val avalancheProvider: AvalancheProvider
+
 ) : VisualInterruptionDecisionProvider {
+
     init {
         check(!VisualInterruptionRefactor.isUnexpectedlyInLegacyMode())
     }
@@ -166,6 +170,10 @@
         addFilter(HunJustLaunchedFsiSuppressor())
         addFilter(AlertKeyguardVisibilitySuppressor(keyguardNotificationVisibilityProvider))
 
+        if (NotificationAvalancheSuppression.isEnabled) {
+            addFilter(AvalancheSuppressor(avalancheProvider, systemClock))
+            avalancheProvider.register()
+        }
         started = true
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
index ee79727..2f80c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -85,7 +85,7 @@
 /** A reason why visual interruptions might be suppressed based on the notification. */
 abstract class VisualInterruptionFilter(
     override val types: Set<VisualInterruptionType>,
-    override val reason: String,
+    override var reason: String,
     override val uiEventId: UiEventEnum? = null,
     override val eventLogData: EventLogData? = null
 ) : VisualInterruptionSuppressor {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index 6c2cbbe..50b08b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
 import kotlin.math.roundToInt
+import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.launch
 
 /** Binds the shared notification container to its view-model. */
@@ -38,8 +39,8 @@
         viewModel: NotificationStackAppearanceViewModel,
         ambientState: AmbientState,
         controller: NotificationStackScrollLayoutController,
-    ) {
-        view.repeatWhenAttached {
+    ): DisposableHandle {
+        return view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
                     viewModel.stackBounds.collect { bounds ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index a436f17..8b723da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
@@ -40,9 +42,11 @@
     shadeInteractor: ShadeInteractor,
     flags: SceneContainerFlags,
     featureFlags: FeatureFlagsClassic,
+    private val keyguardInteractor: KeyguardInteractor,
 ) {
     /** DEBUG: whether the placeholder "Notifications" text should be shown. */
-    val isPlaceholderTextVisible: Boolean = !flags.flexiNotifsEnabled()
+    val isPlaceholderTextVisible: Boolean =
+        !flags.flexiNotifsEnabled() && SceneContainerFlag.isEnabled
 
     /** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
     val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
@@ -64,7 +68,10 @@
         right: Float,
         bottom: Float,
     ) {
-        interactor.setStackBounds(NotificationContainerBounds(left, top, right, bottom))
+        val notificationContainerBounds =
+            NotificationContainerBounds(top = top, bottom = bottom, left = left, right = right)
+        keyguardInteractor.setNotificationContainerBounds(notificationContainerBounds)
+        interactor.setStackBounds(notificationContainerBounds)
     }
 
     /** The corner radius of the placeholder, in dp. */
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 25a7eb8..9517f82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -46,6 +46,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.After
 import org.junit.Assert.assertThrows
 import org.junit.Assume.assumeTrue
 import org.junit.Before
@@ -118,6 +119,11 @@
         initAndAttachContainerView()
     }
 
+    @After
+    fun tearDown() {
+        ViewUtils.detachView(parentView)
+    }
+
     @Test
     fun isEnabled_communalEnabled_returnsTrue() {
         communalRepository.setIsCommunalEnabled(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index da68d9c..5410864 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.notification.interruption
 
+import android.app.Notification.CATEGORY_EVENT
+import android.app.Notification.CATEGORY_REMINDER
+import android.app.NotificationManager
 import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -47,6 +50,7 @@
             systemClock,
             uiEventLogger,
             userTracker,
+            avalancheProvider
         )
     }
 
@@ -70,6 +74,114 @@
         }
     }
 
+    // Avalanche tests are in VisualInterruptionDecisionProviderImplTest
+    // instead of VisualInterruptionDecisionProviderTestBase
+    // because avalanche code is based on the suppression refactor.
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowConversationFromAfterEvent() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isConversation = true
+                isImportantConversation = false
+                whenMs = whenAgo(5)
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_suppressConversationFromBeforeEvent() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldNotHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_DEFAULT
+                isConversation = true
+                isImportantConversation = false
+                whenMs = whenAgo(15)
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowHighPriorityConversation() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isImportantConversation = true
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCall() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isCall = true
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCategoryReminder() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                category = CATEGORY_REMINDER
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCategoryEvent() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                category = CATEGORY_EVENT
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowFsi() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            assertFsiNotSuppressed()
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowColorized() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isColorized = true
+            })
+        }
+    }
+
     @Test
     fun testPeekCondition_suppressesOnlyPeek() {
         withCondition(TestCondition(types = setOf(PEEK)) { true }) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 2ac0cb7..f89b9cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -19,7 +19,10 @@
 import android.app.ActivityManager
 import android.app.Notification
 import android.app.Notification.BubbleMetadata
+import android.app.Notification.EXTRA_COLORIZED
+import android.app.Notification.EXTRA_TEMPLATE
 import android.app.Notification.FLAG_BUBBLE
+import android.app.Notification.FLAG_CAN_COLORIZE
 import android.app.Notification.FLAG_FOREGROUND_SERVICE
 import android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED
 import android.app.Notification.FLAG_USER_INITIATED_JOB
@@ -50,6 +53,8 @@
 import com.android.internal.logging.UiEventLogger.UiEventEnum
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.broadcast.FakeBroadcastDispatcher
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogcatEchoTracker
 import com.android.systemui.log.core.LogLevel
@@ -122,6 +127,7 @@
     protected val systemClock = FakeSystemClock()
     protected val uiEventLogger = UiEventLoggerFake()
     protected val userTracker = FakeUserTracker()
+    protected val avalancheProvider: AvalancheProvider = mock()
 
     protected abstract val provider: VisualInterruptionDecisionProvider
 
@@ -1097,6 +1103,8 @@
         var whenMs: Long? = null
         var isGrouped = false
         var isGroupSummary = false
+        var isCall = false
+        var category: String? = null
         var groupAlertBehavior: Int? = null
         var hasBubbleMetadata = false
         var hasFsi = false
@@ -1106,10 +1114,12 @@
         var isUserInitiatedJob = false
         var isBubble = false
         var isStickyAndNotDemoted = false
+        var isColorized = false
 
         // Set on NotificationEntryBuilder:
         var importance = IMPORTANCE_DEFAULT
         var canBubble: Boolean? = null
+        var isImportantConversation = false
 
         // Set on NotificationEntry:
         var hasJustLaunchedFsi = false
@@ -1118,6 +1128,7 @@
         var packageSuspended = false
         var visibilityOverride: Int? = null
         var suppressedVisualEffects: Int? = null
+        var isConversation = false
 
         private fun buildBubbleMetadata(): BubbleMetadata {
             val builder =
@@ -1158,6 +1169,13 @@
                         nb.setGroupSummary(true)
                     }
 
+                    if (isCall) {
+                        nb.extras.putString(EXTRA_TEMPLATE, Notification.CallStyle::class.java.name)
+                    }
+
+                    if (category != null) {
+                        nb.setCategory(category)
+                    }
                     groupAlertBehavior?.let { nb.setGroupAlertBehavior(it) }
 
                     if (hasBubbleMetadata) {
@@ -1185,6 +1203,10 @@
                     if (isStickyAndNotDemoted) {
                         n.flags = n.flags or FLAG_FSI_REQUESTED_BUT_DENIED
                     }
+                    if (isColorized) {
+                        n.extras.putBoolean(EXTRA_COLORIZED, true)
+                        n.flags = n.flags or FLAG_CAN_COLORIZE
+                    }
                 }
                 .let { NotificationEntryBuilder().setNotification(it) }
                 .also { neb ->
@@ -1193,9 +1215,10 @@
                     neb.setTag(TEST_TAG)
 
                     neb.setImportance(importance)
-                    neb.setChannel(
-                        NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
-                    )
+                    val channel =
+                            NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
+                    channel.isImportantConversation = isImportantConversation
+                    neb.setChannel(channel)
 
                     canBubble?.let { neb.setCanBubble(it) }
                 }
@@ -1216,6 +1239,7 @@
                             }
                             visibilityOverride?.let { mrb.setVisibilityOverride(it) }
                             suppressedVisualEffects?.let { mrb.setSuppressedVisualEffects(it) }
+                            mrb.setIsConversation(isConversation)
                         }
                         .build()
                 }
@@ -1287,7 +1311,7 @@
         }
     }
 
-    private fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs
+    protected fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs
 }
 
 private const val TEST_CONTENT_TITLE = "Test Content Title"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
index 0356c2c..620ad9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
@@ -19,6 +19,7 @@
 import android.os.Handler
 import android.os.PowerManager
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
@@ -49,7 +50,8 @@
         statusBarStateController: StatusBarStateController,
         systemClock: SystemClock,
         uiEventLogger: UiEventLogger,
-        userTracker: UserTracker
+        userTracker: UserTracker,
+        avalancheProvider: AvalancheProvider
     ): VisualInterruptionDecisionProvider {
         return if (VisualInterruptionRefactor.isEnabled) {
             VisualInterruptionDecisionProviderImpl(
@@ -67,7 +69,8 @@
                 statusBarStateController,
                 systemClock,
                 uiEventLogger,
-                userTracker
+                userTracker,
+                avalancheProvider
             )
         } else {
             NotificationInterruptStateProviderWrapper(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 849a13b..b048949 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -153,6 +153,7 @@
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.AvalancheProvider;
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
@@ -313,6 +314,7 @@
     @Mock private CameraLauncher mCameraLauncher;
     @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
     @Mock private UserTracker mUserTracker;
+    @Mock private AvalancheProvider mAvalancheProvider;
     @Mock private FingerprintManager mFingerprintManager;
     @Mock IPowerManager mPowerManagerService;
     @Mock ActivityStarter mActivityStarter;
@@ -367,7 +369,8 @@
                         mStatusBarStateController,
                         mFakeSystemClock,
                         mock(UiEventLogger.class),
-                        mUserTracker);
+                        mUserTracker,
+                        mAvalancheProvider);
         mVisualInterruptionDecisionProvider.start();
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 3bde6e3..99c2dc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -315,6 +315,8 @@
 
     @After
     public void tearDown() {
+        // Detaching view stops flow collection and prevents memory leak.
+        ViewUtils.detachView(mScrimBehind);
         finishAnimationsImmediately();
         Arrays.stream(ScrimState.values()).forEach((scrim) -> {
             scrim.setAodFrontScrimAlpha(0f);
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 f3e9203..f4b36659 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -96,6 +96,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
@@ -151,6 +152,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.interruption.AvalancheProvider;
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionLogger;
@@ -587,7 +589,9 @@
                         mock(StatusBarStateController.class),
                         mock(SystemClock.class),
                         mock(UiEventLogger.class),
-                        mock(UserTracker.class));
+                        mock(UserTracker.class),
+                        mock(AvalancheProvider.class)
+                        );
         interruptionDecisionProvider.start();
 
         mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index d7e948e..29faa58 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
 import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.scene.shared.flag.sceneContainerFlags
@@ -29,5 +30,6 @@
         shadeInteractor = shadeInteractor,
         flags = sceneContainerFlags,
         featureFlags = featureFlagsClassic,
+        keyguardInteractor = keyguardInteractor,
     )
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 687def0..a61199a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1392,6 +1392,11 @@
     }
 
     @Override // Binder call
+    public int getMousePointerSpeed() {
+        return mNative.getMousePointerSpeed();
+    }
+
+    @Override // Binder call
     public void tryPointerSpeed(int speed) {
         if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
                 "tryPointerSpeed()")) {
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index e5f3484..b16df0f 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -119,6 +119,8 @@
      */
     boolean transferTouch(IBinder destChannelToken, int displayId);
 
+    int getMousePointerSpeed();
+
     void setPointerSpeed(int speed);
 
     void setMousePointerAccelerationEnabled(int displayId, boolean enabled);
@@ -364,6 +366,9 @@
         public native boolean transferTouch(IBinder destChannelToken, int displayId);
 
         @Override
+        public native int getMousePointerSpeed();
+
+        @Override
         public native void setPointerSpeed(int speed);
 
         @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fff4939..95189ab 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3732,6 +3732,37 @@
                             return InputBindResult.INVALID_DISPLAY_ID;
                     }
 
+                    // In case mShowForced flag affects the next client to keep IME visible, when
+                    // the current client is leaving due to the next focused client, we clear
+                    // mShowForced flag when the next client's targetSdkVersion is T or higher.
+                    final boolean shouldClearFlag =
+                            mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.mUid);
+                    final boolean showForced = mVisibilityStateComputer.mShowForced;
+                    if (mCurFocusedWindow != windowToken && showForced && shouldClearFlag) {
+                        mVisibilityStateComputer.mShowForced = false;
+                    }
+
+                    // Verify if caller is a background user.
+                    final int currentUserId = mSettings.getUserId();
+                    if (userId != currentUserId) {
+                        if (ArrayUtils.contains(
+                                mUserManagerInternal.getProfileIds(currentUserId, false), userId)) {
+                            // cross-profile access is always allowed here to allow
+                            // profile-switching.
+                            scheduleSwitchUserTaskLocked(userId, cs.mClient);
+                            return InputBindResult.USER_SWITCHING;
+                        }
+                        Slog.w(TAG, "A background user is requesting window. Hiding IME.");
+                        Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
+                                + " a background user, use EditorInfo.targetInputMethodUser with"
+                                + " INTERACT_ACROSS_USERS_FULL permission.");
+                        hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
+                                0 /* flags */,
+                                null /* resultReceiver */,
+                                SoftInputShowHideReason.HIDE_INVALID_USER);
+                        return InputBindResult.INVALID_USER;
+                    }
+
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
                             client, windowToken, startInputFlags, softInputMode, windowFlags,
                             editorInfo, inputConnection, remoteAccessibilityInputConnection,
@@ -3781,32 +3812,6 @@
                     + " cs=" + cs);
         }
 
-        final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.mUid);
-        // In case mShowForced flag affects the next client to keep IME visible, when the current
-        // client is leaving due to the next focused client, we clear mShowForced flag when the
-        // next client's targetSdkVersion is T or higher.
-        final boolean showForced = mVisibilityStateComputer.mShowForced;
-        if (mCurFocusedWindow != windowToken && showForced && shouldClearFlag) {
-            mVisibilityStateComputer.mShowForced = false;
-        }
-
-        final int currentUserId = mSettings.getUserId();
-        if (userId != currentUserId) {
-            if (ArrayUtils.contains(
-                    mUserManagerInternal.getProfileIds(currentUserId, false), userId)) {
-                // cross-profile access is always allowed here to allow profile-switching.
-                scheduleSwitchUserTaskLocked(userId, cs.mClient);
-                return InputBindResult.USER_SWITCHING;
-            }
-            Slog.w(TAG, "A background user is requesting window. Hiding IME.");
-            Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
-                    + " a background user, use EditorInfo.targetInputMethodUser with"
-                    + " INTERACT_ACROSS_USERS_FULL permission.");
-            hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
-                    null /* resultReceiver */, SoftInputShowHideReason.HIDE_INVALID_USER);
-            return InputBindResult.INVALID_USER;
-        }
-
         final boolean sameWindowFocused = mCurFocusedWindow == windowToken;
         final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
         final boolean startInputByWinGainedFocus =
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a06607b..7fb3e00 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -93,7 +93,6 @@
 import android.os.IBinder;
 import android.os.IProgressListener;
 import android.os.Process;
-import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -139,11 +138,11 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.ILockSettings;
-import com.android.internal.widget.ILockSettingsStateListener;
 import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
 import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockSettingsStateListener;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.RebootEscrowListener;
 import com.android.internal.widget.VerifyCredentialResponse;
@@ -185,6 +184,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -332,8 +332,8 @@
 
     private HashMap<UserHandle, UserManager> mUserManagerCache = new HashMap<>();
 
-    private final RemoteCallbackList<ILockSettingsStateListener> mLockSettingsStateListeners =
-            new RemoteCallbackList<>();
+    private final CopyOnWriteArrayList<LockSettingsStateListener> mLockSettingsStateListeners =
+            new CopyOnWriteArrayList<>();
 
     // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
     // devices. The most basic of these is to show/hide notifications about missing features until
@@ -2379,25 +2379,12 @@
     }
 
     private void notifyLockSettingsStateListeners(boolean success, int userId) {
-        int i = mLockSettingsStateListeners.beginBroadcast();
-        try {
-            while (i > 0) {
-                i--;
-                try {
-                    if (success) {
-                        mLockSettingsStateListeners.getBroadcastItem(i)
-                                .onAuthenticationSucceeded(userId);
-                    } else {
-                        mLockSettingsStateListeners.getBroadcastItem(i)
-                                .onAuthenticationFailed(userId);
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Exception while notifying LockSettingsStateListener:"
-                            + " success = " + success + ", userId = " + userId, e);
-                }
+        for (LockSettingsStateListener listener : mLockSettingsStateListeners) {
+            if (success) {
+                listener.onAuthenticationSucceeded(userId);
+            } else {
+                listener.onAuthenticationFailed(userId);
             }
-        } finally {
-            mLockSettingsStateListeners.finishBroadcast();
         }
     }
 
@@ -3720,15 +3707,15 @@
         }
 
         @Override
-        public void registerLockSettingsStateListener(
-                @NonNull ILockSettingsStateListener listener) {
-            mLockSettingsStateListeners.register(listener);
+        public void registerLockSettingsStateListener(@NonNull LockSettingsStateListener listener) {
+            Objects.requireNonNull(listener, "listener cannot be null");
+            mLockSettingsStateListeners.add(listener);
         }
 
         @Override
         public void unregisterLockSettingsStateListener(
-                @NonNull ILockSettingsStateListener listener) {
-            mLockSettingsStateListeners.unregister(listener);
+                @NonNull LockSettingsStateListener listener) {
+            mLockSettingsStateListeners.remove(listener);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 0bbad85..dc97e5f 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -819,6 +819,7 @@
      * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple
      * launcher activities, only one of the icons is returned arbitrarily.
      */
+    @Nullable
     public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user,
             String callingPackageName) {
         Objects.requireNonNull(packageName);
@@ -844,7 +845,7 @@
         // In the rare case the archived app defined more than two launcher activities, we choose
         // the first one arbitrarily.
         Bitmap icon = decodeIcon(archiveState.getActivityInfos().get(0));
-        if (getAppOpsManager().checkOp(
+        if (icon != null && getAppOpsManager().checkOp(
                 AppOpsManager.OP_ARCHIVE_ICON_OVERLAY, callingUid, callingPackageName)
                 == MODE_ALLOWED) {
             icon = includeCloudOverlay(icon);
@@ -900,6 +901,7 @@
         return bitmap;
     }
 
+    @Nullable
     Bitmap includeCloudOverlay(Bitmap bitmap) {
         Drawable cloudDrawable =
                 mContext.getResources()
@@ -920,7 +922,9 @@
         final int iconSize = mContext.getSystemService(
                 ActivityManager.class).getLauncherLargeIconSize();
         Bitmap appIconWithCloudOverlay = drawableToBitmap(layerDrawable, iconSize);
-        bitmap.recycle();
+        if (bitmap != null) {
+            bitmap.recycle();
+        }
         return appIconWithCloudOverlay;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 716aee3..e743172 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5150,6 +5150,15 @@
 
     /** @return the orientation of the display when it's rotation is ROTATION_0. */
     int getNaturalOrientation() {
+        return mBaseDisplayWidth <= mBaseDisplayHeight
+                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+    }
+
+    /**
+     * Returns the orientation which is used for app's Configuration (excluding decor insets) when
+     * the display rotation is ROTATION_0.
+     */
+    int getNaturalConfigurationOrientation() {
         final Configuration config = getConfiguration();
         if (config.windowConfiguration.getDisplayRotation() == ROTATION_0) {
             return config.orientation;
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 34d7651..4204670 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -132,7 +132,7 @@
     void show(SurfaceControl.Transaction t, WindowContainer w) {
         t.show(mInputSurface);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
-        t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
+        t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1 + w.getChildCount());
     }
 
     void show(SurfaceControl.Transaction t, int layer) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2d2857a..80894b2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1589,7 +1589,7 @@
         if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
             // NOSENSOR means the display's "natural" orientation, so return that.
             if (mDisplayContent != null) {
-                return mDisplayContent.getNaturalOrientation();
+                return mDisplayContent.getNaturalConfigurationOrientation();
             }
         } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
             // LOCKED means the activity's orientation remains unchanged, so return existing value.
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index cc08488..df7fb99 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -31,5 +31,8 @@
 per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
 per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
 
+# Memory
+per-file com_android_server_am_OomConnection.cpp = file:/MEMORY_OWNERS
+
 # Bug component : 158088 = per-file *AnrTimer*
 per-file *AnrTimer* = file:/PERFORMANCE_OWNERS
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 4a6b31c..810090a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -285,6 +285,7 @@
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiLightsOut(bool lightsOut);
     void setPointerDisplayId(int32_t displayId);
+    int32_t getMousePointerSpeed();
     void setPointerSpeed(int32_t speed);
     void setMousePointerAccelerationEnabled(int32_t displayId, bool enabled);
     void setTouchpadPointerSpeed(int32_t speed);
@@ -1219,6 +1220,11 @@
     }
 }
 
+int32_t NativeInputManager::getMousePointerSpeed() {
+    std::scoped_lock _l(mLock);
+    return mLocked.pointerSpeed;
+}
+
 void NativeInputManager::setPointerSpeed(int32_t speed) {
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -2211,6 +2217,12 @@
     }
 }
 
+static jint nativeGetMousePointerSpeed(JNIEnv* env, jobject nativeImplObj) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+
+    return static_cast<jint>(im->getMousePointerSpeed());
+}
+
 static void nativeSetPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
@@ -2865,6 +2877,7 @@
         {"transferTouchFocus", "(Landroid/os/IBinder;Landroid/os/IBinder;Z)Z",
          (void*)nativeTransferTouchFocus},
         {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouch},
+        {"getMousePointerSpeed", "()I", (void*)nativeGetMousePointerSpeed},
         {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed},
         {"setMousePointerAccelerationEnabled", "(IZ)V",
          (void*)nativeSetMousePointerAccelerationEnabled},
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 5081198..7053597 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -39,7 +39,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.PropertyInvalidatedCache;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -49,8 +48,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.widget.ILockSettingsStateListener;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockSettingsStateListener;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 
@@ -412,7 +411,7 @@
         mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
-        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
+        final LockSettingsStateListener listener = mock(LockSettingsStateListener.class);
         mLocalService.registerLockSettingsStateListener(listener);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
@@ -429,7 +428,7 @@
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
         final LockscreenCredential badPassword = newPassword("badPassword");
-        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
+        final LockSettingsStateListener listener = mock(LockSettingsStateListener.class);
         mLocalService.registerLockSettingsStateListener(listener);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
@@ -445,7 +444,7 @@
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
         final LockscreenCredential badPassword = newPassword("badPassword");
-        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
+        final LockSettingsStateListener listener = mock(LockSettingsStateListener.class);
 
         mLocalService.registerLockSettingsStateListener(listener);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
@@ -599,12 +598,4 @@
             assertNotEquals(0, mGateKeeperService.getSecureUserId(userId));
         }
     }
-
-    private ILockSettingsStateListener mockLockSettingsStateListener() {
-        ILockSettingsStateListener listener = mock(ILockSettingsStateListener.Stub.class);
-        IBinder binder = mock(IBinder.class);
-        when(binder.isBinderAlive()).thenReturn(true);
-        when(listener.asBinder()).thenReturn(binder);
-        return listener;
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 95850ac..f63ff6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1003,7 +1003,13 @@
         dc.computeScreenConfiguration(config, ROTATION_0);
         dc.onRequestedOverrideConfigurationChanged(config);
         assertEquals(Configuration.ORIENTATION_LANDSCAPE, config.orientation);
-        assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalOrientation());
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalConfigurationOrientation());
+        window.setOverrideOrientation(SCREEN_ORIENTATION_NOSENSOR);
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE,
+                window.getRequestedConfigurationOrientation());
+        // Note that getNaturalOrientation is based on logical display size. So it is portrait if
+        // the display width equals to height.
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, dc.getNaturalOrientation());
     }
 
     @Test
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
index b57b7c7..bb2ac51 100644
--- a/telecomm/OWNERS
+++ b/telecomm/OWNERS
@@ -6,4 +6,5 @@
 rgreenwalt@google.com
 grantmenke@google.com
 pmadapurmath@google.com
-tjstuart@google.com
\ No newline at end of file
+tjstuart@google.com
+huiwang@google.com
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index eb7e67d..1749545 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1927,34 +1927,25 @@
      * Then for SDK 35+, if the caller identity is personal profile, then this will return
      * subscription 1 only and vice versa.
      *
-     * <p> The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
-     * {@link SubscriptionInfo#getSubscriptionId}.
+     * <p> Returned records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+     * {@link SubscriptionInfo#getSubscriptionId}. Beginning with Android SDK 35, this method will
+     * never return null.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see
      * {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
-     * <ul>
-     * <li>
-     * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener}
-     * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be
-     * invoked in the future.
-     * </li>
-     * <li>
-     * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
-     * </li>
-     * <li>
-     * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
-     * then by {@link SubscriptionInfo#getSubscriptionId}.
-     * </li>
-     * </ul>
+     * @return a list of the active {@link SubscriptionInfo} that is visible to the caller. If
+     *         an empty list or null is returned, then there are no active subscriptions that
+     *         are visible to the caller. If the number of active subscriptions available to
+     *         any caller changes, then this change will be indicated by
+     *         {@link OnSubscriptionsChangedListener#onSubscriptionsChanged}.
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *         {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
+    public @Nullable List<SubscriptionInfo> getActiveSubscriptionInfoList() {
         List<SubscriptionInfo> activeList = null;
 
         try {
@@ -1970,6 +1961,8 @@
         if (activeList != null) {
             activeList = activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo))
                     .collect(Collectors.toList());
+        } else {
+            activeList = Collections.emptyList();
         }
         return activeList;
     }
@@ -1998,12 +1991,7 @@
      *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() {
-        List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList(
-                /* userVisibleonly */false);
-        if (completeList == null) {
-            completeList = new ArrayList<>();
-        }
-        return completeList;
+        return getActiveSubscriptionInfoList(/* userVisibleonly */ false);
     }
 
     /**
@@ -2032,7 +2020,7 @@
     *
     * @hide
     */
-    public @Nullable List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+    public @NonNull List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
         List<SubscriptionInfo> activeList = null;
 
         try {
@@ -2045,11 +2033,13 @@
             // ignore it
         }
 
-        if (!userVisibleOnly || activeList == null) {
-            return activeList;
-        } else {
+        if (activeList == null || activeList.isEmpty()) {
+            return Collections.emptyList();
+        } else if (userVisibleOnly) {
             return activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo))
                     .collect(Collectors.toList());
+        } else {
+            return activeList;
         }
     }
 
@@ -2086,7 +2076,7 @@
      * @hide
      */
     @SystemApi
-    public List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
+    public @Nullable List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
         try {
@@ -2098,7 +2088,7 @@
         } catch (RemoteException ex) {
             // ignore it
         }
-        return result;
+        return (result == null) ? Collections.emptyList() : result;
     }
 
     /**
@@ -2128,7 +2118,7 @@
      * @throws UnsupportedOperationException If the device does not have
      *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
-    public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
+    public @Nullable List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
         try {
@@ -2139,7 +2129,7 @@
         } catch (RemoteException ex) {
             // ignore it
         }
-        return result;
+        return (result == null) ? Collections.emptyList() : result;
     }
 
     /**