Merge changes from topic "desktoo-immersive-handler" into main

* changes:
  Add DesktopFullImmersiveTransitionHandler
  Disable App Header drag-move and drag-resize in full immersive
diff --git a/core/api/current.txt b/core/api/current.txt
index b740ef3..53da338 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -61658,6 +61658,7 @@
     method public void unregisterOnBackInvokedCallback(@NonNull android.window.OnBackInvokedCallback);
     field public static final int PRIORITY_DEFAULT = 0; // 0x0
     field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240
+    field @FlaggedApi("com.android.window.flags.predictive_back_priority_system_navigation_observer") public static final int PRIORITY_SYSTEM_NAVIGATION_OBSERVER = -2; // 0xfffffffe
   }
 
   public interface SplashScreen {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index e043a5d..a458b4e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1980,6 +1980,7 @@
     public void registerAllResourcesReference(@NonNull Resources resources) {
         if (android.content.res.Flags.registerResourcePaths()) {
             synchronized (mLock) {
+                cleanupReferences(mAllResourceReferences, mAllResourceReferencesQueue);
                 mAllResourceReferences.add(
                         new WeakReference<>(resources, mAllResourceReferencesQueue));
             }
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index b9eba9c..ce8661e 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -1028,6 +1028,9 @@
                     // Camera is already closed, so nothing left to do
                     if (DEBUG) Log.v(TAG, mIdString +
                             "Camera was already closed or busy, skipping unconfigure");
+                } catch (SecurityException e) {
+                    // UID state change revoked camera permission
+                    Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
                 }
             }
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1919f77..1a15d09 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -17813,12 +17813,6 @@
         public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT =
                 "force_non_debuggable_final_build_for_compat";
 
-        /**
-         * Flag to enable the use of ApplicationInfo for getting not-launched status.
-         *
-         * @hide
-         */
-        public static final String ENABLE_USE_APP_INFO_NOT_LAUNCHED = "use_app_info_not_launched";
 
         /**
          * Current version of signed configuration applied.
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index a86c961..aedf8e0 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -106,3 +106,10 @@
     description: "Clear StrongAuth on add credential"
     bug: "320817991"
 }
+
+flag {
+    name: "afl_api"
+    namespace: "platform_security"
+    description: "AFL feature"
+    bug: "365994454"
+}
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index de2c6f77..afff8fe 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -28,8 +29,10 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.SmsMessage;
 
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.Preconditions;
 
 import java.util.List;
@@ -91,8 +94,12 @@
         mOnServiceReadyCallback = onServiceReadyCallback;
         mServiceReadyCallbackExecutor = executor;
         mContext = context;
-        return context.bindService(intent, mCarrierMessagingServiceConnection,
-                Context.BIND_AUTO_CREATE);
+        return Flags.supportCarrierServicesForHsum()
+                ? context.bindServiceAsUser(intent, mCarrierMessagingServiceConnection,
+                Context.BIND_AUTO_CREATE,
+                UserHandle.of(ActivityManager.getCurrentUser()))
+                : context.bindService(intent, mCarrierMessagingServiceConnection,
+                        Context.BIND_AUTO_CREATE);
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8fb17c7..0ca442d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7531,6 +7531,7 @@
                         if (keyEvent.isCanceled()) {
                             animationCallback.onBackCancelled();
                         } else {
+                            dispatcher.tryInvokeSystemNavigationObserverCallback();
                             topCallback.onBackInvoked();
                         }
                         break;
@@ -7538,6 +7539,7 @@
             } else if (topCallback != null) {
                 if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
                     if (!keyEvent.isCanceled()) {
+                        dispatcher.tryInvokeSystemNavigationObserverCallback();
                         topCallback.onBackInvoked();
                     } else {
                         Log.d(mTag, "Skip onBackInvoked(), reason: keyEvent.isCanceled=true");
diff --git a/core/java/android/window/OnBackInvokedDispatcher.java b/core/java/android/window/OnBackInvokedDispatcher.java
index 0632a37..02ed57d 100644
--- a/core/java/android/window/OnBackInvokedDispatcher.java
+++ b/core/java/android/window/OnBackInvokedDispatcher.java
@@ -16,11 +16,14 @@
 
 package android.window;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 
+import com.android.window.flags.Flags;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -43,6 +46,7 @@
     @IntDef({
             PRIORITY_DEFAULT,
             PRIORITY_OVERLAY,
+            PRIORITY_SYSTEM_NAVIGATION_OBSERVER,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface Priority{}
@@ -67,6 +71,20 @@
     int PRIORITY_SYSTEM = -1;
 
     /**
+     * Priority level of {@link OnBackInvokedCallback}s designed to observe system-level back
+     * handling.
+     *
+     * <p>Callbacks registered with this priority do not consume back events. They receive back
+     * events whenever the system handles a back navigation and have no impact on the normal back
+     * navigation flow. Useful for logging or analytics.
+     *
+     * <p>Only one callback with {@link #PRIORITY_SYSTEM_NAVIGATION_OBSERVER} can be registered at a
+     * time.
+     */
+    @FlaggedApi(Flags.FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER)
+    int PRIORITY_SYSTEM_NAVIGATION_OBSERVER = -2;
+
+    /**
      * Registers a {@link OnBackInvokedCallback}.
      *
      * Within the same priority level, callbacks are invoked in the reverse order in which
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 56c05b2..dfc4a58 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -95,7 +97,7 @@
         synchronized (mLock) {
             mCallbacks.add(Pair.create(callback, priority));
             if (mActualDispatcher != null) {
-                if (priority <= PRIORITY_SYSTEM) {
+                if (priority == PRIORITY_SYSTEM) {
                     mActualDispatcher.registerSystemOnBackInvokedCallback(callback);
                 } else {
                     mActualDispatcher.registerOnBackInvokedCallback(priority, callback);
@@ -123,10 +125,19 @@
         }
         for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
             int priority = callbackPair.second;
-            if (priority >= PRIORITY_DEFAULT) {
-                mActualDispatcher.registerOnBackInvokedCallback(priority, callbackPair.first);
+            if (predictiveBackPrioritySystemNavigationObserver()) {
+                if (priority >= PRIORITY_DEFAULT
+                        || priority == PRIORITY_SYSTEM_NAVIGATION_OBSERVER) {
+                    mActualDispatcher.registerOnBackInvokedCallback(priority, callbackPair.first);
+                } else {
+                    mActualDispatcher.registerSystemOnBackInvokedCallback(callbackPair.first);
+                }
             } else {
-                mActualDispatcher.registerSystemOnBackInvokedCallback(callbackPair.first);
+                if (priority >= PRIORITY_DEFAULT) {
+                    mActualDispatcher.registerOnBackInvokedCallback(priority, callbackPair.first);
+                } else {
+                    mActualDispatcher.registerSystemOnBackInvokedCallback(callbackPair.first);
+                }
             }
         }
         mCallbacks.clear();
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index bb89a24..51bc7d5 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -103,6 +105,9 @@
     public final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
             mOnBackInvokedCallbacks = new TreeMap<>();
 
+    @VisibleForTesting
+    public OnBackInvokedCallback mSystemNavigationObserverCallback = null;
+
     private Checker mChecker;
     private final Object mLock = new Object();
     // The threshold for back swipe full progress.
@@ -170,6 +175,20 @@
         }
     }
 
+    private void registerSystemNavigationObserverCallback(@NonNull OnBackInvokedCallback callback) {
+        synchronized (mLock) {
+            // If callback has already been added as regular callback, remove it.
+            if (mAllCallbacks.containsKey(callback)) {
+                if (DEBUG) {
+                    Log.i(TAG, "Callback already added. Removing and re-adding it as "
+                            + "system-navigation-observer-callback.");
+                }
+                removeCallbackInternal(callback);
+            }
+            mSystemNavigationObserverCallback = callback;
+        }
+    }
+
     /**
      * Register a callback bypassing platform checks. This is used to register compatibility
      * callbacks.
@@ -181,6 +200,12 @@
                 mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
                 return;
             }
+            if (predictiveBackPrioritySystemNavigationObserver()) {
+                if (priority == PRIORITY_SYSTEM_NAVIGATION_OBSERVER) {
+                    registerSystemNavigationObserverCallback(callback);
+                    return;
+                }
+            }
             if (callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) {
                 if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback
                         && mImeBackAnimationController != null) {
@@ -202,6 +227,13 @@
                 Integer prevPriority = mAllCallbacks.get(callback);
                 mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
             }
+            if (mSystemNavigationObserverCallback == callback) {
+                mSystemNavigationObserverCallback = null;
+                if (DEBUG) {
+                    Log.i(TAG, "Callback already registered (as system-navigation-observer "
+                            + "callback). Removing and re-adding it.");
+                }
+            }
 
             OnBackInvokedCallback previousTopCallback = getTopCallback();
             callbacks.add(callback);
@@ -221,6 +253,10 @@
                 mImeDispatcher.unregisterOnBackInvokedCallback(callback);
                 return;
             }
+            if (mSystemNavigationObserverCallback == callback) {
+                mSystemNavigationObserverCallback = null;
+                return;
+            }
             if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
                 callback = mImeBackAnimationController;
             }
@@ -230,25 +266,29 @@
                 }
                 return;
             }
-            OnBackInvokedCallback previousTopCallback = getTopCallback();
-            Integer priority = mAllCallbacks.get(callback);
-            ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
-            callbacks.remove(callback);
-            if (callbacks.isEmpty()) {
-                mOnBackInvokedCallbacks.remove(priority);
-            }
-            mAllCallbacks.remove(callback);
-            // Re-populate the top callback to WM if the removed callback was previously the top
-            // one.
-            if (previousTopCallback == callback) {
-                // We should call onBackCancelled() when an active callback is removed from
-                // dispatcher.
-                mProgressAnimator.removeOnBackCancelledFinishCallback();
-                mProgressAnimator.removeOnBackInvokedFinishCallback();
-                sendCancelledIfInProgress(callback);
-                mHandler.post(mProgressAnimator::reset);
-                setTopOnBackInvokedCallback(getTopCallback());
-            }
+            removeCallbackInternal(callback);
+        }
+    }
+
+    private void removeCallbackInternal(@NonNull OnBackInvokedCallback callback) {
+        OnBackInvokedCallback previousTopCallback = getTopCallback();
+        Integer priority = mAllCallbacks.get(callback);
+        ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+        callbacks.remove(callback);
+        if (callbacks.isEmpty()) {
+            mOnBackInvokedCallbacks.remove(priority);
+        }
+        mAllCallbacks.remove(callback);
+        // Re-populate the top callback to WM if the removed callback was previously the top
+        // one.
+        if (previousTopCallback == callback) {
+            // We should call onBackCancelled() when an active callback is removed from
+            // dispatcher.
+            mProgressAnimator.removeOnBackCancelledFinishCallback();
+            mProgressAnimator.removeOnBackInvokedFinishCallback();
+            sendCancelledIfInProgress(callback);
+            mHandler.post(mProgressAnimator::reset);
+            setTopOnBackInvokedCallback(getTopCallback());
         }
     }
 
@@ -304,6 +344,7 @@
             mHandler.post(mProgressAnimator::reset);
             mAllCallbacks.clear();
             mOnBackInvokedCallbacks.clear();
+            mSystemNavigationObserverCallback = null;
         }
     }
 
@@ -315,6 +356,25 @@
         }
     }
 
+    /**
+     * Tries to call {@link OnBackInvokedCallback#onBackInvoked} on the system navigation observer
+     * callback (if one is set and if the top-most regular callback has
+     * {@link OnBackInvokedDispatcher#PRIORITY_SYSTEM})
+     */
+    public void tryInvokeSystemNavigationObserverCallback() {
+        OnBackInvokedCallback topCallback = getTopCallback();
+        Integer callbackPriority = mAllCallbacks.getOrDefault(topCallback, null);
+        if (callbackPriority != null && callbackPriority == PRIORITY_SYSTEM) {
+            invokeSystemNavigationObserverCallback();
+        }
+    }
+
+    private void invokeSystemNavigationObserverCallback() {
+        if (mSystemNavigationObserverCallback != null) {
+            mSystemNavigationObserverCallback.onBackInvoked();
+        }
+    }
+
     private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
         if (mWindowSession == null || mWindow == null) {
             return;
@@ -324,7 +384,9 @@
             if (callback != null) {
                 int priority = mAllCallbacks.get(callback);
                 final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper(callback,
-                        mTouchTracker, mProgressAnimator, mHandler, this::callOnKeyPreIme);
+                        mTouchTracker, mProgressAnimator, mHandler, this::callOnKeyPreIme,
+                        this::invokeSystemNavigationObserverCallback,
+                        /*isSystemCallback*/ priority == PRIORITY_SYSTEM);
                 callbackInfo = new OnBackInvokedCallbackInfo(
                         iCallback,
                         priority,
@@ -416,18 +478,26 @@
         private final Handler mHandler;
         @NonNull
         private final BooleanSupplier mOnKeyPreIme;
+        @NonNull
+        private final Runnable mSystemNavigationObserverCallbackRunnable;
+        private final boolean mIsSystemCallback;
 
         OnBackInvokedCallbackWrapper(
                 @NonNull OnBackInvokedCallback callback,
                 @NonNull BackTouchTracker touchTracker,
                 @NonNull BackProgressAnimator progressAnimator,
                 @NonNull Handler handler,
-                @NonNull BooleanSupplier onKeyPreIme) {
+                @NonNull BooleanSupplier onKeyPreIme,
+                @NonNull Runnable systemNavigationObserverCallbackRunnable,
+                boolean isSystemCallback
+        ) {
             mCallback = new WeakReference<>(callback);
             mTouchTracker = touchTracker;
             mProgressAnimator = progressAnimator;
             mHandler = handler;
             mOnKeyPreIme = onKeyPreIme;
+            mSystemNavigationObserverCallbackRunnable = systemNavigationObserverCallbackRunnable;
+            mIsSystemCallback = isSystemCallback;
         }
 
         @Override
@@ -494,9 +564,17 @@
                 OnBackAnimationCallback animationCallback = getBackAnimationCallback();
                 if (animationCallback != null
                         && !(callback instanceof ImeBackAnimationController)) {
-                    mProgressAnimator.onBackInvoked(callback::onBackInvoked);
+                    mProgressAnimator.onBackInvoked(() -> {
+                        if (mIsSystemCallback) {
+                            mSystemNavigationObserverCallbackRunnable.run();
+                        }
+                        callback.onBackInvoked();
+                    });
                 } else {
                     mProgressAnimator.reset();
+                    if (mIsSystemCallback) {
+                        mSystemNavigationObserverCallbackRunnable.run();
+                    }
                     callback.onBackInvoked();
                 }
             });
@@ -597,9 +675,18 @@
                                 + " application manifest.");
                 return false;
             }
-            if (priority < 0) {
-                throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
-                        + "cannot have negative priority. Priority: " + priority);
+            if (predictiveBackPrioritySystemNavigationObserver()) {
+                if (priority < 0 && priority != PRIORITY_SYSTEM_NAVIGATION_OBSERVER) {
+                    throw new IllegalArgumentException("Application registered "
+                            + "OnBackInvokedCallback cannot have negative priority. Priority: "
+                            + priority);
+                }
+            } else {
+                if (priority < 0) {
+                    throw new IllegalArgumentException("Application registered "
+                            + "OnBackInvokedCallback cannot have negative priority. Priority: "
+                            + priority);
+                }
             }
             Objects.requireNonNull(callback);
             return true;
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 086063f..c9b93c9 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -307,3 +307,11 @@
   bug: "364930619"
   is_fixed_read_only: true
 }
+
+flag {
+    name: "predictive_back_priority_system_navigation_observer"
+    namespace: "systemui"
+    description: "PRIORITY_SYSTEM_NAVIGATION_OBSERVER predictive back API extension"
+    is_fixed_read_only: true
+    bug: "362938401"
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1a3a30d..b90ee2b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1529,6 +1529,11 @@
         factory reset. -->
     <bool name="config_enableCredentialFactoryResetProtection">true</bool>
 
+    <!-- If true, then work around broken Weaver HALs that don't work reliably before the device has
+         fully booted. Setting this to true weakens a security feature; it should be done only when
+         necessary, though it is still better than not using Weaver at all. -->
+    <bool name="config_disableWeaverOnUnsecuredUsers">false</bool>
+
     <!-- Control the behavior when the user long presses the home button.
             0 - Nothing
             1 - Launch all apps intent
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d5298ac..c50c336 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3988,6 +3988,7 @@
   <java-symbol type="string" name="foreground_service_multiple_separator" />
 
   <java-symbol type="bool" name="config_enableCredentialFactoryResetProtection" />
+  <java-symbol type="bool" name="config_disableWeaverOnUnsecuredUsers" />
 
   <!-- ETWS primary messages -->
   <java-symbol type="string" name="etws_primary_default_message_earthquake" />
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 0bda0ff..0a4c5e6 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -18,6 +18,9 @@
 
 import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
 import static android.window.OnBackInvokedDispatcher.PRIORITY_OVERLAY;
+import static android.window.OnBackInvokedDispatcher.PRIORITY_SYSTEM_NAVIGATION_OBSERVER;
+
+import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -39,6 +42,10 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.ImeBackAnimationController;
@@ -80,6 +87,8 @@
 
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Mock
     private IWindowSession mWindowSession;
@@ -145,7 +154,8 @@
         assertEquals("No setOnBackInvokedCallbackInfo", mCallbackInfoCalls, actual);
     }
 
-    private void assertCallbacksSize(int expectedDefault, int expectedOverlay) {
+    private void assertCallbacksSize(int expectedDefault, int expectedOverlay,
+            int expectedObserver) {
         ArrayList<OnBackInvokedCallback> callbacksDefault = mDispatcher
                 .mOnBackInvokedCallbacks.get(PRIORITY_DEFAULT);
         int actualSizeDefault = callbacksDefault != null ? callbacksDefault.size() : 0;
@@ -155,6 +165,10 @@
                 .mOnBackInvokedCallbacks.get(PRIORITY_OVERLAY);
         int actualSizeOverlay = callbacksOverlay != null ? callbacksOverlay.size() : 0;
         assertEquals("mOnBackInvokedCallbacks OVERLAY size", expectedOverlay, actualSizeOverlay);
+
+        int actualSizeObserver = mDispatcher.mSystemNavigationObserverCallback == null ? 0 : 1;
+        assertEquals("mOnBackInvokedCallbacks SYSTEM_NAVIGATION_OBSERVER size", expectedObserver,
+                actualSizeObserver);
     }
 
     private void assertTopCallback(OnBackInvokedCallback expectedCallback) {
@@ -164,13 +178,13 @@
     @Test
     public void registerCallback_samePriority_sameCallback() throws RemoteException {
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
-        assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback1);
 
         // The callback is removed and added again
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
-        assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback1);
 
@@ -182,13 +196,13 @@
     @Test
     public void registerCallback_samePriority_differentCallback() throws RemoteException {
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
-        assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback1);
 
         // The new callback becomes the TopCallback
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
-        assertCallbacksSize(/* default */ 2, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 2, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback2);
 
@@ -201,13 +215,13 @@
     @Test
     public void registerCallback_differentPriority_sameCallback() throws RemoteException {
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
-        assertCallbacksSize(/* default */ 0, /* overlay */ 1);
+        assertCallbacksSize(/* default */ 0, /* overlay */ 1, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback1);
 
         // The callback is moved to the new priority list
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
-        assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback1);
 
@@ -220,13 +234,13 @@
     public void registerCallback_differentPriority_differentCallback() throws RemoteException {
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
         assertSetCallbackInfo();
-        assertCallbacksSize(/* default */ 0, /* overlay */ 1);
+        assertCallbacksSize(/* default */ 0, /* overlay */ 1, /* observer */ 0);
         assertTopCallback(mCallback1);
 
         // The callback with higher priority is still the TopCallback
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
         assertNoSetCallbackInfo();
-        assertCallbacksSize(/* default */ 1, /* overlay */ 1);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 1, /* observer */ 0);
         assertTopCallback(mCallback1);
 
         waitForIdle();
@@ -238,22 +252,22 @@
     @Test
     public void registerCallback_sameInstanceAddedTwice() throws RemoteException {
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback1);
-        assertCallbacksSize(/* default */ 0, /* overlay */ 1);
+        assertCallbacksSize(/* default */ 0, /* overlay */ 1, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback1);
 
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
-        assertCallbacksSize(/* default */ 1, /* overlay */ 1);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 1, /* observer */ 0);
         assertNoSetCallbackInfo();
         assertTopCallback(mCallback1);
 
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
-        assertCallbacksSize(/* default */ 2, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 2, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback1);
 
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_OVERLAY, mCallback2);
-        assertCallbacksSize(/* default */ 1, /* overlay */ 1);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 1, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mCallback2);
 
@@ -570,6 +584,102 @@
         assertFalse(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
     }
 
+    @Test(expected = IllegalArgumentException.class)
+    @RequiresFlagsDisabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER)
+    public void testNoUiCallback_registrationFailsWithoutFlaggedApiEnabled() {
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER)
+    public void testNoUiCallback_invokedWithSystemCallback() throws RemoteException {
+        mDispatcher.registerSystemOnBackInvokedCallback(mCallback1);
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2);
+
+        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1);
+        OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
+        assertTopCallback(mCallback1);
+
+        callbackInfo.getCallback().onBackStarted(mBackEvent);
+        waitForIdle();
+        verify(mCallback1).onBackStarted(any());
+        verify(mCallback2, never()).onBackStarted(any());
+
+        callbackInfo.getCallback().onBackProgressed(mBackEvent);
+        waitForIdle();
+        verify(mCallback1).onBackProgressed(any());
+        verify(mCallback2, never()).onBackProgressed(any());
+
+        callbackInfo.getCallback().onBackCancelled();
+        waitForIdle();
+        verify(mCallback1, timeout(1000)).onBackCancelled();
+        verify(mCallback2, never()).onBackCancelled();
+
+        // start new gesture to test onBackInvoked case
+        callbackInfo.getCallback().onBackStarted(mBackEvent);
+        callbackInfo.getCallback().onBackInvoked();
+        waitForIdle();
+        verify(mCallback1, timeout(1000)).onBackInvoked();
+        verify(mCallback2).onBackInvoked();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER)
+    public void testNoUiCallback_notInvokedWithNonSystemCallback() throws RemoteException {
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2);
+
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 1);
+        OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
+        assertTopCallback(mCallback1);
+
+        callbackInfo.getCallback().onBackStarted(mBackEvent);
+        waitForIdle();
+        verify(mCallback1).onBackStarted(any());
+        verify(mCallback2, never()).onBackStarted(any());
+
+        callbackInfo.getCallback().onBackProgressed(mBackEvent);
+        waitForIdle();
+        verify(mCallback1).onBackProgressed(any());
+        verify(mCallback2, never()).onBackProgressed(any());
+
+        callbackInfo.getCallback().onBackCancelled();
+        waitForIdle();
+        verify(mCallback1, timeout(1000)).onBackCancelled();
+        verify(mCallback2, never()).onBackCancelled();
+
+        // start new gesture to test onBackInvoked case
+        callbackInfo.getCallback().onBackStarted(mBackEvent);
+        callbackInfo.getCallback().onBackInvoked();
+        waitForIdle();
+        verify(mCallback1, timeout(1000)).onBackInvoked();
+        verify(mCallback2, never()).onBackInvoked();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER)
+    public void testNoUiCallback_reregistrations() {
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback1);
+        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1);
+        assertEquals(mCallback1, mDispatcher.mSystemNavigationObserverCallback);
+
+        // test reregistration of observer-callback as observer-callback
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2);
+        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1);
+        assertEquals(mCallback2, mDispatcher.mSystemNavigationObserverCallback);
+
+        // test reregistration of observer-callback as regular callback
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback2);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 0);
+
+        // test reregistration of regular callback as observer-callback
+        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2);
+        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1);
+
+        mDispatcher.unregisterOnBackInvokedCallback(mCallback2);
+        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 0);
+    }
+
     private BackMotionEvent backMotionEventFrom(float progress) {
         return new BackMotionEvent(
                 /* touchX = */ 0,
@@ -585,13 +695,13 @@
     private void verifyImeCallackRegistrations() throws RemoteException {
         // verify default callback is replaced with ImeBackAnimationController
         mDispatcher.registerOnBackInvokedCallbackUnchecked(mDefaultImeCallback, PRIORITY_DEFAULT);
-        assertCallbacksSize(/* default */ 1, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mImeBackAnimationController);
 
         // verify regular ime callback is successfully registered
         mDispatcher.registerOnBackInvokedCallbackUnchecked(mImeCallback, PRIORITY_DEFAULT);
-        assertCallbacksSize(/* default */ 2, /* overlay */ 0);
+        assertCallbacksSize(/* default */ 2, /* overlay */ 0, /* observer */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mImeCallback);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 03b7c8b..a8a8c2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -274,7 +274,8 @@
     private final DragAndDropController mDragAndDropController;
     /** Used to send bubble events to launcher. */
     private Bubbles.BubbleStateListener mBubbleStateListener;
-
+    /** Used to track previous navigation mode to detect switch to buttons navigation. */
+    private boolean mIsPrevNavModeGestures;
     /** Used to send updates to the views from {@link #mBubbleDataListener}. */
     private BubbleViewCallback mBubbleViewCallback;
 
@@ -356,6 +357,7 @@
             }
         };
         mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(this);
+        mIsPrevNavModeGestures = ContextUtils.isGestureNavigationMode(mContext);
     }
 
     private void registerOneHandedState(OneHandedController oneHanded) {
@@ -589,6 +591,13 @@
      */
     private void sendInitialListenerUpdate() {
         if (mBubbleStateListener != null) {
+            boolean isCurrentNavModeGestures = ContextUtils.isGestureNavigationMode(mContext);
+            if (mIsPrevNavModeGestures && !isCurrentNavModeGestures) {
+                BubbleBarLocation navButtonsLocation = ContextUtils.isRtl(mContext)
+                        ? BubbleBarLocation.RIGHT : BubbleBarLocation.LEFT;
+                mBubblePositioner.setBubbleBarLocation(navButtonsLocation);
+            }
+            mIsPrevNavModeGestures = isCurrentNavModeGestures;
             BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar();
             mBubbleStateListener.onBubbleStateChange(update);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 2795881..35a0d07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -61,7 +61,6 @@
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
-import android.view.WindowManagerPolicyConstants;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.widget.FrameLayout;
@@ -91,10 +90,10 @@
 import com.android.wm.shell.bubbles.animation.StackAnimationController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.shared.bubbles.DismissView;
-import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
 import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
 import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import java.io.PrintWriter;
@@ -2276,7 +2275,7 @@
     void startMonitoringSwipeUpGesture() {
         stopMonitoringSwipeUpGestureInternal();
 
-        if (isGestureNavEnabled()) {
+        if (ContextUtils.isGestureNavigationMode(mContext)) {
             mBubblesNavBarGestureTracker = new BubblesNavBarGestureTracker(mContext, mPositioner);
             mBubblesNavBarGestureTracker.start(mSwipeUpListener);
             setOnTouchListener(mContainerSwipeListener);
@@ -2311,12 +2310,6 @@
         }
     }
 
-    private boolean isGestureNavEnabled() {
-        return mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_navBarInteractionMode)
-                == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
-    }
-
     /**
      * Stop monitoring for swipe up gesture
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt
new file mode 100644
index 0000000..0b36f45
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.wm.shell.bubbles
+
+import android.content.Context
+import android.view.View
+import android.view.WindowManagerPolicyConstants
+import com.android.internal.R
+
+/** Simplifies accessing context fields. */
+object ContextUtils {
+
+    /** Gets navigation mode. */
+    @JvmStatic
+    val Context.navigationMode: Int
+        get() = resources.getInteger(R.integer.config_navBarInteractionMode)
+
+    /** Returns whether the navigation mode is gestures. */
+    @JvmStatic
+    val Context.isGestureNavigationMode: Boolean
+        get() = navigationMode == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+
+    /** Returns whether layout direction is rtl. */
+    @JvmStatic
+    val Context.isRtl: Boolean
+        get() = resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 3464fef..702552e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -77,10 +77,12 @@
             PipTaskListener pipTaskListener,
             @NonNull PipScheduler pipScheduler,
             @NonNull PipTransitionState pipStackListenerController,
+            @NonNull PipDisplayLayoutState pipDisplayLayoutState,
             @NonNull PipUiStateChangeController pipUiStateChangeController) {
         return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
                 pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
-                pipScheduler, pipStackListenerController, pipUiStateChangeController);
+                pipScheduler, pipStackListenerController, pipDisplayLayoutState,
+                pipUiStateChangeController);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 7261919..52b92a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -21,6 +21,12 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.RectEvaluator;
@@ -125,7 +131,7 @@
         mContext = context;
         mTaskSurface = taskSurface;
         mRootTdaOrganizer = taskDisplayAreaOrganizer;
-        mCurrentType = IndicatorType.NO_INDICATOR;
+        mCurrentType = NO_INDICATOR;
         mDragStartState = dragStartState;
     }
 
@@ -136,8 +142,16 @@
     @NonNull
     IndicatorType updateIndicatorType(PointF inputCoordinates) {
         final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+        // Perform a quick check first: any input off the left edge of the display should be split
+        // left, and split right for the right edge. This is universal across all drag event types.
+        if (inputCoordinates.x < 0) return TO_SPLIT_LEFT_INDICATOR;
+        if (inputCoordinates.x > layout.width()) return TO_SPLIT_RIGHT_INDICATOR;
         // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
-        IndicatorType result = IndicatorType.NO_INDICATOR;
+        // In drags not originating on a freeform caption, we should default to a TO_DESKTOP
+        // indicator.
+        IndicatorType result = mDragStartState == DragStartState.FROM_FREEFORM
+                ? NO_INDICATOR
+                : TO_DESKTOP_INDICATOR;
         final int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness);
         // Because drags in freeform use task position for indicator calculation, we need to
@@ -149,10 +163,8 @@
                 captionHeight);
         final Region splitRightRegion = calculateSplitRightRegion(layout, transitionAreaWidth,
                 captionHeight);
-        final Region toDesktopRegion = calculateToDesktopRegion(layout, splitLeftRegion,
-                splitRightRegion, fullscreenRegion);
         if (fullscreenRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
-            result = IndicatorType.TO_FULLSCREEN_INDICATOR;
+            result = TO_FULLSCREEN_INDICATOR;
         }
         if (splitLeftRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
             result = IndicatorType.TO_SPLIT_LEFT_INDICATOR;
@@ -160,9 +172,6 @@
         if (splitRightRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
             result = IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
         }
-        if (toDesktopRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
-            result = IndicatorType.TO_DESKTOP_INDICATOR;
-        }
         if (mDragStartState != DragStartState.DRAGGED_INTENT) {
             transitionIndicator(result);
         }
@@ -182,7 +191,7 @@
                     R.dimen.desktop_mode_fullscreen_region_scale);
             final float toFullscreenWidth = (layout.width() * toFullscreenScale);
             region.union(new Rect((int) ((layout.width() / 2f) - (toFullscreenWidth / 2f)),
-                    -captionHeight,
+                    Short.MIN_VALUE,
                     (int) ((layout.width() / 2f) + (toFullscreenWidth / 2f)),
                     transitionHeight));
         }
@@ -192,7 +201,7 @@
                 || mDragStartState == DragStartState.DRAGGED_INTENT
         ) {
             region.union(new Rect(0,
-                    -captionHeight,
+                    Short.MIN_VALUE,
                     layout.width(),
                     transitionHeight));
         }
@@ -200,21 +209,6 @@
     }
 
     @VisibleForTesting
-    Region calculateToDesktopRegion(DisplayLayout layout,
-            Region splitLeftRegion, Region splitRightRegion,
-            Region toFullscreenRegion) {
-        final Region region = new Region();
-        // If in desktop, we need no region. Otherwise it's the same for all windowing modes.
-        if (mDragStartState != DragStartState.FROM_FREEFORM) {
-            region.union(new Rect(0, 0, layout.width(), layout.height()));
-            region.op(splitLeftRegion, Region.Op.DIFFERENCE);
-            region.op(splitRightRegion, Region.Op.DIFFERENCE);
-            region.op(toFullscreenRegion, Region.Op.DIFFERENCE);
-        }
-        return region;
-    }
-
-    @VisibleForTesting
     Region calculateSplitLeftRegion(DisplayLayout layout,
             int transitionEdgeWidth, int captionHeight) {
         final Region region = new Region();
@@ -311,7 +305,7 @@
                 }
             });
         }
-        mCurrentType = IndicatorType.NO_INDICATOR;
+        mCurrentType = NO_INDICATOR;
     }
 
     /**
@@ -322,9 +316,9 @@
         if (mView == null) {
             createView();
         }
-        if (mCurrentType == IndicatorType.NO_INDICATOR) {
+        if (mCurrentType == NO_INDICATOR) {
             fadeInIndicator(newType);
-        } else if (newType == IndicatorType.NO_INDICATOR) {
+        } else if (newType == NO_INDICATOR) {
             fadeOutIndicator(null /* finishCallback */);
         } else {
             final VisualIndicatorAnimator animator = VisualIndicatorAnimator.animateIndicatorType(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index cebeca5..4e548a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -1628,7 +1628,7 @@
             return
         }
 
-        val indicator = visualIndicator ?: return
+        val indicator = getVisualIndicator() ?: return
         val indicatorType =
             indicator.updateIndicatorType(
                 PointF(inputCoordinate.x, currentDragBounds.top.toFloat()),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index e9c4c14..73be8db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -324,10 +324,16 @@
             int launcherRotation, Rect hotseatKeepClearArea) {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "getSwipePipToHomeBounds: %s", componentName);
-        // preemptively add the keep clear area for Hotseat, so that it is taken into account
-        // when calculating the entry destination bounds of PiP window
+        // Preemptively add the keep clear area for Hotseat, so that it is taken into account
+        // when calculating the entry destination bounds of PiP window.
         mPipBoundsState.setNamedUnrestrictedKeepClearArea(
                 PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, hotseatKeepClearArea);
+
+        // Set the display layout rotation early to calculate final orientation bounds that
+        // the animator expects, this will also be used to detect the fixed rotation when
+        // Shell resolves the type of the animation we are undergoing.
+        mPipDisplayLayoutState.rotateTo(launcherRotation);
+
         mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, pictureInPictureParams,
                 mPipBoundsAlgorithm);
         return mPipBoundsAlgorithm.getEntryDestinationBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index dc0bc78..62a60fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.pip2.phone;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Surface.ROTATION_270;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_PIP;
@@ -33,6 +34,7 @@
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.content.Context;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -49,6 +51,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipMenuController;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.PipTransitionController;
@@ -82,7 +85,7 @@
      * The fixed start delay in ms when fading out the content overlay from bounds animation.
      * The fadeout animation is guaranteed to start after the client has drawn under the new config.
      */
-    private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 400;
+    private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;
 
     //
     // Dependencies
@@ -92,6 +95,7 @@
     private final PipTaskListener mPipTaskListener;
     private final PipScheduler mPipScheduler;
     private final PipTransitionState mPipTransitionState;
+    private final PipDisplayLayoutState mPipDisplayLayoutState;
 
     //
     // Transition caches
@@ -124,6 +128,7 @@
             PipTaskListener pipTaskListener,
             PipScheduler pipScheduler,
             PipTransitionState pipTransitionState,
+            PipDisplayLayoutState pipDisplayLayoutState,
             PipUiStateChangeController pipUiStateChangeController) {
         super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
                 pipBoundsAlgorithm);
@@ -134,6 +139,7 @@
         mPipScheduler.setPipTransitionController(this);
         mPipTransitionState = pipTransitionState;
         mPipTransitionState.addPipTransitionStateChangedListener(this);
+        mPipDisplayLayoutState = pipDisplayLayoutState;
     }
 
     @Override
@@ -321,11 +327,30 @@
                             (destinationBounds.width() - overlaySize) / 2f,
                             (destinationBounds.height() - overlaySize) / 2f);
         }
-
         startTransaction.merge(finishTransaction);
+
+        final int startRotation = pipChange.getStartRotation();
+        final int endRotation = mPipDisplayLayoutState.getRotation();
+        if (endRotation != startRotation) {
+            boolean isClockwise = (endRotation - startRotation) == -ROTATION_270;
+
+            // Display bounds were already updated to represent the final orientation,
+            // so we just need to readjust the origin, and perform rotation about (0, 0).
+            Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
+            int originTranslateX = isClockwise ? 0 : -displayBounds.width();
+            int originTranslateY = isClockwise ? -displayBounds.height() : 0;
+
+            Matrix transformTensor = new Matrix();
+            final float[] matrixTmp = new float[9];
+            transformTensor.setTranslate(originTranslateX + destinationBounds.left,
+                    originTranslateY + destinationBounds.top);
+            final float degrees = (endRotation - startRotation) * 90f;
+            transformTensor.postRotate(degrees);
+            startTransaction.setMatrix(pipLeash, transformTensor, matrixTmp);
+        }
         startTransaction.apply();
         finishCallback.onTransitionFinished(null /* finishWct */);
-        onClientDrawAtTransitionEnd();
+        finishInner();
         return true;
     }
 
@@ -397,7 +422,7 @@
                 sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0);
 
         tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
-                this::onClientDrawAtTransitionEnd);
+                this::finishInner);
         finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
 
         animator.setAnimationEndCallback(() ->
@@ -430,7 +455,7 @@
         animator.setAnimationEndCallback(() -> {
             finishCallback.onTransitionFinished(null);
             // This should update the pip transition state accordingly after we stop playing.
-            onClientDrawAtTransitionEnd();
+            finishInner();
         });
 
         animator.start();
@@ -605,7 +630,7 @@
     // Miscellaneous callbacks and listeners
     //
 
-    private void onClientDrawAtTransitionEnd() {
+    private void finishInner() {
         if (mPipTransitionState.getSwipePipToHomeOverlay() != null) {
             startOverlayFadeoutAnimation();
         } else if (mPipTransitionState.getState() == PipTransitionState.ENTERING_PIP) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index 2b7f86f..935e6d0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -17,8 +17,8 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.PointF
 import android.graphics.Rect
-import android.graphics.Region
 import android.testing.AndroidTestingRunner
 import android.view.SurfaceControl
 import androidx.test.filters.SmallTest
@@ -33,6 +33,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.kotlin.whenever
 
@@ -58,13 +59,15 @@
         whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
         whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
         whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS)
+        whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
     }
 
     @Test
     fun testFullscreenRegionCalculation() {
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, 2 * STABLE_INSETS.top))
+        assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
+            2 * STABLE_INSETS.top))
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
@@ -75,17 +78,19 @@
         val toFullscreenWidth = displayLayout.width() * toFullscreenScale
         assertThat(testRegion.bounds).isEqualTo(Rect(
             (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
-            -50,
+            Short.MIN_VALUE.toInt(),
             (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
             transitionHeight))
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, 2 * STABLE_INSETS.top))
+        assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
+            2 * STABLE_INSETS.top))
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
+        assertThat(testRegion.bounds).isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400,
+            transitionHeight))
     }
 
     @Test
@@ -133,22 +138,19 @@
     }
 
     @Test
-    fun testToDesktopRegionCalculation() {
+    fun testDefaultIndicators() {
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
-        val fullscreenRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
-            CAPTION_HEIGHT)
-        val splitLeftRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
-        val splitRightRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
-        val desktopRegion = visualIndicator.calculateToDesktopRegion(displayLayout,
-            splitLeftRegion, splitRightRegion, fullscreenRegion)
-        var testRegion = Region()
-        testRegion.union(DISPLAY_BOUNDS)
-        testRegion.op(splitLeftRegion, Region.Op.DIFFERENCE)
-        testRegion.op(splitRightRegion, Region.Op.DIFFERENCE)
-        testRegion.op(fullscreenRegion, Region.Op.DIFFERENCE)
-        assertThat(desktopRegion).isEqualTo(testRegion)
+        var result = visualIndicator.updateIndicatorType(PointF(-10000f, 500f))
+        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
+        result = visualIndicator.updateIndicatorType(PointF(10000f, 500f))
+        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
+        result = visualIndicator.updateIndicatorType(PointF(500f, 10000f))
+        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
+        result = visualIndicator.updateIndicatorType(PointF(500f, 10000f))
+        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
     }
 
     private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 3d813c7..9e5c1a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -2647,13 +2647,17 @@
   @Test
   fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
     val task = setUpFreeformTask()
+    val spyController = spy(controller)
     val mockSurface = mock(SurfaceControl::class.java)
     val mockDisplayLayout = mock(DisplayLayout::class.java)
     whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
     whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
-    controller.onDragPositioningMove(task, mockSurface, 200f, Rect(100, -100, 500, 1000))
+    spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, -100, 500, 1000))
 
-    controller.onDragPositioningEnd(
+    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
+      .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+    spyController.onDragPositioningEnd(
         task,
         mockSurface,
         Point(100, -100), /* position */
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b491b5a..d39b564 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -223,7 +223,6 @@
                     Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
                     Settings.Global.ENABLE_DISKSTATS_LOGGING,
                     Settings.Global.ENABLE_EPHEMERAL_FEATURE,
-                    Settings.Global.ENABLE_USE_APP_INFO_NOT_LAUNCHED,
                     Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
                     Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
                     Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt
deleted file mode 100644
index a310ef4..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.statusbar.notification.row.ui.viewmodel
-
-import android.platform.test.annotations.EnableFlags
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.notification.row.data.repository.fakeNotificationRowRepository
-import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
-import com.android.systemui.statusbar.notification.row.shared.IconModel
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-@EnableFlags(RichOngoingNotificationFlag.FLAG_NAME)
-class EnRouteViewModelTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val repository = kosmos.fakeNotificationRowRepository
-
-    private var contentModel: EnRouteContentModel?
-        get() = repository.richOngoingContentModel.value as? EnRouteContentModel
-        set(value) {
-            repository.richOngoingContentModel.value = value
-        }
-
-    private lateinit var underTest: EnRouteViewModel
-
-    @Before
-    fun setup() {
-        underTest = kosmos.getEnRouteViewModel(repository)
-    }
-
-    @Test
-    fun viewModelShowsContent() =
-        testScope.runTest {
-            val title by collectLastValue(underTest.title)
-            val text by collectLastValue(underTest.text)
-            contentModel =
-                exampleEnRouteContent(
-                    title = "Example EnRoute Title",
-                    text = "Example EnRoute Text",
-                )
-            assertThat(title).isEqualTo("Example EnRoute Title")
-            assertThat(text).isEqualTo("Example EnRoute Text")
-        }
-
-    private fun exampleEnRouteContent(
-        icon: IconModel = mock(),
-        title: CharSequence = "example text",
-        text: CharSequence = "example title",
-    ) =
-        EnRouteContentModel(
-            smallIcon = icon,
-            title = title,
-            text = text,
-        )
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelTest.kt
deleted file mode 100644
index 61873ad..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelTest.kt
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.statusbar.notification.row.ui.viewmodel
-
-import android.app.Notification
-import android.app.PendingIntent
-import android.platform.test.annotations.EnableFlags
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.notification.row.data.repository.fakeNotificationRowRepository
-import com.android.systemui.statusbar.notification.row.shared.IconModel
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
-import com.android.systemui.statusbar.notification.row.shared.TimerContentModel.TimerState.Paused
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import java.time.Duration
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-@EnableFlags(RichOngoingNotificationFlag.FLAG_NAME)
-class TimerViewModelTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val repository = kosmos.fakeNotificationRowRepository
-
-    private var contentModel: TimerContentModel?
-        get() = repository.richOngoingContentModel.value as? TimerContentModel
-        set(value) {
-            repository.richOngoingContentModel.value = value
-        }
-
-    private lateinit var underTest: TimerViewModel
-
-    @Before
-    fun setup() {
-        underTest = kosmos.getTimerViewModel(repository)
-    }
-
-    @Test
-    fun labelShowsTheTimerName() =
-        testScope.runTest {
-            val label by collectLastValue(underTest.label)
-            contentModel = pausedTimer(name = "Example Timer Name")
-            assertThat(label).isEqualTo("Example Timer Name")
-        }
-
-    @Test
-    fun pausedTimeRemainingFormatsWell() =
-        testScope.runTest {
-            val label by collectLastValue(underTest.pausedTime)
-            contentModel = pausedTimer(timeRemaining = Duration.ofMinutes(3))
-            assertThat(label).isEqualTo("3:00")
-            contentModel = pausedTimer(timeRemaining = Duration.ofSeconds(119))
-            assertThat(label).isEqualTo("1:59")
-            contentModel = pausedTimer(timeRemaining = Duration.ofSeconds(121))
-            assertThat(label).isEqualTo("2:01")
-            contentModel = pausedTimer(timeRemaining = Duration.ofHours(1))
-            assertThat(label).isEqualTo("1:00:00")
-            contentModel = pausedTimer(timeRemaining = Duration.ofHours(24))
-            assertThat(label).isEqualTo("24:00:00")
-        }
-
-    private fun pausedTimer(
-        icon: IconModel = mock(),
-        name: String = "example",
-        timeRemaining: Duration = Duration.ofMinutes(3),
-        resumeIntent: PendingIntent? = null,
-        addMinuteAction: Notification.Action? = null,
-        resetAction: Notification.Action? = null
-    ) =
-        TimerContentModel(
-            icon = icon,
-            name = name,
-            state =
-                Paused(
-                    timeRemaining = timeRemaining,
-                    resumeIntent = resumeIntent,
-                    addMinuteAction = addMinuteAction,
-                    resetAction = resetAction,
-                )
-        )
-}
diff --git a/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
new file mode 100644
index 0000000..8a77d88
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2024, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the screen that shows the 9 circle unlock widget and instructs
+     the user how to unlock their device, or make an emergency call.  This
+     is the landscape layout.  -->
+<com.android.keyguard.KeyguardPatternView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_pattern_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center_horizontal|bottom"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:orientation="horizontal">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="2"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical">
+
+        <include layout="@layout/keyguard_bouncer_message_area"/>
+
+        <com.android.systemui.bouncer.ui.BouncerMessageView
+            android:id="@+id/bouncer_message_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            androidprv:layout_constraintBottom_toTopOf="@+id/lockPatternView"
+            androidprv:layout_constraintTop_toTopOf="parent"
+            androidprv:layout_constraintVertical_chainStyle="packed" />
+
+        <include
+            android:id="@+id/keyguard_selector_fade_container"
+            layout="@layout/keyguard_eca"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center_horizontal"
+            android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+            android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+            android:gravity="center_horizontal"
+            android:orientation="vertical"
+            androidprv:layout_constraintBottom_toBottomOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/pattern_container"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="3"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical">
+
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/pattern_top_guideline"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            androidprv:layout_constraintGuide_percent="0" />
+
+        <com.android.internal.widget.LockPatternView
+            android:id="@+id/lockPatternView"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:orientation="horizontal"
+            androidprv:layout_constraintDimensionRatio="1.0"
+            androidprv:layout_constraintVertical_bias="1.0"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            androidprv:layout_constraintBottom_toBottomOf="parent"
+            androidprv:layout_constraintTop_toBottomOf="@id/pattern_top_guideline"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_pin_view.xml
new file mode 100644
index 0000000..4b8b63f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_pin_view.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2024, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. -->
+<com.android.keyguard.KeyguardSimPinView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_sim_pin_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center_horizontal|bottom"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:orientation="horizontal">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="2"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical">
+
+        <include layout="@layout/keyguard_bouncer_message_area"/>
+
+        <ImageView
+            android:id="@+id/keyguard_sim"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_marginBottom="3dp"
+            android:src="@drawable/ic_lockscreen_sim"
+            androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            app:tint="@color/background_protected"/>
+
+        <include
+            android:id="@+id/keyguard_esim_area"
+            layout="@layout/keyguard_esim_area"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area"/>
+
+        <com.android.keyguard.AlphaOptimizedRelativeLayout
+            android:id="@+id/pin_entry_area"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
+            androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container">
+
+            <com.android.keyguard.PasswordTextView
+                android:id="@+id/simPinEntry"
+                style="@style/Widget.TextView.Password"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/keyguard_password_height"
+                android:layout_centerHorizontal="true"
+                android:layout_marginRight="72dp"
+                android:contentDescription="@string/keyguard_accessibility_sim_pin_area"
+                androidprv:scaledTextSize="@integer/scaled_password_text_size" />
+        </com.android.keyguard.AlphaOptimizedRelativeLayout>
+
+        <include
+            android:id="@+id/keyguard_selector_fade_container"
+            layout="@layout/keyguard_eca"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center_horizontal"
+            android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+            android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+            android:gravity="center_horizontal"
+            android:orientation="vertical"
+            androidprv:layout_constraintBottom_toBottomOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/sim_pin_container"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="3"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical">
+
+        <androidx.constraintlayout.helper.widget.Flow
+            android:id="@+id/flow1"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:orientation="horizontal"
+            androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter"
+            androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end"
+            androidprv:flow_horizontalStyle="packed"
+            androidprv:flow_maxElementsWrap="3"
+            androidprv:flow_verticalBias="0.5"
+            androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom"
+            androidprv:flow_verticalStyle="packed"
+            androidprv:flow_wrapMode="aligned"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            androidprv:layout_constraintTop_toTopOf="parent"
+            androidprv:layout_constraintBottom_toBottomOf="parent"/>
+
+        <com.android.keyguard.NumPadButton
+            android:id="@+id/delete_button"
+            style="@style/NumPadKey.Delete"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key0"
+            android:contentDescription="@string/keyboardview_keycode_delete" />
+
+        <com.android.keyguard.NumPadButton
+            android:id="@+id/key_enter"
+            style="@style/NumPadKey.Enter"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:contentDescription="@string/keyboardview_keycode_enter" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key1"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key2"
+            androidprv:digit="1"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key2"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key3"
+            androidprv:digit="2"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key3"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key4"
+            androidprv:digit="3"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key4"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key5"
+            androidprv:digit="4"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key5"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key6"
+            androidprv:digit="5"
+            androidprv:textView="@+id/simPinEntry" />
+
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key6"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key7"
+            androidprv:digit="6"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key7"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key8"
+            androidprv:digit="7"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key8"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key9"
+            androidprv:digit="8"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key9"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/delete_button"
+            androidprv:digit="9"
+            androidprv:textView="@+id/simPinEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key0"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key_enter"
+            androidprv:digit="0"
+            androidprv:textView="@+id/simPinEntry" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_puk_view.xml
new file mode 100644
index 0000000..9012856
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout-land/keyguard_sim_puk_view.xml
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2024, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<!-- This is the SIM PUK view that allows the user to recover their device by entering the
+    carrier-provided PUK code and entering a new SIM PIN for it. -->
+<com.android.keyguard.KeyguardSimPukView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_sim_puk_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center_horizontal|bottom"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:orientation="horizontal">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="2"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical">
+
+        <include layout="@layout/keyguard_bouncer_message_area"/>
+
+        <ImageView
+            android:id="@+id/keyguard_sim"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_marginBottom="3dp"
+            android:src="@drawable/ic_lockscreen_sim"
+            androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            app:tint="@color/background_protected"/>
+
+        <include
+            android:id="@+id/keyguard_esim_area"
+            layout="@layout/keyguard_esim_area"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            androidprv:layout_constraintBottom_toTopOf="@+id/pin_entry_area"/>
+
+        <com.android.keyguard.AlphaOptimizedRelativeLayout
+            android:id="@+id/pin_entry_area"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
+            androidprv:layout_constraintBottom_toTopOf="@+id/keyguard_selector_fade_container">
+
+            <com.android.keyguard.PasswordTextView
+                android:id="@+id/pukEntry"
+                style="@style/Widget.TextView.Password"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/keyguard_password_height"
+                android:layout_centerHorizontal="true"
+                android:layout_marginRight="72dp"
+                android:contentDescription="@string/keyguard_accessibility_sim_pin_area"
+                androidprv:scaledTextSize="@integer/scaled_password_text_size" />
+        </com.android.keyguard.AlphaOptimizedRelativeLayout>
+
+        <include
+            android:id="@+id/keyguard_selector_fade_container"
+            layout="@layout/keyguard_eca"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center_horizontal"
+            android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin"
+            android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+            android:gravity="center_horizontal"
+            android:orientation="vertical"
+            androidprv:layout_constraintBottom_toBottomOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/sim_puk_container"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="3"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layoutDirection="ltr"
+        android:orientation="vertical">
+
+        <androidx.constraintlayout.helper.widget.Flow
+            android:id="@+id/flow1"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:orientation="horizontal"
+            androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter"
+            androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end"
+            androidprv:flow_horizontalStyle="packed"
+            androidprv:flow_maxElementsWrap="3"
+            androidprv:flow_verticalBias="0.5"
+            androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom"
+            androidprv:flow_verticalStyle="packed"
+            androidprv:flow_wrapMode="aligned"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            androidprv:layout_constraintTop_toTopOf="parent"
+            androidprv:layout_constraintBottom_toBottomOf="parent"/>
+
+        <com.android.keyguard.NumPadButton
+            android:id="@+id/delete_button"
+            style="@style/NumPadKey.Delete"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key0"
+            android:contentDescription="@string/keyboardview_keycode_delete" />
+
+        <com.android.keyguard.NumPadButton
+            android:id="@+id/key_enter"
+            style="@style/NumPadKey.Enter"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:contentDescription="@string/keyboardview_keycode_enter" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key1"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key2"
+            androidprv:digit="1"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key2"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key3"
+            androidprv:digit="2"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key3"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key4"
+            androidprv:digit="3"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key4"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key5"
+            androidprv:digit="4"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key5"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key6"
+            androidprv:digit="5"
+            androidprv:textView="@+id/pukEntry" />
+
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key6"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key7"
+            androidprv:digit="6"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key7"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key8"
+            androidprv:digit="7"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key8"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key9"
+            androidprv:digit="8"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key9"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/delete_button"
+            androidprv:digit="9"
+            androidprv:textView="@+id/pukEntry" />
+
+        <com.android.keyguard.NumPadKey
+            android:id="@+id/key0"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key_enter"
+            androidprv:digit="0"
+            androidprv:textView="@+id/pukEntry" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.keyguard.KeyguardSimPukView>
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
deleted file mode 100644
index e7a40d1..0000000
--- a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2024 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@*android:id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_weight="1"
-    android:minHeight="@*android:dimen/notification_headerless_min_height"
-    android:tag="enroute"
-    >
-
-    <include layout="@*android:layout/notification_template_material_base" />
-
-</com.android.systemui.statusbar.notification.row.ui.view.EnRouteView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml b/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml
deleted file mode 100644
index ca6d66a..0000000
--- a/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2014 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
-  -->
-<com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@*android:id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:clipChildren="false"
-    android:tag="big"
-    >
-
-    <LinearLayout
-        android:id="@*android:id/notification_action_list_margin_target"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="@*android:dimen/notification_content_margin"
-        android:orientation="vertical"
-        >
-
-        <FrameLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_gravity="top"
-            >
-
-            <include layout="@*android:layout/notification_template_header" />
-
-            <LinearLayout
-                android:id="@*android:id/notification_main_column"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="@*android:dimen/notification_content_margin_start"
-                android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
-                android:layout_marginTop="@*android:dimen/notification_content_margin_top"
-                android:orientation="vertical"
-                >
-
-                <include layout="@*android:layout/notification_template_part_line1" />
-
-                <include layout="@*android:layout/notification_template_text_multiline" />
-
-                <include
-                    android:layout_width="match_parent"
-                    android:layout_height="@*android:dimen/notification_progress_bar_height"
-                    android:layout_marginTop="@*android:dimen/notification_progress_margin_top"
-                    layout="@*android:layout/notification_template_progress"
-                    />
-            </LinearLayout>
-
-            <include layout="@*android:layout/notification_template_right_icon" />
-        </FrameLayout>
-
-        <ViewStub
-            android:layout="@*android:layout/notification_material_reply_text"
-            android:id="@*android:id/notification_material_reply_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            />
-
-        <include
-            layout="@*android:layout/notification_template_smart_reply_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@*android:dimen/notification_content_margin_start"
-            android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
-            android:layout_marginTop="@*android:dimen/notification_content_margin"
-            />
-
-        <include layout="@*android:layout/notification_material_action_list" />
-    </LinearLayout>
-</com.android.systemui.statusbar.notification.row.ui.view.EnRouteView>
diff --git a/packages/SystemUI/res/layout/rich_ongoing_timer_notification.xml b/packages/SystemUI/res/layout/rich_ongoing_timer_notification.xml
deleted file mode 100644
index 3a679e3..0000000
--- a/packages/SystemUI/res/layout/rich_ongoing_timer_notification.xml
+++ /dev/null
@@ -1,122 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2024 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<com.android.systemui.statusbar.notification.row.ui.view.TimerView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/topBaseline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_begin="22sp"
-        />
-
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        app:tint="@android:color/white"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/label"
-        android:baseline="18dp"
-        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
-        />
-    <TextView
-        android:id="@+id/label"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        app:layout_constraintStart_toEndOf="@id/icon"
-        app:layout_constraintEnd_toStartOf="@id/chronoRemaining"
-        android:singleLine="true"
-        tools:text="15s Timer"
-        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
-        android:paddingEnd="4dp"
-        />
-    <Chronometer
-        android:id="@+id/chronoRemaining"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:textSize="20sp"
-        android:gravity="end"
-        tools:text="0:12"
-        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
-        app:layout_constraintEnd_toStartOf="@id/pausedTimeRemaining"
-        app:layout_constraintStart_toEndOf="@id/label"
-        android:countDown="true"
-        android:paddingEnd="4dp"
-        />
-    <TextView
-        android:id="@+id/pausedTimeRemaining"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:textSize="20sp"
-        android:gravity="end"
-        tools:text="0:12"
-        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toEndOf="@id/chronoRemaining"
-        android:paddingEnd="4dp"
-        />
-
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/bottomOfTop"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        app:barrierDirection="bottom"
-        app:constraint_referenced_ids="icon,label,chronoRemaining,pausedTimeRemaining"
-        />
-
-    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
-        style="@*android:style/NotificationEmphasizedAction"
-        android:id="@+id/mainButton"
-        android:layout_width="124dp"
-        android:layout_height="wrap_content"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/altButton"
-        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
-        app:layout_constraintHorizontal_chainStyle="spread"
-        android:paddingEnd="4dp"
-        />
-
-    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
-        style="@*android:style/NotificationEmphasizedAction"
-        android:id="@+id/altButton"
-        android:layout_width="124dp"
-        android:layout_height="wrap_content"
-        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
-        app:layout_constraintStart_toEndOf="@id/mainButton"
-        app:layout_constraintEnd_toEndOf="@id/resetButton"
-        android:paddingEnd="4dp"
-        />
-
-    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
-        style="@*android:style/NotificationEmphasizedAction"
-        android:id="@+id/resetButton"
-        android:layout_width="124dp"
-        android:layout_height="wrap_content"
-        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
-        app:layout_constraintStart_toEndOf="@id/altButton"
-        app:layout_constraintEnd_toEndOf="parent"
-        android:paddingEnd="4dp"
-        />
-</com.android.systemui.statusbar.notification.row.ui.view.TimerView>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 1e9541e..6d1d9cb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -189,6 +189,7 @@
                     internalTransitionInteractor.currentTransitionInfoInternal,
                     keyguardInteractor.statusBarState,
                     keyguardInteractor.isKeyguardDismissible,
+                    keyguardInteractor.isKeyguardOccluded,
                 )
                 .collect {
                     (
@@ -196,7 +197,8 @@
                         startedStep,
                         currentTransitionInfo,
                         statusBarState,
-                        isKeyguardUnlocked) ->
+                        isKeyguardUnlocked,
+                        isKeyguardOccluded) ->
                     val id = transitionId
                     if (id != null) {
                         if (startedStep.to == KeyguardState.PRIMARY_BOUNCER) {
@@ -236,9 +238,13 @@
                             if (nextState == TransitionState.CANCELED) {
                                 transitionRepository.startTransition(
                                     TransitionInfo(
-                                        ownerName = name,
+                                        ownerName =
+                                            "$name " +
+                                                "(on behalf of FromPrimaryBouncerInteractor)",
                                         from = KeyguardState.PRIMARY_BOUNCER,
-                                        to = KeyguardState.LOCKSCREEN,
+                                        to =
+                                            if (isKeyguardOccluded) KeyguardState.OCCLUDED
+                                            else KeyguardState.LOCKSCREEN,
                                         modeOnCanceled = TransitionModeOnCanceled.REVERSE,
                                         animator =
                                             getDefaultAnimatorForTransitionsToState(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 5896659..2bff7c86 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -30,6 +30,7 @@
 import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.classifier.Classifier.UNLOCK;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.AOD;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE;
@@ -1213,6 +1214,16 @@
                     }, mMainDispatcher);
         }
 
+        if (MigrateClocksToBlueprint.isEnabled()) {
+            collectFlow(mView, mKeyguardTransitionInteractor.transition(
+                    Edge.Companion.create(AOD, LOCKSCREEN)),
+                    (TransitionStep step) -> {
+                    if (step.getTransitionState() == TransitionState.FINISHED) {
+                        updateExpandedHeightToMaxHeight();
+                    }
+                }, mMainDispatcher);
+        }
+
         // Ensures that flags are updated when an activity launches
         collectFlow(mView,
                 mShadeAnimationInteractor.isLaunchingActivity(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index cb133ec..e74ed8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -68,11 +68,9 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository;
 import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel;
 import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel;
 import com.android.systemui.statusbar.notification.stack.PriorityBucket;
 import com.android.systemui.util.ListenerSet;
 
@@ -99,7 +97,7 @@
  * At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
  * clean this up in the future.
  */
-public final class NotificationEntry extends ListEntry implements NotificationRowRepository {
+public final class NotificationEntry extends ListEntry {
 
     private final String mKey;
     private StatusBarNotification mSbn;
@@ -161,8 +159,6 @@
             StateFlowKt.MutableStateFlow(null);
     private final MutableStateFlow<CharSequence> mHeadsUpStatusBarTextPublic =
             StateFlowKt.MutableStateFlow(null);
-    private final MutableStateFlow<RichOngoingContentModel> mRichOngoingContentModel =
-            StateFlowKt.MutableStateFlow(null);
 
     // indicates when this entry's view was first attached to a window
     // this value will reset when the view is completely removed from the shade (ie: filtered out)
@@ -969,12 +965,6 @@
         return mHeadsUpStatusBarTextPublic;
     }
 
-    /** Gets the current RON content model, which may be null */
-    @NonNull
-    public StateFlow<RichOngoingContentModel> getRichOngoingContentModel() {
-        return mRichOngoingContentModel;
-    }
-
     /**
      * Sets the text to be displayed on the StatusBar, when this notification is the top pinned
      * heads up, and its content is sensitive right now.
@@ -1069,7 +1059,6 @@
         HeadsUpStatusBarModel headsUpStatusBarModel = contentModel.getHeadsUpStatusBarModel();
         this.mHeadsUpStatusBarText.setValue(headsUpStatusBarModel.getPrivateText());
         this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
-        this.mRichOngoingContentModel.setValue(contentModel.getRichOngoingContentModel());
     }
 
     /** Information about a suggestion that is being edited. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 48c974a..9166e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -76,8 +76,6 @@
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.DumpUtilsKt;
 
-import kotlinx.coroutines.DisposableHandle;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -116,10 +114,6 @@
     @VisibleForTesting
     protected HybridNotificationView mSingleLineView;
 
-    @Nullable public DisposableHandle mContractedBinderHandle;
-    @Nullable public DisposableHandle mExpandedBinderHandle;
-    @Nullable public DisposableHandle mHeadsUpBinderHandle;
-
     private RemoteInputView mExpandedRemoteInput;
     private RemoteInputView mHeadsUpRemoteInput;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index c342bcd..b166def 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -46,9 +46,6 @@
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.InflationException
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP
@@ -71,7 +68,6 @@
 import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews
 import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
 import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
@@ -95,8 +91,6 @@
     private val remoteViewCache: NotifRemoteViewCache,
     private val remoteInputManager: NotificationRemoteInputManager,
     private val conversationProcessor: ConversationNotificationProcessor,
-    private val ronExtractor: RichOngoingNotificationContentExtractor,
-    private val ronInflater: RichOngoingNotificationViewInflater,
     @NotifInflation private val inflationExecutor: Executor,
     private val smartReplyStateInflater: SmartReplyStateInflater,
     private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
@@ -144,8 +138,6 @@
                 remoteViewCache,
                 entry,
                 conversationProcessor,
-                ronExtractor,
-                ronInflater,
                 row,
                 bindParams.isMinimized,
                 bindParams.usesIncreasedHeight,
@@ -190,7 +182,6 @@
                 notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
                 headsUpStyleProvider = headsUpStyleProvider,
                 conversationProcessor = conversationProcessor,
-                ronExtractor = ronExtractor,
                 logger = logger,
             )
         inflateSmartReplyViews(
@@ -282,22 +273,16 @@
         when (inflateFlag) {
             FLAG_CONTENT_VIEW_CONTRACTED ->
                 row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_CONTRACTED) {
-                    row.privateLayout.mContractedBinderHandle?.dispose()
-                    row.privateLayout.mContractedBinderHandle = null
                     row.privateLayout.setContractedChild(null)
                     remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
                 }
             FLAG_CONTENT_VIEW_EXPANDED ->
                 row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_EXPANDED) {
-                    row.privateLayout.mExpandedBinderHandle?.dispose()
-                    row.privateLayout.mExpandedBinderHandle = null
                     row.privateLayout.setExpandedChild(null)
                     remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
                 }
             FLAG_CONTENT_VIEW_HEADS_UP ->
                 row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_HEADSUP) {
-                    row.privateLayout.mHeadsUpBinderHandle?.dispose()
-                    row.privateLayout.mHeadsUpBinderHandle = null
                     row.privateLayout.setHeadsUpChild(null)
                     remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
                     row.privateLayout.setHeadsUpInflatedSmartReplies(null)
@@ -378,8 +363,6 @@
         private val remoteViewCache: NotifRemoteViewCache,
         private val entry: NotificationEntry,
         private val conversationProcessor: ConversationNotificationProcessor,
-        private val ronExtractor: RichOngoingNotificationContentExtractor,
-        private val ronInflater: RichOngoingNotificationViewInflater,
         private val row: ExpandableNotificationRow,
         private val isMinimized: Boolean,
         private val usesIncreasedHeight: Boolean,
@@ -459,7 +442,6 @@
                     notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
                     headsUpStyleProvider = headsUpStyleProvider,
                     conversationProcessor = conversationProcessor,
-                    ronExtractor = ronExtractor,
                     logger = logger
                 )
             logger.logAsyncTaskProgress(
@@ -506,90 +488,6 @@
                     }
             }
 
-            val richOngoingContentModel = inflationProgress.contentModel.richOngoingContentModel
-
-            if (
-                richOngoingContentModel != null &&
-                    reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0
-            ) {
-                logger.logAsyncTaskProgress(entry, "inflating RON view")
-                val inflateContractedView = reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0
-                val inflateExpandedView = reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0
-                val inflateHeadsUpView = reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0
-
-                inflationProgress.contractedRichOngoingNotificationViewHolder =
-                    if (inflateContractedView) {
-                        ronInflater.inflateView(
-                            contentModel = richOngoingContentModel,
-                            existingView = row.privateLayout.contractedChild,
-                            entry = entry,
-                            systemUiContext = context,
-                            parentView = row.privateLayout,
-                            viewType = RichOngoingNotificationViewType.Contracted
-                        )
-                    } else {
-                        if (
-                            ronInflater.canKeepView(
-                                contentModel = richOngoingContentModel,
-                                existingView = row.privateLayout.contractedChild,
-                                viewType = RichOngoingNotificationViewType.Contracted
-                            )
-                        ) {
-                            KeepExistingView
-                        } else {
-                            NullContentView
-                        }
-                    }
-
-                inflationProgress.expandedRichOngoingNotificationViewHolder =
-                    if (inflateExpandedView) {
-                        ronInflater.inflateView(
-                            contentModel = richOngoingContentModel,
-                            existingView = row.privateLayout.expandedChild,
-                            entry = entry,
-                            systemUiContext = context,
-                            parentView = row.privateLayout,
-                            viewType = RichOngoingNotificationViewType.Expanded
-                        )
-                    } else {
-                        if (
-                            ronInflater.canKeepView(
-                                contentModel = richOngoingContentModel,
-                                existingView = row.privateLayout.expandedChild,
-                                viewType = RichOngoingNotificationViewType.Expanded
-                            )
-                        ) {
-                            KeepExistingView
-                        } else {
-                            NullContentView
-                        }
-                    }
-
-                inflationProgress.headsUpRichOngoingNotificationViewHolder =
-                    if (inflateHeadsUpView) {
-                        ronInflater.inflateView(
-                            contentModel = richOngoingContentModel,
-                            existingView = row.privateLayout.headsUpChild,
-                            entry = entry,
-                            systemUiContext = context,
-                            parentView = row.privateLayout,
-                            viewType = RichOngoingNotificationViewType.HeadsUp
-                        )
-                    } else {
-                        if (
-                            ronInflater.canKeepView(
-                                contentModel = richOngoingContentModel,
-                                existingView = row.privateLayout.headsUpChild,
-                                viewType = RichOngoingNotificationViewType.HeadsUp
-                            )
-                        ) {
-                            KeepExistingView
-                        } else {
-                            NullContentView
-                        }
-                    }
-            }
-
             logger.logAsyncTaskProgress(entry, "getting row image resolver (on wrong thread!)")
             val imageResolver = row.imageResolver
             // wait for image resolver to finish preloading
@@ -695,9 +593,6 @@
         var inflatedSmartReplyState: InflatedSmartReplyState? = null
         var expandedInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
         var headsUpInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
-        var contractedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null
-        var expandedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null
-        var headsUpRichOngoingNotificationViewHolder: ContentViewInflationResult? = null
 
         // Inflated SingleLineView that lacks the UI State
         var inflatedSingleLineView: HybridNotificationView? = null
@@ -734,7 +629,6 @@
             val inflateHeadsUp =
                 (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0 &&
                     result.remoteViews.headsUp != null)
-
             if (inflateContracted || inflateExpanded || inflateHeadsUp) {
                 logger.logAsyncTaskProgress(entry, "inflating contracted smart reply state")
                 result.inflatedSmartReplyState = inflater.inflateSmartReplyState(entry)
@@ -776,7 +670,6 @@
             notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
             headsUpStyleProvider: HeadsUpStyleProvider,
             conversationProcessor: ConversationNotificationProcessor,
-            ronExtractor: RichOngoingNotificationContentExtractor,
             logger: NotificationRowContentBinderLogger
         ): InflationProgress {
             // process conversations and extract the messaging style
@@ -785,24 +678,9 @@
                     conversationProcessor.processNotification(entry, builder, logger)
                 } else null
 
-            val richOngoingContentModel =
-                if (reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0) {
-                    ronExtractor.extractContentModel(
-                        entry = entry,
-                        builder = builder,
-                        systemUIContext = systemUIContext,
-                        packageContext = packageContext
-                    )
-                } else {
-                    // if we're not re-inflating any RON views, make sure the model doesn't change
-                    entry.richOngoingContentModel.value
-                }
-
-            val remoteViewsFlags = getRemoteViewsFlags(reInflateFlags, richOngoingContentModel)
-
             val remoteViews =
                 createRemoteViews(
-                    reInflateFlags = remoteViewsFlags,
+                    reInflateFlags = reInflateFlags,
                     builder = builder,
                     isMinimized = isMinimized,
                     usesIncreasedHeight = usesIncreasedHeight,
@@ -850,7 +728,6 @@
                     headsUpStatusBarModel = headsUpStatusBarModel,
                     singleLineViewModel = singleLineViewModel,
                     publicSingleLineViewModel = publicSingleLineViewModel,
-                    richOngoingContentModel = richOngoingContentModel,
                 )
 
             return InflationProgress(
@@ -1506,31 +1383,11 @@
             }
             logger.logAsyncTaskProgress(entry, "finishing")
 
-            // before updating the content model, stop existing binding if necessary
-            if (result.contractedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) {
-                row.privateLayout.mContractedBinderHandle?.dispose()
-                row.privateLayout.mContractedBinderHandle = null
-            }
-
-            if (result.expandedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) {
-                row.privateLayout.mExpandedBinderHandle?.dispose()
-                row.privateLayout.mExpandedBinderHandle = null
-            }
-
-            if (result.headsUpRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) {
-                row.privateLayout.mHeadsUpBinderHandle?.dispose()
-                row.privateLayout.mHeadsUpBinderHandle = null
-            }
-
-            // set the content model after disposal and before setting new rich ongoing view
             entry.setContentModel(result.contentModel)
             result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
 
-            // set normal remote views (skipping rich ongoing states when that model exists)
-            val remoteViewsFlags =
-                getRemoteViewsFlags(reInflateFlags, result.contentModel.richOngoingContentModel)
             setContentViewsFromRemoteViews(
-                remoteViewsFlags,
+                reInflateFlags,
                 entry,
                 remoteViewCache,
                 result,
@@ -1538,7 +1395,6 @@
                 isMinimized,
             )
 
-            // set single line view
             if (
                 AsyncHybridViewInflation.isEnabled &&
                     reInflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE != 0
@@ -1563,55 +1419,6 @@
                 }
             }
 
-            val hasRichOngoingViewHolder =
-                result.contractedRichOngoingNotificationViewHolder != null ||
-                    result.expandedRichOngoingNotificationViewHolder != null ||
-                    result.headsUpRichOngoingNotificationViewHolder != null
-
-            if (hasRichOngoingViewHolder) {
-                // after updating the content model, set the view, then start the new binder
-                result.contractedRichOngoingNotificationViewHolder?.let { contractedViewHolder ->
-                    if (contractedViewHolder is InflatedContentViewHolder) {
-                        row.privateLayout.contractedChild = contractedViewHolder.view
-                        row.privateLayout.mContractedBinderHandle =
-                            contractedViewHolder.binder.setupContentViewBinder()
-                    } else if (contractedViewHolder == NullContentView) {
-                        row.privateLayout.contractedChild = null
-                    }
-                }
-
-                result.expandedRichOngoingNotificationViewHolder?.let { expandedViewHolder ->
-                    if (expandedViewHolder is InflatedContentViewHolder) {
-                        row.privateLayout.expandedChild = expandedViewHolder.view
-                        row.privateLayout.mExpandedBinderHandle =
-                            expandedViewHolder.binder.setupContentViewBinder()
-                    } else if (expandedViewHolder == NullContentView) {
-                        row.privateLayout.expandedChild = null
-                    }
-                }
-
-                result.headsUpRichOngoingNotificationViewHolder?.let { headsUpViewHolder ->
-                    if (headsUpViewHolder is InflatedContentViewHolder) {
-                        row.privateLayout.headsUpChild = headsUpViewHolder.view
-                        row.privateLayout.mHeadsUpBinderHandle =
-                            headsUpViewHolder.binder.setupContentViewBinder()
-                    } else if (headsUpViewHolder == NullContentView) {
-                        row.privateLayout.headsUpChild = null
-                    }
-                }
-
-                // clean remoteViewCache when we don't keep existing views.
-                remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
-                remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
-                remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
-
-                // Since RONs don't support smart reply, remove them from HUNs and Expanded.
-                row.privateLayout.setExpandedInflatedSmartReplies(null)
-                row.privateLayout.setHeadsUpInflatedSmartReplies(null)
-
-                row.setExpandable(row.privateLayout.expandedChild != null)
-            }
-
             Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
             endListener?.onAsyncInflationFinished(entry)
             return true
@@ -1775,21 +1582,6 @@
                     !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED)
         }
 
-        @InflationFlag
-        private fun getRemoteViewsFlags(
-            @InflationFlag reInflateFlags: Int,
-            richOngoingContentModel: RichOngoingContentModel?
-        ): Int =
-            if (richOngoingContentModel != null) {
-                reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING.inv()
-            } else {
-                reInflateFlags
-            }
-
-        @InflationFlag
-        private const val CONTENT_VIEWS_TO_CREATE_RICH_ONGOING =
-            FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
-
         private const val ASYNC_TASK_TRACE_METHOD =
             "NotificationRowContentBinderImpl.AsyncInflationTask"
         private const val APPLY_TRACE_METHOD = "NotificationRowContentBinderImpl#apply"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index c630c4d..84f2f66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -18,8 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag;
-import com.android.systemui.statusbar.notification.row.ui.viewmodel.RichOngoingViewModelComponent;
 
 import dagger.Binds;
 import dagger.Module;
@@ -30,7 +28,7 @@
 /**
  * Dagger Module containing notification row and view inflation implementations.
  */
-@Module(subcomponents = {RichOngoingViewModelComponent.class})
+@Module
 public abstract class NotificationRowModule {
 
     /**
@@ -49,25 +47,6 @@
         }
     }
 
-    /** Provides ron content model extractor. */
-    @Provides
-    @SysUISingleton
-    public static RichOngoingNotificationContentExtractor provideRonContentExtractor(
-            Provider<RichOngoingNotificationContentExtractorImpl> realImpl
-    ) {
-        if (RichOngoingNotificationFlag.isEnabled()) {
-            return realImpl.get();
-        } else {
-            return new NoOpRichOngoingNotificationContentExtractor();
-        }
-    }
-
-    /** Provides ron view inflater. */
-    @Binds
-    @SysUISingleton
-    public abstract RichOngoingNotificationViewInflater provideRonViewInflater(
-            RichOngoingNotificationViewInflaterImpl impl);
-
     /**
      * Provides notification remote view cache instance.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
deleted file mode 100644
index ec5ebc36..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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.row
-
-import android.app.Notification
-import android.app.PendingIntent
-import android.content.Context
-import android.util.Log
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
-import com.android.systemui.statusbar.notification.row.shared.IconModel
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
-import java.time.Duration
-import java.time.LocalDate
-import java.time.LocalDateTime
-import java.time.LocalTime
-import java.time.ZoneId
-import javax.inject.Inject
-
-/**
- * Interface which provides a [RichOngoingContentModel] for a given [Notification] when one is
- * applicable to the given style.
- */
-interface RichOngoingNotificationContentExtractor {
-    fun extractContentModel(
-        entry: NotificationEntry,
-        builder: Notification.Builder,
-        systemUIContext: Context,
-        packageContext: Context,
-    ): RichOngoingContentModel?
-}
-
-class NoOpRichOngoingNotificationContentExtractor : RichOngoingNotificationContentExtractor {
-    override fun extractContentModel(
-        entry: NotificationEntry,
-        builder: Notification.Builder,
-        systemUIContext: Context,
-        packageContext: Context,
-    ): RichOngoingContentModel? = null
-}
-
-@SysUISingleton
-class RichOngoingNotificationContentExtractorImpl @Inject constructor() :
-    RichOngoingNotificationContentExtractor {
-
-    init {
-        /* check if */ RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()
-    }
-
-    override fun extractContentModel(
-        entry: NotificationEntry,
-        builder: Notification.Builder,
-        systemUIContext: Context,
-        packageContext: Context,
-    ): RichOngoingContentModel? {
-        val sbn = entry.sbn
-        val notification = sbn.notification
-        val icon = IconModel(notification.smallIcon)
-
-        try {
-            return if (sbn.packageName == "com.google.android.deskclock") {
-                when (notification.channelId) {
-                    "Timers v2" -> {
-                        parseTimerNotification(notification, icon)
-                    }
-                    "Stopwatch v2" -> {
-                        Log.i("RONs", "Can't process stopwatch yet")
-                        null
-                    }
-                    else -> {
-                        Log.i("RONs", "Can't process channel '${notification.channelId}'")
-                        null
-                    }
-                }
-            } else if (builder.style is Notification.ProgressStyle) {
-                parseEnRouteNotification(notification, icon)
-            } else null
-        } catch (e: Exception) {
-            Log.e("RONs", "Error parsing RON", e)
-            return null
-        }
-    }
-
-    /**
-     * FOR PROTOTYPING ONLY: create a RON TimerContentModel using the time information available
-     * inside the sortKey of the clock app's timer notifications.
-     */
-    private fun parseTimerNotification(
-        notification: Notification,
-        icon: IconModel,
-    ): TimerContentModel {
-        // sortKey=1 0|↺7|RUNNING|▶16:21:58.523|Σ0:05:00|Δ0:00:03|⏳0:04:57
-        // sortKey=1 0|↺7|PAUSED|Σ0:05:00|Δ0:04:54|⏳0:00:06
-        // sortKey=1 1|↺7|RUNNING|▶16:30:28.433|Σ0:04:05|Δ0:00:06|⏳0:03:59
-        // sortKey=1 0|↺7|RUNNING|▶16:36:18.350|Σ0:05:00|Δ0:01:42|⏳0:03:18
-        // sortKey=1 2|↺7|RUNNING|▶16:38:37.816|Σ0:02:00|Δ0:01:09|⏳0:00:51
-        // ▶ = "current" time (when updated)
-        // Σ = total time
-        // Δ = time elapsed
-        // ⏳ = time remaining
-        val sortKey = notification.sortKey
-        val (_, _, state, extra) = sortKey.split("|", limit = 4)
-        return when (state) {
-            "PAUSED" -> {
-                val (total, _, remaining) = extra.split("|")
-                val timeRemaining = parseTimeDelta(remaining)
-                TimerContentModel(
-                    icon = icon,
-                    // TODO: b/352142761 - define and use a string resource rather than " Timer".
-                    // (The UX isn't final so using " Timer" for now).
-                    name = total.replace("Σ", "") + " Timer",
-                    state =
-                        TimerContentModel.TimerState.Paused(
-                            timeRemaining = timeRemaining,
-                            resumeIntent = notification.findStartIntent(),
-                            addMinuteAction = notification.findAddMinuteAction(),
-                            resetAction = notification.findResetAction(),
-                        ),
-                )
-            }
-            "RUNNING" -> {
-                val (current, total, _, remaining) = extra.split("|")
-                val finishTime = parseCurrentTime(current) + parseTimeDelta(remaining).toMillis()
-                TimerContentModel(
-                    icon = icon,
-                    // TODO: b/352142761 - define and use a string resource rather than " Timer".
-                    // (The UX isn't final so using " Timer" for now).
-                    name = total.replace("Σ", "") + " Timer",
-                    state =
-                        TimerContentModel.TimerState.Running(
-                            finishTime = finishTime,
-                            pauseIntent = notification.findPauseIntent(),
-                            addMinuteAction = notification.findAddMinuteAction(),
-                            resetAction = notification.findResetAction(),
-                        ),
-                )
-            }
-            else -> error("unknown state ($state) in sortKey=$sortKey")
-        }
-    }
-
-    private fun Notification.findPauseIntent(): PendingIntent? {
-        return actions
-            .firstOrNull { it.actionIntent.intent?.action?.endsWith(".PAUSE_TIMER") == true }
-            ?.actionIntent
-    }
-
-    private fun Notification.findStartIntent(): PendingIntent? {
-        return actions
-            .firstOrNull { it.actionIntent.intent?.action?.endsWith(".START_TIMER") == true }
-            ?.actionIntent
-    }
-
-    // TODO: b/352142761 - switch to system attributes for label and icon.
-    //   - We probably want a consistent look for the Reset button. (Double check with UX.)
-    //   - Using the custom assets now since I couldn't an existing "Reset" icon.
-    private fun Notification.findResetAction(): Notification.Action? {
-        return actions.firstOrNull {
-            it.actionIntent.intent?.action?.endsWith(".RESET_TIMER") == true
-        }
-    }
-
-    // TODO: b/352142761 - check with UX on whether this should be required.
-    //   - Alternative is to allow for optional actions in addition to main and reset.
-    //   - For optional actions, we should take the custom label and icon.
-    private fun Notification.findAddMinuteAction(): Notification.Action? {
-        return actions.firstOrNull {
-            it.actionIntent.intent?.action?.endsWith(".ADD_MINUTE_TIMER") == true
-        }
-    }
-
-    private fun parseCurrentTime(current: String): Long {
-        val (hour, minute, second, millis) = current.replace("▶", "").split(":", ".")
-        // NOTE: this won't work correctly at/around midnight.  It's just for prototyping.
-        val localDateTime =
-            LocalDateTime.of(
-                LocalDate.now(),
-                LocalTime.of(hour.toInt(), minute.toInt(), second.toInt(), millis.toInt() * 1000000),
-            )
-        val offset = ZoneId.systemDefault().rules.getOffset(localDateTime)
-        return localDateTime.toInstant(offset).toEpochMilli()
-    }
-
-    private fun parseTimeDelta(delta: String): Duration {
-        val (hour, minute, second) = delta.replace("Σ", "").replace("⏳", "").split(":")
-        return Duration.ofHours(hour.toLong())
-            .plusMinutes(minute.toLong())
-            .plusSeconds(second.toLong())
-    }
-
-    private fun parseEnRouteNotification(
-        notification: Notification,
-        icon: IconModel,
-    ): EnRouteContentModel {
-        return EnRouteContentModel(
-            smallIcon = icon,
-            title = notification.extras.getCharSequence(Notification.EXTRA_TITLE),
-            text = notification.extras.getCharSequence(Notification.EXTRA_TEXT),
-        )
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
deleted file mode 100644
index 77c4130..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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.row
-
-import android.app.Notification
-import android.content.Context
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView
-import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
-import com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
-import com.android.systemui.statusbar.notification.row.ui.view.TimerView
-import com.android.systemui.statusbar.notification.row.ui.viewbinder.EnRouteViewBinder
-import com.android.systemui.statusbar.notification.row.ui.viewbinder.TimerViewBinder
-import com.android.systemui.statusbar.notification.row.ui.viewmodel.EnRouteViewModel
-import com.android.systemui.statusbar.notification.row.ui.viewmodel.RichOngoingViewModelComponent
-import com.android.systemui.statusbar.notification.row.ui.viewmodel.TimerViewModel
-import javax.inject.Inject
-import kotlinx.coroutines.DisposableHandle
-
-fun interface DeferredContentViewBinder {
-    fun setupContentViewBinder(): DisposableHandle
-}
-
-enum class RichOngoingNotificationViewType {
-    Contracted,
-    Expanded,
-    HeadsUp,
-}
-
-/**
- * * Supertype of the 3 different possible result types of
- *   [RichOngoingNotificationViewInflater.inflateView].
- */
-sealed interface ContentViewInflationResult {
-
-    /** Indicates that the content view should be removed if present. */
-    data object NullContentView : ContentViewInflationResult
-
-    /**
-     * Indicates that the content view (which *must be* present) should be unmodified during this
-     * inflation.
-     */
-    data object KeepExistingView : ContentViewInflationResult
-
-    /**
-     * Contains the new view and binder that should replace any existing content view for this slot.
-     */
-    data class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder) :
-        ContentViewInflationResult
-}
-
-fun ContentViewInflationResult?.shouldDisposeViewBinder() = this !is KeepExistingView
-
-/**
- * Interface which provides a [RichOngoingContentModel] for a given [Notification] when one is
- * applicable to the given style.
- */
-interface RichOngoingNotificationViewInflater {
-    fun inflateView(
-        contentModel: RichOngoingContentModel,
-        existingView: View?,
-        entry: NotificationEntry,
-        systemUiContext: Context,
-        parentView: ViewGroup,
-        viewType: RichOngoingNotificationViewType,
-    ): ContentViewInflationResult
-
-    fun canKeepView(
-        contentModel: RichOngoingContentModel,
-        existingView: View?,
-        viewType: RichOngoingNotificationViewType
-    ): Boolean
-}
-
-@SysUISingleton
-class RichOngoingNotificationViewInflaterImpl
-@Inject
-constructor(
-    private val viewModelComponentFactory: RichOngoingViewModelComponent.Factory,
-) : RichOngoingNotificationViewInflater {
-
-    override fun inflateView(
-        contentModel: RichOngoingContentModel,
-        existingView: View?,
-        entry: NotificationEntry,
-        systemUiContext: Context,
-        parentView: ViewGroup,
-        viewType: RichOngoingNotificationViewType,
-    ): ContentViewInflationResult {
-        if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return NullContentView
-        val component = viewModelComponentFactory.create(entry)
-        return when (contentModel) {
-            is TimerContentModel ->
-                inflateTimerView(
-                    existingView,
-                    component::createTimerViewModel,
-                    systemUiContext,
-                    parentView,
-                    viewType
-                )
-            is EnRouteContentModel ->
-                inflateEnRouteView(
-                    existingView,
-                    component::createEnRouteViewModel,
-                    systemUiContext,
-                    parentView,
-                    viewType
-                )
-            else -> TODO("Not yet implemented")
-        }
-    }
-
-    override fun canKeepView(
-        contentModel: RichOngoingContentModel,
-        existingView: View?,
-        viewType: RichOngoingNotificationViewType
-    ): Boolean {
-        if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return false
-        return when (contentModel) {
-            is TimerContentModel -> canKeepTimerView(contentModel, existingView, viewType)
-            is EnRouteContentModel -> canKeepEnRouteView(contentModel, existingView, viewType)
-            else -> TODO("Not yet implemented")
-        }
-    }
-
-    private fun inflateTimerView(
-        existingView: View?,
-        createViewModel: () -> TimerViewModel,
-        systemUiContext: Context,
-        parentView: ViewGroup,
-        viewType: RichOngoingNotificationViewType,
-    ): ContentViewInflationResult {
-        if (existingView is TimerView && !existingView.isReinflateNeeded()) return KeepExistingView
-
-        return when (viewType) {
-            RichOngoingNotificationViewType.Contracted -> {
-                val newView =
-                    LayoutInflater.from(systemUiContext)
-                        .inflate(
-                            R.layout.rich_ongoing_timer_notification,
-                            parentView,
-                            /* attachToRoot= */ false
-                        ) as TimerView
-                InflatedContentViewHolder(newView) {
-                    TimerViewBinder.bindWhileAttached(newView, createViewModel())
-                }
-            }
-            RichOngoingNotificationViewType.Expanded,
-            RichOngoingNotificationViewType.HeadsUp -> NullContentView
-        }
-    }
-
-    private fun canKeepTimerView(
-        contentModel: TimerContentModel,
-        existingView: View?,
-        viewType: RichOngoingNotificationViewType
-    ): Boolean = true
-
-    private fun inflateEnRouteView(
-        existingView: View?,
-        createViewModel: () -> EnRouteViewModel,
-        systemUiContext: Context,
-        parentView: ViewGroup,
-        viewType: RichOngoingNotificationViewType,
-    ): ContentViewInflationResult {
-        if (existingView is EnRouteView && !existingView.isReinflateNeeded())
-            return KeepExistingView
-        return when (viewType) {
-            RichOngoingNotificationViewType.Contracted -> {
-                val newView =
-                    LayoutInflater.from(systemUiContext)
-                        .inflate(
-                            R.layout.notification_template_en_route_contracted,
-                            parentView,
-                            /* attachToRoot= */ false
-                        ) as EnRouteView
-                InflatedContentViewHolder(newView) {
-                    EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
-                }
-            }
-            RichOngoingNotificationViewType.Expanded -> {
-                val newView =
-                    LayoutInflater.from(systemUiContext)
-                        .inflate(
-                            R.layout.notification_template_en_route_expanded,
-                            parentView,
-                            /* attachToRoot= */ false
-                        ) as EnRouteView
-                InflatedContentViewHolder(newView) {
-                    EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
-                }
-            }
-            RichOngoingNotificationViewType.HeadsUp -> NullContentView
-        }
-    }
-
-    private fun canKeepEnRouteView(
-        contentModel: EnRouteContentModel,
-        existingView: View?,
-        viewType: RichOngoingNotificationViewType
-    ): Boolean = true
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepository.kt
deleted file mode 100644
index bac887b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepository.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.row.data.repository
-
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
-import kotlinx.coroutines.flow.StateFlow
-
-/** A repository of states relating to a specific notification row. */
-interface NotificationRowRepository {
-    /**
-     * A flow of an immutable data class with the current state of the Rich Ongoing Notification
-     * content, if applicable.
-     */
-    val richOngoingContentModel: StateFlow<RichOngoingContentModel?>
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
deleted file mode 100644
index 72823a7..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.row.domain.interactor
-
-import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
-import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
-import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filterIsInstance
-
-/** Interactor specific to a particular notification row. */
-class NotificationRowInteractor @Inject constructor(repository: NotificationRowRepository) {
-    /** Content of a rich ongoing timer notification. */
-    val timerContentModel: Flow<TimerContentModel> =
-        repository.richOngoingContentModel.filterIsInstance<TimerContentModel>()
-
-    /** Content of a rich ongoing timer notification. */
-    val enRouteContentModel: Flow<EnRouteContentModel> =
-        repository.richOngoingContentModel.filterIsInstance<EnRouteContentModel>()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt
deleted file mode 100644
index 7e78cca..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.row.shared
-
-/**
- * Represents something en route.
- *
- * @param smallIcon the main small icon of the EnRoute notification.
- * @param title the title of the EnRoute notification.
- * @param text the text of the EnRoute notification.
- */
-data class EnRouteContentModel(
-    val smallIcon: IconModel,
-    val title: CharSequence?,
-    val text: CharSequence?,
-) : RichOngoingContentModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/IconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/IconModel.kt
deleted file mode 100644
index e611938..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/IconModel.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.row.shared
-
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.Icon
-
-// TODO: figure out how to support lazy resolution of the drawable, e.g. on unrelated text change
-class IconModel(val icon: Icon) {
-    var drawable: Drawable? = null
-
-    override fun equals(other: Any?): Boolean =
-        when (other) {
-            null -> false
-            (other === this) -> true
-            !is IconModel -> false
-            else -> other.icon.sameAs(icon)
-        }
-
-    override fun toString(): String = "IconModel(icon=$icon, drawable=$drawable)"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
index 0f9a5a3..004c66b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
@@ -22,7 +22,4 @@
     val headsUpStatusBarModel: HeadsUpStatusBarModel,
     val singleLineViewModel: SingleLineViewModel? = null,
     val publicSingleLineViewModel: SingleLineViewModel? = null,
-    val richOngoingContentModel: RichOngoingContentModel? = null,
 )
-
-sealed interface RichOngoingContentModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingClock.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingClock.kt
deleted file mode 100644
index 33b2564..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingClock.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.row.shared
-
-import android.app.Notification
-import android.app.PendingIntent
-import java.time.Duration
-
-/**
- * Represents a simple timer that counts down to a time.
- *
- * @param name the label for the timer
- * @param state state of the timer, including time and whether it is paused or running
- */
-data class TimerContentModel(
-    val icon: IconModel,
-    val name: String,
-    val state: TimerState,
-) : RichOngoingContentModel {
-    /** The state (paused or running) of the timer, and relevant time */
-    sealed interface TimerState {
-        val addMinuteAction: Notification.Action?
-        val resetAction: Notification.Action?
-
-        /**
-         * Indicates a running timer
-         *
-         * @param finishTime the time in ms since epoch that the timer will finish
-         * @param pauseIntent the action for pausing the timer
-         */
-        data class Running(
-            val finishTime: Long,
-            val pauseIntent: PendingIntent?,
-            override val addMinuteAction: Notification.Action?,
-            override val resetAction: Notification.Action?,
-        ) : TimerState
-
-        /**
-         * Indicates a paused timer
-         *
-         * @param timeRemaining the time in ms remaining on the paused timer
-         * @param resumeIntent the action for resuming the timer
-         */
-        data class Paused(
-            val timeRemaining: Duration,
-            val resumeIntent: PendingIntent?,
-            override val addMinuteAction: Notification.Action?,
-            override val resetAction: Notification.Action?,
-        ) : TimerState
-    }
-}
-
-/**
- * Represents a simple stopwatch that counts up and allows tracking laps.
- *
- * @param state state of the stopwatch, including time and whether it is paused or running
- * @param lapDurations a list of durations of each completed lap
- */
-data class StopwatchContentModel(
-    val icon: IconModel,
-    val state: StopwatchState,
-    val lapDurations: List<Long>,
-) : RichOngoingContentModel {
-    /** The state (paused or running) of the stopwatch, and relevant time */
-    sealed interface StopwatchState {
-        /**
-         * Indicates a running stopwatch
-         *
-         * @param startTime the time in ms since epoch that the stopwatch started, plus any
-         *   accumulated pause time
-         * @param pauseIntent the action for pausing the stopwatch
-         */
-        data class Running(
-            val startTime: Long,
-            val pauseIntent: PendingIntent,
-        ) : StopwatchState
-
-        /**
-         * Indicates a paused stopwatch
-         *
-         * @param timeElapsed the time in ms elapsed on the stopwatch
-         * @param resumeIntent the action for resuming the stopwatch
-         */
-        data class Paused(
-            val timeElapsed: Duration,
-            val resumeIntent: PendingIntent,
-        ) : StopwatchState
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingNotificationFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingNotificationFlag.kt
deleted file mode 100644
index 4a7f7cd..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingNotificationFlag.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.row.shared
-
-import android.app.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the api rich ongoing flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object RichOngoingNotificationFlag {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_API_RICH_ONGOING
-
-    /** A token used for dependency declaration */
-    val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
-
-    /** Is the refactor enabled */
-    @JvmStatic
-    inline val isEnabled
-        get() = Flags.apiRichOngoing()
-
-    /**
-     * Called to ensure code is only run when the flag is enabled. This protects users from the
-     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
-     * build to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun isUnexpectedlyInLegacyMode() =
-        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
-    /**
-     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
-     * the flag is enabled to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/ConfigurationTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/ConfigurationTracker.kt
deleted file mode 100644
index 95c507c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/ConfigurationTracker.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.row.ui.view
-
-import android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS
-import android.content.pm.ActivityInfo.CONFIG_DENSITY
-import android.content.pm.ActivityInfo.CONFIG_FONT_SCALE
-import android.content.pm.ActivityInfo.CONFIG_LAYOUT_DIRECTION
-import android.content.pm.ActivityInfo.CONFIG_LOCALE
-import android.content.pm.ActivityInfo.CONFIG_UI_MODE
-import android.content.res.Configuration
-import android.content.res.Resources
-
-/**
- * Tracks the active configuration when constructed and returns (when queried) whether the
- * configuration has unhandled changes.
- */
-class ConfigurationTracker(
-    private val resources: Resources,
-    private val unhandledConfigChanges: Int
-) {
-    private val initialConfig = Configuration(resources.configuration)
-
-    constructor(
-        resources: Resources,
-        handlesDensityFontScale: Boolean = false,
-        handlesTheme: Boolean = false,
-        handlesLocaleAndLayout: Boolean = true,
-    ) : this(
-        resources,
-        unhandledConfigChanges =
-            (if (handlesDensityFontScale) 0 else CONFIG_DENSITY or CONFIG_FONT_SCALE) or
-                (if (handlesTheme) 0 else CONFIG_ASSETS_PATHS or CONFIG_UI_MODE) or
-                (if (handlesLocaleAndLayout) 0 else CONFIG_LOCALE or CONFIG_LAYOUT_DIRECTION)
-    )
-
-    /**
-     * Whether the current configuration has unhandled changes relative to the initial configuration
-     */
-    fun hasUnhandledConfigChange(): Boolean =
-        initialConfig.diff(resources.configuration) and unhandledConfigChanges != 0
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt
deleted file mode 100644
index e5c2b5f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.row.ui.view
-
-import android.content.Context
-import android.graphics.drawable.Icon
-import android.util.AttributeSet
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.TextView
-import com.android.internal.R
-import com.android.internal.widget.NotificationExpandButton
-
-class EnRouteView
-@JvmOverloads
-constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0,
-    defStyleRes: Int = 0,
-) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
-
-    private val configTracker = ConfigurationTracker(resources)
-
-    private lateinit var icon: ImageView
-    private lateinit var title: TextView
-    private lateinit var text: TextView
-    private lateinit var expandButton: NotificationExpandButton
-
-    override fun onFinishInflate() {
-        super.onFinishInflate()
-        icon = requireViewById(R.id.icon)
-        title = requireViewById(R.id.title)
-        text = requireViewById(R.id.text)
-
-        expandButton = requireViewById(R.id.expand_button)
-        expandButton.setExpanded(false)
-    }
-
-    /** the resources configuration has changed such that the view needs to be reinflated */
-    fun isReinflateNeeded(): Boolean = configTracker.hasUnhandledConfigChange()
-
-    fun setIcon(icon: Icon?) {
-        this.icon.setImageIcon(icon)
-    }
-
-    fun setTitle(title: CharSequence?) {
-        this.title.text = title
-    }
-
-    fun setText(text: CharSequence?) {
-        this.text.text = text
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerButtonView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerButtonView.kt
deleted file mode 100644
index 8c95187..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerButtonView.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.row.ui.view
-
-import android.annotation.DrawableRes
-import android.content.Context
-import android.graphics.BlendMode
-import android.util.AttributeSet
-import com.android.internal.widget.EmphasizedNotificationButton
-
-class TimerButtonView
-@JvmOverloads
-constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0,
-    defStyleRes: Int = 0,
-) : EmphasizedNotificationButton(context, attrs, defStyleAttr, defStyleRes) {
-
-    private val Int.dp: Int
-        get() = (this * context.resources.displayMetrics.density).toInt()
-
-    fun setIcon(@DrawableRes icon: Int) {
-        val drawable = context.getDrawable(icon)
-
-        drawable?.mutate()
-        drawable?.setTintList(textColors)
-        drawable?.setTintBlendMode(BlendMode.SRC_IN)
-        drawable?.setBounds(0, 0, 24.dp, 24.dp)
-
-        setCompoundDrawablesRelative(drawable, null, null, null)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerView.kt
deleted file mode 100644
index d481b50..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerView.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.row.ui.view
-
-import android.content.Context
-import android.graphics.drawable.Icon
-import android.os.SystemClock
-import android.util.AttributeSet
-import android.widget.Chronometer
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.view.isVisible
-import com.android.systemui.res.R
-
-class TimerView
-@JvmOverloads
-constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0,
-    defStyleRes: Int = 0,
-) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) {
-
-    private val configTracker = ConfigurationTracker(resources)
-
-    private lateinit var icon: ImageView
-    private lateinit var label: TextView
-    private lateinit var chronometer: Chronometer
-    private lateinit var pausedTimeRemaining: TextView
-    lateinit var mainButton: TimerButtonView
-        private set
-
-    lateinit var altButton: TimerButtonView
-        private set
-
-    lateinit var resetButton: TimerButtonView
-        private set
-
-    override fun onFinishInflate() {
-        super.onFinishInflate()
-        icon = requireViewById(R.id.icon)
-        label = requireViewById(R.id.label)
-        chronometer = requireViewById(R.id.chronoRemaining)
-        pausedTimeRemaining = requireViewById(R.id.pausedTimeRemaining)
-        mainButton = requireViewById(R.id.mainButton)
-        altButton = requireViewById(R.id.altButton)
-        resetButton = requireViewById(R.id.resetButton)
-    }
-
-    /** the resources configuration has changed such that the view needs to be reinflated */
-    fun isReinflateNeeded(): Boolean = configTracker.hasUnhandledConfigChange()
-
-    fun setIcon(icon: Icon?) {
-        this.icon.setImageIcon(icon)
-    }
-
-    fun setLabel(label: String) {
-        this.label.text = label
-    }
-
-    fun setPausedTime(pausedTime: String?) {
-        if (pausedTime != null) {
-            pausedTimeRemaining.text = pausedTime
-            pausedTimeRemaining.isVisible = true
-        } else {
-            pausedTimeRemaining.isVisible = false
-        }
-    }
-
-    fun setCountdownTime(countdownTimeMs: Long?) {
-        if (countdownTimeMs != null) {
-            chronometer.base =
-                countdownTimeMs - System.currentTimeMillis() + SystemClock.elapsedRealtime()
-            chronometer.isVisible = true
-            chronometer.start()
-        } else {
-            chronometer.isVisible = false
-            chronometer.stop()
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt
deleted file mode 100644
index 3b8957c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.row.ui.viewbinder
-
-import androidx.lifecycle.lifecycleScope
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
-import com.android.systemui.statusbar.notification.row.ui.viewmodel.EnRouteViewModel
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-
-/** Binds a [EnRouteView] to its [view model][EnRouteViewModel]. */
-object EnRouteViewBinder {
-    fun bindWhileAttached(
-        view: EnRouteView,
-        viewModel: EnRouteViewModel,
-    ): DisposableHandle {
-        return view.repeatWhenAttached { lifecycleScope.launch { bind(view, viewModel) } }
-    }
-
-    suspend fun bind(
-        view: EnRouteView,
-        viewModel: EnRouteViewModel,
-    ) = coroutineScope {
-        launch { viewModel.icon.collect { view.setIcon(it) } }
-        launch { viewModel.title.collect { view.setTitle(it) } }
-        launch { viewModel.text.collect { view.setText(it) } }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/TimerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/TimerViewBinder.kt
deleted file mode 100644
index 042d1bc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/TimerViewBinder.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.row.ui.viewbinder
-
-import android.content.res.ColorStateList
-import android.graphics.drawable.Icon
-import android.view.View
-import androidx.core.view.isGone
-import androidx.lifecycle.lifecycleScope
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
-import com.android.systemui.statusbar.notification.row.ui.view.TimerView
-import com.android.systemui.statusbar.notification.row.ui.viewmodel.TimerViewModel
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-
-/** Binds a [TimerView] to its [view model][TimerViewModel]. */
-object TimerViewBinder {
-    fun bindWhileAttached(
-        view: TimerView,
-        viewModel: TimerViewModel,
-    ): DisposableHandle {
-        return view.repeatWhenAttached { lifecycleScope.launch { bind(view, viewModel) } }
-    }
-
-    suspend fun bind(
-        view: TimerView,
-        viewModel: TimerViewModel,
-    ) = coroutineScope {
-        launch { viewModel.icon.collect { view.setIcon(it) } }
-        launch { viewModel.label.collect { view.setLabel(it) } }
-        launch { viewModel.pausedTime.collect { view.setPausedTime(it) } }
-        launch { viewModel.countdownTime.collect { view.setCountdownTime(it) } }
-        launch { viewModel.mainButtonModel.collect { bind(view.mainButton, it) } }
-        launch { viewModel.altButtonModel.collect { bind(view.altButton, it) } }
-        launch { viewModel.resetButtonModel.collect { bind(view.resetButton, it) } }
-    }
-
-    fun bind(buttonView: TimerButtonView, model: TimerViewModel.ButtonViewModel?) {
-        if (model != null) {
-            buttonView.setButtonBackground(
-                ColorStateList.valueOf(
-                    buttonView.context.getColor(com.android.internal.R.color.system_accent2_100)
-                )
-            )
-            buttonView.setTextColor(
-                buttonView.context.getColor(
-                    com.android.internal.R.color.notification_primary_text_color_light
-                )
-            )
-
-            when (model) {
-                is TimerViewModel.ButtonViewModel.WithSystemAttrs -> {
-                    buttonView.setIcon(model.iconRes)
-                    buttonView.setText(model.labelRes)
-                }
-                is TimerViewModel.ButtonViewModel.WithCustomAttrs -> {
-                    // TODO: b/352142761 - is there a better way to deal with TYPE_RESOURCE icons
-                    // with empty resPackage? RemoteViews handles this by using a  different
-                    // `contextForResources` for inflation.
-                    val icon =
-                        if (model.icon.type == Icon.TYPE_RESOURCE && model.icon.resPackage == "")
-                            Icon.createWithResource(
-                                "com.google.android.deskclock",
-                                model.icon.resId
-                            )
-                        else model.icon
-                    buttonView.setImageIcon(icon)
-                    buttonView.text = model.label
-                }
-            }
-
-            buttonView.setOnClickListener(
-                model.pendingIntent?.let { pendingIntent ->
-                    View.OnClickListener { pendingIntent.send() }
-                }
-            )
-            buttonView.isEnabled = model.pendingIntent != null
-        }
-        buttonView.isGone = model == null
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt
deleted file mode 100644
index 307a983..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.row.ui.viewmodel
-
-import android.graphics.drawable.Icon
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.row.domain.interactor.NotificationRowInteractor
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.util.kotlin.FlowDumperImpl
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
-
-/** A view model for EnRoute notifications. */
-class EnRouteViewModel
-@Inject
-constructor(
-    dumpManager: DumpManager,
-    rowInteractor: NotificationRowInteractor,
-) : FlowDumperImpl(dumpManager) {
-    init {
-        /* check if */ RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()
-    }
-
-    val icon: Flow<Icon?> = rowInteractor.enRouteContentModel.mapNotNull { it.smallIcon.icon }
-
-    val title: Flow<CharSequence?> = rowInteractor.enRouteContentModel.map { it.title }
-
-    val text: Flow<CharSequence?> = rowInteractor.enRouteContentModel.map { it.text }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
deleted file mode 100644
index 5552d89..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.row.ui.viewmodel
-
-// noinspection CleanArchitectureDependencyViolation
-import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
-import dagger.BindsInstance
-import dagger.Subcomponent
-
-@Subcomponent
-interface RichOngoingViewModelComponent {
-
-    @Subcomponent.Factory
-    interface Factory {
-        /** Creates an instance of [RichOngoingViewModelComponent]. */
-        fun create(
-            @BindsInstance repository: NotificationRowRepository
-        ): RichOngoingViewModelComponent
-    }
-
-    fun createTimerViewModel(): TimerViewModel
-
-    fun createEnRouteViewModel(): EnRouteViewModel
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModel.kt
deleted file mode 100644
index 768a093..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModel.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.row.ui.viewmodel
-
-import android.annotation.DrawableRes
-import android.annotation.StringRes
-import android.app.PendingIntent
-import android.graphics.drawable.Icon
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.row.domain.interactor.NotificationRowInteractor
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.statusbar.notification.row.shared.TimerContentModel.TimerState
-import com.android.systemui.util.kotlin.FlowDumperImpl
-import java.time.Duration
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
-
-/** A view model for Timer notifications. */
-class TimerViewModel
-@Inject
-constructor(
-    dumpManager: DumpManager,
-    rowInteractor: NotificationRowInteractor,
-) : FlowDumperImpl(dumpManager) {
-    init {
-        /* check if */ RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()
-    }
-
-    private val state: Flow<TimerState> = rowInteractor.timerContentModel.mapNotNull { it.state }
-
-    val icon: Flow<Icon?> = rowInteractor.timerContentModel.mapNotNull { it.icon.icon }
-
-    val label: Flow<String> = rowInteractor.timerContentModel.mapNotNull { it.name }
-
-    val countdownTime: Flow<Long?> = state.map { (it as? TimerState.Running)?.finishTime }
-
-    val pausedTime: Flow<String?> =
-        state.map { (it as? TimerState.Paused)?.timeRemaining?.format() }
-
-    val mainButtonModel: Flow<ButtonViewModel> =
-        state.map {
-            when (it) {
-                is TimerState.Paused ->
-                    ButtonViewModel.WithSystemAttrs(
-                        it.resumeIntent,
-                        com.android.systemui.res.R.string.controls_media_resume, // "Resume",
-                        com.android.systemui.res.R.drawable.ic_media_play
-                    )
-                is TimerState.Running ->
-                    ButtonViewModel.WithSystemAttrs(
-                        it.pauseIntent,
-                        com.android.systemui.res.R.string.controls_media_button_pause, // "Pause",
-                        com.android.systemui.res.R.drawable.ic_media_pause
-                    )
-            }
-        }
-
-    val altButtonModel: Flow<ButtonViewModel?> =
-        state.map {
-            it.addMinuteAction?.let { action ->
-                ButtonViewModel.WithCustomAttrs(
-                    action.actionIntent,
-                    action.title, // "1:00",
-                    action.getIcon()
-                )
-            }
-        }
-
-    val resetButtonModel: Flow<ButtonViewModel?> =
-        state.map {
-            it.resetAction?.let { action ->
-                ButtonViewModel.WithCustomAttrs(
-                    action.actionIntent,
-                    action.title, // "Reset",
-                    action.getIcon()
-                )
-            }
-        }
-
-    sealed interface ButtonViewModel {
-        val pendingIntent: PendingIntent?
-
-        data class WithSystemAttrs(
-            override val pendingIntent: PendingIntent?,
-            @StringRes val labelRes: Int,
-            @DrawableRes val iconRes: Int,
-        ) : ButtonViewModel
-
-        data class WithCustomAttrs(
-            override val pendingIntent: PendingIntent?,
-            val label: CharSequence,
-            val icon: Icon,
-        ) : ButtonViewModel
-    }
-}
-
-private fun Duration.format(): String {
-    val hours = this.toHours()
-    return if (hours > 0) {
-        String.format("%d:%02d:%02d", hours, toMinutesPart(), toSecondsPart())
-    } else {
-        String.format("%d:%02d", toMinutes(), toSecondsPart())
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 9cd5215..8206c21 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -85,8 +85,9 @@
         `when`(telephonyManager.createForSubscriptionId(anyInt())).thenReturn(telephonyManager)
         `when`(telephonyManager.supplyIccLockPin(anyString())).thenReturn(mock())
         simPinView =
-            LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
-                as KeyguardSimPinView
+            LayoutInflater.from(context)
+                .inflate(R.layout.keyguard_sim_pin_view, null)
+                .requireViewById(R.id.keyguard_sim_pin_view) as KeyguardSimPinView
         val fakeFeatureFlags = FakeFeatureFlags()
         val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index f5a90196..0e9ef06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -556,11 +556,13 @@
             return null;
         }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));
 
-        // Dreaming->Lockscreen
+        // Any edge transition
         when(mKeyguardTransitionInteractor.transition(any()))
                 .thenReturn(emptyFlow());
         when(mKeyguardTransitionInteractor.transition(any(), any()))
                 .thenReturn(emptyFlow());
+
+        // Dreaming->Lockscreen
         when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha())
                 .thenReturn(emptyFlow());
         when(mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index a099c9d..48608eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -35,9 +35,6 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView
-import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
@@ -51,33 +48,23 @@
 import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews
 import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
-import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder
 import com.android.systemui.statusbar.policy.SmartReplyStateInflater
-import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
-import kotlinx.coroutines.DisposableHandle
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
-import org.mockito.kotlin.argThat
-import org.mockito.kotlin.clearInvocations
-import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
-import org.mockito.kotlin.inOrder
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -118,45 +105,6 @@
             }
         }
 
-    private var fakeRonContentModel: RichOngoingContentModel? = null
-    private val fakeRonExtractor =
-        object : RichOngoingNotificationContentExtractor {
-            override fun extractContentModel(
-                entry: NotificationEntry,
-                builder: Notification.Builder,
-                systemUIContext: Context,
-                packageContext: Context
-            ): RichOngoingContentModel? = fakeRonContentModel
-        }
-
-    private var fakeContractedRonViewHolder: ContentViewInflationResult = NullContentView
-    private var fakeExpandedRonViewHolder: ContentViewInflationResult = NullContentView
-    private var fakeHeadsUpRonViewHolder: ContentViewInflationResult = NullContentView
-    private var fakeRonViewInflater =
-        spy(
-            object : RichOngoingNotificationViewInflater {
-                override fun inflateView(
-                    contentModel: RichOngoingContentModel,
-                    existingView: View?,
-                    entry: NotificationEntry,
-                    systemUiContext: Context,
-                    parentView: ViewGroup,
-                    viewType: RichOngoingNotificationViewType
-                ): ContentViewInflationResult =
-                    when (viewType) {
-                        RichOngoingNotificationViewType.Contracted -> fakeContractedRonViewHolder
-                        RichOngoingNotificationViewType.Expanded -> fakeExpandedRonViewHolder
-                        RichOngoingNotificationViewType.HeadsUp -> fakeHeadsUpRonViewHolder
-                    }
-
-                override fun canKeepView(
-                    contentModel: RichOngoingContentModel,
-                    existingView: View?,
-                    viewType: RichOngoingNotificationViewType
-                ): Boolean = false
-            }
-        )
-
     @Before
     fun setUp() {
         allowTestableLooperAsMainThread()
@@ -167,15 +115,12 @@
                 .setContentText("Text")
                 .setStyle(Notification.BigTextStyle().bigText("big text"))
         testHelper = NotificationTestHelper(mContext, mDependency)
-        testHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL)
         row = spy(testHelper.createRow(builder.build()))
         notificationInflater =
             NotificationRowContentBinderImpl(
                 cache,
                 mock(),
                 mock<ConversationNotificationProcessor>(),
-                fakeRonExtractor,
-                fakeRonViewInflater,
                 mock(),
                 smartReplyStateInflater,
                 layoutInflaterFactoryProvider,
@@ -405,496 +350,6 @@
     }
 
     @Test
-    fun testRonModelRequiredForRonView() {
-        fakeRonContentModel = null
-        val contractedRonView = View(context)
-        val expandedRonView = View(context)
-        val headsUpRonView = View(context)
-        fakeContractedRonViewHolder =
-            InflatedContentViewHolder(view = contractedRonView, binder = mock())
-        fakeExpandedRonViewHolder =
-            InflatedContentViewHolder(view = expandedRonView, binder = mock())
-        fakeHeadsUpRonViewHolder = InflatedContentViewHolder(view = headsUpRonView, binder = mock())
-
-        // WHEN inflater inflates
-        val contentToInflate =
-            FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
-        inflateAndWait(notificationInflater, contentToInflate, row)
-        verifyNoMoreInteractions(fakeRonViewInflater)
-    }
-
-    @Test
-    fun testRonModelCleansUpRemoteViews() {
-        val ronView = View(context)
-
-        val entry = row.entry
-
-        fakeRonContentModel = mock<TimerContentModel>()
-        fakeContractedRonViewHolder =
-            InflatedContentViewHolder(view = ronView, binder = mock<DeferredContentViewBinder>())
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
-
-        // VERIFY
-        verify(cache).removeCachedView(eq(entry), eq(FLAG_CONTENT_VIEW_CONTRACTED))
-        verify(cache).removeCachedView(eq(entry), eq(FLAG_CONTENT_VIEW_EXPANDED))
-        verify(cache).removeCachedView(eq(entry), eq(FLAG_CONTENT_VIEW_HEADS_UP))
-    }
-
-    @Test
-    fun testRonModelCleansUpSmartReplies() {
-        val ronView = View(context)
-
-        val privateLayout = spy(row.privateLayout)
-
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mock<TimerContentModel>()
-        fakeContractedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mock())
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
-
-        // VERIFY
-        verify(privateLayout).setExpandedInflatedSmartReplies(eq(null))
-        verify(privateLayout).setHeadsUpInflatedSmartReplies(eq(null))
-    }
-
-    @Test
-    fun testRonModelTriggersInflationOfContractedRonView() {
-        val mockRonModel = mock<TimerContentModel>()
-        val ronView = View(context)
-        val mockBinder = mock<DeferredContentViewBinder>()
-
-        val entry = row.entry
-        val privateLayout = row.privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeContractedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
-
-        // VERIFY that the inflater is invoked
-        verify(fakeRonViewInflater)
-            .inflateView(
-                eq(mockRonModel),
-                any(),
-                eq(entry),
-                any(),
-                eq(privateLayout),
-                eq(RichOngoingNotificationViewType.Contracted)
-            )
-        assertThat(row.privateLayout.contractedChild).isSameInstanceAs(ronView)
-        verify(mockBinder).setupContentViewBinder()
-    }
-
-    @Test
-    fun testRonModelTriggersInflationOfExpandedRonView() {
-        val mockRonModel = mock<TimerContentModel>()
-        val ronView = View(context)
-        val mockBinder = mock<DeferredContentViewBinder>()
-
-        val entry = row.entry
-        val privateLayout = row.privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeExpandedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
-
-        // VERIFY that the inflater is invoked
-        verify(fakeRonViewInflater)
-            .inflateView(
-                eq(mockRonModel),
-                any(),
-                eq(entry),
-                any(),
-                eq(privateLayout),
-                eq(RichOngoingNotificationViewType.Expanded)
-            )
-        assertThat(row.privateLayout.expandedChild).isSameInstanceAs(ronView)
-        verify(mockBinder).setupContentViewBinder()
-    }
-
-    @Test
-    fun testRonModelTriggersInflationOfHeadsUpRonView() {
-        val mockRonModel = mock<TimerContentModel>()
-        val ronView = View(context)
-        val mockBinder = mock<DeferredContentViewBinder>()
-
-        val entry = row.entry
-        val privateLayout = row.privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeHeadsUpRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
-
-        // VERIFY that the inflater is invoked
-        verify(fakeRonViewInflater)
-            .inflateView(
-                eq(mockRonModel),
-                any(),
-                eq(entry),
-                any(),
-                eq(privateLayout),
-                eq(RichOngoingNotificationViewType.HeadsUp)
-            )
-        assertThat(row.privateLayout.headsUpChild).isSameInstanceAs(ronView)
-        verify(mockBinder).setupContentViewBinder()
-    }
-
-    @Test
-    fun keepExistingViewForContractedRonNotChangingContractedChild() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-
-        row.privateLayout.mContractedBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeContractedRonViewHolder = KeepExistingView
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
-
-        // THEN  do not dispose old contracted binder handle and change contracted child
-        verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-        verifyNoMoreInteractions(oldHandle)
-        verify(privateLayout, never()).setContractedChild(any())
-    }
-
-    @Test
-    fun keepExistingViewForExpandedRonNotChangingExpandedChild() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-
-        row.privateLayout.mExpandedBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeExpandedRonViewHolder = KeepExistingView
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
-
-        // THEN  do not dispose old expanded binder handle and change expanded child
-        verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-        verifyNoMoreInteractions(oldHandle)
-        verify(privateLayout, never()).setExpandedChild(any())
-    }
-
-    @Test
-    fun keepExistingViewForHeadsUpRonNotChangingHeadsUpChild() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-
-        row.privateLayout.mHeadsUpBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeHeadsUpRonViewHolder = KeepExistingView
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
-
-        // THEN - do not dispose old heads up binder handle and change heads up child
-        verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-        verifyNoMoreInteractions(oldHandle)
-        verify(privateLayout, never()).setHeadsUpChild(any())
-    }
-
-    @Test
-    fun nullContentViewForContractedRonAppliesElementsInOrder() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-
-        row.privateLayout.mContractedBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeContractedRonViewHolder = NullContentView
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
-
-        // Validate that these 4 steps happen in this precise order
-        inOrder(oldHandle, entry, privateLayout, cache) {
-            verify(oldHandle).dispose()
-            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-            verify(privateLayout).setContractedChild(eq(null))
-        }
-    }
-
-    @Test
-    fun nullContentViewForExpandedRonAppliesElementsInOrder() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-
-        row.privateLayout.mExpandedBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeExpandedRonViewHolder = NullContentView
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
-
-        // Validate that these 4 steps happen in this precise order
-        inOrder(oldHandle, entry, privateLayout, cache) {
-            verify(oldHandle).dispose()
-            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-            verify(privateLayout).setExpandedChild(eq(null))
-        }
-    }
-
-    @Test
-    fun nullContentViewForHeadsUpRonAppliesElementsInOrder() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-
-        row.privateLayout.mHeadsUpBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeHeadsUpRonViewHolder = NullContentView
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
-
-        // Validate that these 4 steps happen in this precise order
-        inOrder(oldHandle, entry, privateLayout, cache) {
-            verify(oldHandle).dispose()
-            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-            verify(privateLayout).setHeadsUpChild(eq(null))
-        }
-    }
-
-    @Test
-    fun contractedRonViewAppliesElementsInOrder() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-        val ronView = View(context)
-        val mockBinder = mock<DeferredContentViewBinder>()
-
-        row.privateLayout.mContractedBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeContractedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
-
-        // Validate that these 4 steps happen in this precise order
-        inOrder(oldHandle, entry, privateLayout, mockBinder) {
-            verify(oldHandle).dispose()
-            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-            verify(privateLayout).setContractedChild(eq(ronView))
-            verify(mockBinder).setupContentViewBinder()
-        }
-    }
-
-    @Test
-    fun expandedRonViewAppliesElementsInOrder() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-        val ronView = View(context)
-        val mockBinder = mock<DeferredContentViewBinder>()
-
-        row.privateLayout.mExpandedBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeExpandedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
-
-        // Validate that these 4 steps happen in this precise order
-        inOrder(oldHandle, entry, privateLayout, mockBinder) {
-            verify(oldHandle).dispose()
-            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-            verify(privateLayout).setExpandedChild(eq(ronView))
-            verify(mockBinder).setupContentViewBinder()
-        }
-    }
-
-    @Test
-    fun headsUpRonViewAppliesElementsInOrder() {
-        val oldHandle = mock<DisposableHandle>()
-        val mockRonModel = mock<TimerContentModel>()
-        val ronView = View(context)
-        val mockBinder = mock<DeferredContentViewBinder>()
-
-        row.privateLayout.mHeadsUpBinderHandle = oldHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        fakeRonContentModel = mockRonModel
-        fakeHeadsUpRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
-
-        // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
-
-        // Validate that these 4 steps happen in this precise order
-        inOrder(oldHandle, entry, privateLayout, mockBinder) {
-            verify(oldHandle).dispose()
-            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
-            verify(privateLayout).setHeadsUpChild(eq(ronView))
-            verify(mockBinder).setupContentViewBinder()
-        }
-    }
-
-    @Test
-    fun testRonNotReinflating() {
-        val oldContractedBinderHandle = mock<DisposableHandle>()
-        val oldExpandedBinderHandle = mock<DisposableHandle>()
-        val oldHeadsUpBinderHandle = mock<DisposableHandle>()
-
-        val contractedBinderHandle = mock<DisposableHandle>()
-        val expandedBinderHandle = mock<DisposableHandle>()
-        val headsUpBinderHandle = mock<DisposableHandle>()
-
-        val contractedRonView = View(context)
-        val expandedRonView = View(context)
-        val headsUpRonView = View(context)
-
-        val mockRonModel1 = mock<TimerContentModel>()
-        val mockRonModel2 = mock<TimerContentModel>()
-
-        val mockContractedViewBinder = mock<DeferredContentViewBinder>()
-        val mockExpandedViewBinder = mock<DeferredContentViewBinder>()
-        val mockHeadsUpViewBinder = mock<DeferredContentViewBinder>()
-
-        doReturn(contractedBinderHandle).whenever(mockContractedViewBinder).setupContentViewBinder()
-        doReturn(expandedBinderHandle).whenever(mockExpandedViewBinder).setupContentViewBinder()
-        doReturn(headsUpBinderHandle).whenever(mockHeadsUpViewBinder).setupContentViewBinder()
-
-        row.privateLayout.mContractedBinderHandle = oldContractedBinderHandle
-        row.privateLayout.mExpandedBinderHandle = oldExpandedBinderHandle
-        row.privateLayout.mHeadsUpBinderHandle = oldHeadsUpBinderHandle
-        val entry = spy(row.entry)
-        row.entry = entry
-        val privateLayout = spy(row.privateLayout)
-        row.privateLayout = privateLayout
-
-        // WHEN inflater inflates both a model and a view
-        fakeRonContentModel = mockRonModel1
-        fakeContractedRonViewHolder =
-            InflatedContentViewHolder(view = contractedRonView, binder = mockContractedViewBinder)
-        fakeExpandedRonViewHolder =
-            InflatedContentViewHolder(view = expandedRonView, binder = mockExpandedViewBinder)
-        fakeHeadsUpRonViewHolder =
-            InflatedContentViewHolder(view = headsUpRonView, binder = mockHeadsUpViewBinder)
-
-        val contentToInflate =
-            FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
-        inflateAndWait(notificationInflater, contentToInflate, row)
-
-        // Validate that these 4 steps happen in this precise order
-        inOrder(
-            oldContractedBinderHandle,
-            oldExpandedBinderHandle,
-            oldHeadsUpBinderHandle,
-            entry,
-            privateLayout,
-            mockContractedViewBinder,
-            mockExpandedViewBinder,
-            mockHeadsUpViewBinder,
-            contractedBinderHandle,
-            expandedBinderHandle,
-            headsUpBinderHandle
-        ) {
-            verify(oldContractedBinderHandle).dispose()
-            verify(oldExpandedBinderHandle).dispose()
-            verify(oldHeadsUpBinderHandle).dispose()
-
-            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel1 })
-
-            verify(privateLayout).setContractedChild(eq(contractedRonView))
-            verify(mockContractedViewBinder).setupContentViewBinder()
-
-            verify(privateLayout).setExpandedChild(eq(expandedRonView))
-            verify(mockExpandedViewBinder).setupContentViewBinder()
-
-            verify(privateLayout).setHeadsUpChild(eq(headsUpRonView))
-            verify(mockHeadsUpViewBinder).setupContentViewBinder()
-
-            verify(contractedBinderHandle, never()).dispose()
-            verify(expandedBinderHandle, never()).dispose()
-            verify(headsUpBinderHandle, never()).dispose()
-        }
-
-        clearInvocations(
-            oldContractedBinderHandle,
-            oldExpandedBinderHandle,
-            oldHeadsUpBinderHandle,
-            entry,
-            privateLayout,
-            mockContractedViewBinder,
-            mockExpandedViewBinder,
-            mockHeadsUpViewBinder,
-            contractedBinderHandle,
-            expandedBinderHandle,
-            headsUpBinderHandle
-        )
-
-        // THEN when the inflater inflates just a model
-        fakeRonContentModel = mockRonModel2
-        fakeContractedRonViewHolder = KeepExistingView
-        fakeExpandedRonViewHolder = KeepExistingView
-        fakeHeadsUpRonViewHolder = KeepExistingView
-
-        inflateAndWait(notificationInflater, contentToInflate, row)
-
-        // Validate that for reinflation, the only thing we do us update the model
-        verify(contractedBinderHandle, never()).dispose()
-        verify(expandedBinderHandle, never()).dispose()
-        verify(headsUpBinderHandle, never()).dispose()
-        verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel2 })
-        verify(privateLayout, never()).setContractedChild(any())
-        verify(privateLayout, never()).setExpandedChild(any())
-        verify(privateLayout, never()).setHeadsUpChild(any())
-        verify(mockContractedViewBinder, never()).setupContentViewBinder()
-        verify(mockExpandedViewBinder, never()).setupContentViewBinder()
-        verify(mockHeadsUpViewBinder, never()).setupContentViewBinder()
-        verify(contractedBinderHandle, never()).dispose()
-        verify(expandedBinderHandle, never()).dispose()
-        verify(headsUpBinderHandle, never()).dispose()
-    }
-
-    @Test
     fun testNotificationViewHeightTooSmallFailsValidation() {
         val validationError =
             getValidationError(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 75376e6..2340d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -200,8 +200,6 @@
                                 mock(NotifRemoteViewCache.class),
                                 mock(NotificationRemoteInputManager.class),
                                 mock(ConversationNotificationProcessor.class),
-                                mock(RichOngoingNotificationContentExtractor.class),
-                                mock(RichOngoingNotificationViewInflater.class),
                                 mock(Executor.class),
                                 new MockSmartReplyInflater(),
                                 mock(NotifLayoutInflaterFactory.Provider.class),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 2eb1573..fc4f05d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -222,8 +222,6 @@
                     Mockito.mock(NotifRemoteViewCache::class.java, STUB_ONLY),
                     remoteInputManager,
                     conversationProcessor,
-                    Mockito.mock(RichOngoingNotificationContentExtractor::class.java, STUB_ONLY),
-                    Mockito.mock(RichOngoingNotificationViewInflater::class.java, STUB_ONLY),
                     Mockito.mock(Executor::class.java, STUB_ONLY),
                     smartReplyStateInflater,
                     notifLayoutInflaterFactoryProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepositoryKosmos.kt
deleted file mode 100644
index 84ef4b5..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepositoryKosmos.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.row.data.repository
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
-import kotlinx.coroutines.flow.MutableStateFlow
-
-val Kosmos.fakeNotificationRowRepository by Fixture { FakeNotificationRowRepository() }
-
-class FakeNotificationRowRepository : NotificationRowRepository {
-    override val richOngoingContentModel = MutableStateFlow<RichOngoingContentModel?>(null)
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractorKosmos.kt
deleted file mode 100644
index 3a7d7ba..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractorKosmos.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.row.domain.interactor
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
-
-fun Kosmos.getNotificationRowInteractor(repository: NotificationRowRepository) =
-    NotificationRowInteractor(repository = repository)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt
deleted file mode 100644
index 7e51135..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.row.ui.viewmodel
-
-import com.android.systemui.dump.dumpManager
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
-import com.android.systemui.statusbar.notification.row.domain.interactor.getNotificationRowInteractor
-
-fun Kosmos.getEnRouteViewModel(repository: NotificationRowRepository) =
-    EnRouteViewModel(
-        dumpManager = dumpManager,
-        rowInteractor = getNotificationRowInteractor(repository),
-    )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelKosmos.kt
deleted file mode 100644
index 00f45b2..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelKosmos.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.row.ui.viewmodel
-
-import com.android.systemui.dump.dumpManager
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
-import com.android.systemui.statusbar.notification.row.domain.interactor.getNotificationRowInteractor
-
-fun Kosmos.getTimerViewModel(repository: NotificationRowRepository) =
-    TimerViewModel(
-        dumpManager = dumpManager,
-        rowInteractor = getNotificationRowInteractor(repository),
-    )
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 86246e2..72f62c5 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -5,7 +5,8 @@
     { "name": "hoststubgen-test-tiny-test" },
     { "name": "hoststubgen-invoke-test" },
     { "name": "RavenwoodMockitoTest_device" },
-    { "name": "RavenwoodBivalentTest_device" },
+    // TODO(b/371215487): Re-enable when the test is fixed.
+    // { "name": "RavenwoodBivalentTest_device" },
 
     { "name": "RavenwoodBivalentInstTest_nonself_inst" },
     { "name": "RavenwoodBivalentInstTest_self_inst_device" },
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 6fd281e..f5a297b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -685,11 +685,6 @@
     // default. Controlled by Settings.Global.FORCE_ENABLE_PSS_PROFILING
     volatile boolean mForceEnablePssProfiling = false;
 
-    // Indicates whether to use ApplicationInfo to determine launched state instead of PM user state
-    // This is a temporary workaround until the trunk-stable flag is pushed to nextfood.
-    // TODO: b/365979852 - remove this workaround when redundant
-    volatile boolean mFlagUseAppInfoNotLaunched = false;
-
     /**
      * Indicates whether the foreground service background start restriction is enabled for
      * caller app that is targeting S+.
@@ -1022,9 +1017,6 @@
     private static final Uri FORCE_ENABLE_PSS_PROFILING_URI =
             Settings.Global.getUriFor(Settings.Global.FORCE_ENABLE_PSS_PROFILING);
 
-    private static final Uri ENABLE_USE_APP_INFO_NOT_LAUNCHED_URI =
-            Settings.Global.getUriFor(Settings.Global.ENABLE_USE_APP_INFO_NOT_LAUNCHED);
-
     /**
      * The threshold to decide if a given association should be dumped into metrics.
      */
@@ -1487,7 +1479,6 @@
                     false, this);
         }
         mResolver.registerContentObserver(FORCE_ENABLE_PSS_PROFILING_URI, false, this);
-        mResolver.registerContentObserver(ENABLE_USE_APP_INFO_NOT_LAUNCHED_URI, false, this);
         updateConstants();
         if (mSystemServerAutomaticHeapDumpEnabled) {
             updateEnableAutomaticSystemServerHeapDumps();
@@ -1504,7 +1495,6 @@
         updateActivityStartsLoggingEnabled();
         updateForegroundServiceStartsLoggingEnabled();
         updateForceEnablePssProfiling();
-        updateEnableUseAppInfoNotLaunched();
         // Read DropboxRateLimiter params from flags.
         mService.initDropboxRateLimiter();
     }
@@ -1550,8 +1540,6 @@
             updateEnableAutomaticSystemServerHeapDumps();
         } else if (FORCE_ENABLE_PSS_PROFILING_URI.equals(uri)) {
             updateForceEnablePssProfiling();
-        } else if (ENABLE_USE_APP_INFO_NOT_LAUNCHED_URI.equals(uri)) {
-            updateEnableUseAppInfoNotLaunched();
         }
     }
 
@@ -1671,11 +1659,6 @@
                 Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1;
     }
 
-    private void updateEnableUseAppInfoNotLaunched() {
-        mFlagUseAppInfoNotLaunched = Settings.Global.getInt(mResolver,
-                Settings.Global.ENABLE_USE_APP_INFO_NOT_LAUNCHED, 0) == 1;
-    }
-
     private void updateBackgroundActivityStarts() {
         mFlagBackgroundActivityStartsEnabled = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2555,8 +2538,6 @@
         pw.print("  OOMADJ_UPDATE_QUICK="); pw.println(OOMADJ_UPDATE_QUICK);
         pw.print("  ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION=");
         pw.println(mEnableWaitForFinishAttachApplication);
-        pw.print("  FLAG_USE_APP_INFO_NOT_LAUNCHED=");
-        pw.println(mFlagUseAppInfoNotLaunched);
 
         pw.print("  "); pw.print(KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
         pw.print("="); pw.println(FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 83bc75e..9219cc12 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18398,25 +18398,34 @@
                     "Cannot kill the dependents of a package without its name.");
         }
 
+        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, true, ALLOW_FULL_ONLY, "killPackageDependents", null);
+        final int[] userIds = mUserController.expandUserId(userId);
+
         final long callingId = Binder.clearCallingIdentity();
         IPackageManager pm = AppGlobals.getPackageManager();
-        int pkgUid = -1;
         try {
-            pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
-        } catch (RemoteException e) {
-        }
-        if (userId != UserHandle.USER_ALL && pkgUid == -1) {
-            throw new IllegalArgumentException(
-                    "Cannot kill dependents of non-existing package " + packageName);
-        }
-        try {
-            synchronized(this) {
-                synchronized (mProcLock) {
-                    mProcessList.killPackageProcessesLSP(packageName, UserHandle.getAppId(pkgUid),
-                            userId, ProcessList.FOREGROUND_APP_ADJ,
-                            ApplicationExitInfo.REASON_DEPENDENCY_DIED,
-                            ApplicationExitInfo.SUBREASON_UNKNOWN,
-                            "dep: " + packageName);
+            for (int targetUserId : userIds) {
+                int pkgUid = -1;
+                try {
+                    pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
+                            targetUserId);
+                } catch (RemoteException e) {
+                }
+                if (userId != UserHandle.USER_ALL && pkgUid == -1) {
+                    throw new IllegalArgumentException(
+                            "Cannot kill dependents of non-existing package " + packageName);
+                }
+                synchronized (this) {
+                    synchronized (mProcLock) {
+                        mProcessList.killPackageProcessesLSP(packageName,
+                                UserHandle.getAppId(pkgUid),
+                                targetUserId,
+                                ProcessList.FOREGROUND_APP_ADJ,
+                                ApplicationExitInfo.REASON_DEPENDENCY_DIED,
+                                ApplicationExitInfo.SUBREASON_UNKNOWN,
+                                "dep: " + packageName);
+                    }
                 }
             }
         } finally {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a93ae72..57922d5 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3401,8 +3401,7 @@
         // Check if we should mark the processrecord for first launch after force-stopping
         if (wasStopped) {
             boolean wasEverLaunched = false;
-            if (android.app.Flags.useAppInfoNotLaunched()
-                    || mService.mConstants.mFlagUseAppInfoNotLaunched) {
+            if (android.app.Flags.useAppInfoNotLaunched()) {
                 wasEverLaunched = !info.isNotLaunched();
             } else {
                 try {
@@ -3423,8 +3422,7 @@
                         : STOPPED_STATE_FIRST_LAUNCH;
                 r.getWindowProcessController().setStoppedState(stoppedState);
             } else {
-                if (android.app.Flags.useAppInfoNotLaunched()
-                        || mService.mConstants.mFlagUseAppInfoNotLaunched) {
+                if (android.app.Flags.useAppInfoNotLaunched()) {
                     // If it was launched before, then it must be a force-stop
                     r.setWasForceStopped(wasEverLaunched);
                 } else {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e0cf96f..596e375 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -72,6 +72,9 @@
 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
 import static android.permission.flags.Flags.deviceAwareAppOpNewSchemaEnabled;
 
+import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_OPERATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_PROXY_OPERATION;
 import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
 
 import android.Manifest;
@@ -160,6 +163,7 @@
 import com.android.internal.pm.pkg.component.ParsedAttribution;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -2829,12 +2833,26 @@
 
     @Override
     public int checkOperation(int code, int uid, String packageName) {
+        if (Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED,
+                    uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+                    false);
+        }
         return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
                 Context.DEVICE_ID_DEFAULT, false /*raw*/);
     }
 
     @Override
     public int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId) {
+        if (Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED,
+                    uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+                    false);
+        }
         return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
                 virtualDeviceId, false /*raw*/);
     }
@@ -3015,6 +3033,14 @@
     public SyncNotedAppOp noteProxyOperationWithState(int code,
             AttributionSourceState attributionSourceState, boolean shouldCollectAsyncNotedOp,
             String message, boolean shouldCollectMessage, boolean skipProxyOperation) {
+        if (Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED,
+                    attributionSourceState.uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_PROXY_OPERATION,
+                    attributionSourceState.attributionTag != null);
+        }
+
         AttributionSource attributionSource = new AttributionSource(attributionSourceState);
         return mCheckOpsDelegateDispatcher.noteProxyOperation(code, attributionSource,
                 shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
@@ -3096,6 +3122,14 @@
     public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
             String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
             boolean shouldCollectMessage) {
+        if (Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED,
+                    uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_OPERATION,
+                    attributionTag != null);
+        }
+
         return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
                 attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
                 shouldCollectMessage);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 3780fbd..bbdac56 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -99,6 +99,7 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -126,6 +127,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -253,6 +255,8 @@
     private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
     private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
     private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
+    private static final String MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS =
+            "migrated_weaver_disabled_on_unsecured_users";
 
     // Duration that LockSettingsService will store the gatekeeper password for. This allows
     // multiple biometric enrollments without prompting the user to enter their password via
@@ -309,6 +313,10 @@
     @GuardedBy("mUserCreationAndRemovalLock")
     private boolean mThirdPartyAppsStarted;
 
+    // This list contains the (protectorId, userId) of any protectors that were by replaced by a
+    // migration and should be destroyed once rollback to the old build is no longer possible.
+    private ArrayList<Pair<Long, Integer>> mProtectorsToDestroyOnBootCompleted = new ArrayList<>();
+
     // Current password metrics for all secured users on the device. Updated when user unlocks the
     // device or changes password. Removed if user is stopped with its CE key evicted.
     @GuardedBy("this")
@@ -363,6 +371,10 @@
                 mLockSettingsService.migrateOldDataAfterSystemReady();
                 mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
             } else if (phase == PHASE_BOOT_COMPLETED) {
+                // In the case of an upgrade, PHASE_BOOT_COMPLETED means that a rollback to the old
+                // build can no longer occur.  This is the time to destroy any migrated protectors.
+                mLockSettingsService.destroyMigratedProtectors();
+
                 mLockSettingsService.loadEscrowData();
             }
         }
@@ -1076,6 +1088,11 @@
         mStorage.deleteRepairModePersistentData();
     }
 
+    private boolean isWeaverDisabledOnUnsecuredUsers() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers);
+    }
+
     // This is called when Weaver is guaranteed to be available (if the device supports Weaver).
     // It does any synthetic password related work that was delayed from earlier in the boot.
     private void onThirdPartyAppsStarted() {
@@ -1114,13 +1131,20 @@
             //
             // - Upgrading from Android 14, where unsecured users didn't have Keystore super keys.
             //
+            // - Upgrading from a build with config_disableWeaverOnUnsecuredUsers=false to one with
+            //   config_disableWeaverOnUnsecuredUsers=true.  (We don't bother to proactively add
+            //   Weaver for the reverse update to false, as it's too late to help in that case.)
+            //
             // The end result is that all users, regardless of whether they are secured or not, have
-            // a synthetic password with all keys initialized and protected by it.
+            // a synthetic password with all keys initialized and protected by it, and honoring
+            // config_disableWeaverOnUnsecuredUsers=true when applicable.
             //
             // Note: if this migration gets interrupted (e.g. by the device powering off), there
             // shouldn't be a problem since this will run again on the next boot, and
             // setCeStorageProtection() and initKeystoreSuperKeys(..., true) are idempotent.
-            if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
+            if (!getBoolean(MIGRATED_SP_FULL, false, 0)
+                    || (isWeaverDisabledOnUnsecuredUsers()
+                        && !getBoolean(MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS, false, 0))) {
                 for (UserInfo user : mUserManager.getAliveUsers()) {
                     removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
                     synchronized (mSpManager) {
@@ -1128,6 +1152,9 @@
                     }
                 }
                 setBoolean(MIGRATED_SP_FULL, true, 0);
+                if (isWeaverDisabledOnUnsecuredUsers()) {
+                    setBoolean(MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS, true, 0);
+                }
             }
 
             mThirdPartyAppsStarted = true;
@@ -1151,13 +1178,61 @@
                 getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
                 null);
         SyntheticPassword sp = result.syntheticPassword;
-        if (sp == null) {
+        if (isWeaverDisabledOnUnsecuredUsers()) {
+            Slog.i(TAG, "config_disableWeaverOnUnsecuredUsers=true");
+
+            // If config_disableWeaverOnUnsecuredUsers=true, then the Weaver HAL may be buggy and
+            // need multiple retries before it works here to unwrap the SP, if the SP was already
+            // protected by Weaver.  Note that the problematic HAL can also deadlock if called with
+            // the ActivityManagerService lock held, but that should not be a problem here since
+            // that lock isn't held here, unlike unlockUserKeyIfUnsecured() where it is.
+            for (int i = 0; i < 12 && sp == null; i++) {
+                Slog.e(TAG, "Failed to unwrap synthetic password. Waiting 5 seconds to retry.");
+                SystemClock.sleep(5000);
+                result = mSpManager.unlockLskfBasedProtector(getGateKeeperService(), protectorId,
+                            LockscreenCredential.createNone(), userId, null);
+                sp = result.syntheticPassword;
+            }
+            if (sp == null) {
+                throw new IllegalStateException(
+                        "Failed to unwrap synthetic password for unsecured user");
+            }
+            // If the SP is protected by Weaver, then remove the Weaver protection in order to make
+            // config_disableWeaverOnUnsecuredUsers=true take effect.
+            if (result.usedWeaver) {
+                Slog.i(TAG, "Removing Weaver protection from the synthetic password");
+                // Create a new protector, which will not use Weaver.
+                long newProtectorId = mSpManager.createLskfBasedProtector(
+                        getGateKeeperService(), LockscreenCredential.createNone(), sp, userId);
+
+                // Out of paranoia, make sure the new protector really works.
+                result = mSpManager.unlockLskfBasedProtector(getGateKeeperService(),
+                            newProtectorId, LockscreenCredential.createNone(), userId, null);
+                sp = result.syntheticPassword;
+                if (sp == null) {
+                    throw new IllegalStateException("New SP protector does not work");
+                }
+
+                // Replace the protector.  Wait until PHASE_BOOT_COMPLETED to destroy the old
+                // protector, since the Weaver slot erasure and freeing cannot be rolled back.
+                setCurrentLskfBasedProtectorId(newProtectorId, userId);
+                mProtectorsToDestroyOnBootCompleted.add(new Pair(protectorId, userId));
+            } else {
+                Slog.i(TAG, "Synthetic password is already not protected by Weaver");
+            }
+        } else if (sp == null) {
             Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
             return;
         }
-        // While setCeStorageProtection() is idempotent, it does log some error messages when called
-        // again.  Skip it if we know it was already handled by an earlier upgrade to Android 14.
-        if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
+
+        // Call setCeStorageProtection(), to re-encrypt the CE key with the SP if it's currently
+        // encrypted by an empty secret.  Skip this if it was definitely already done as part of the
+        // upgrade to Android 14, since while setCeStorageProtection() is idempotent it does log
+        // some error messages when called again.  Do not skip this if
+        // config_disableWeaverOnUnsecuredUsers=true, since in that case we'd like to recover from
+        // the case where an earlier upgrade to Android 14 incorrectly skipped this step.
+        if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null
+                || isWeaverDisabledOnUnsecuredUsers()) {
             Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
             setCeStorageProtection(userId, sp);
         }
@@ -1165,6 +1240,17 @@
         initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
     }
 
+    private void destroyMigratedProtectors() {
+        if (!mProtectorsToDestroyOnBootCompleted.isEmpty()) {
+            synchronized (mSpManager) {
+                for (Pair<Long, Integer> pair : mProtectorsToDestroyOnBootCompleted) {
+                    mSpManager.destroyLskfBasedProtector(pair.first, pair.second);
+                }
+            }
+        }
+        mProtectorsToDestroyOnBootCompleted = null; // The list is no longer needed.
+    }
+
     /**
      * Returns the lowest password quality that still presents the same UI for entering it.
      *
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 3a429b0..47788f2 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -195,6 +195,8 @@
         // ERROR: password / token fails verification
         // RETRY: password / token verification is throttled at the moment.
         @Nullable public VerifyCredentialResponse gkResponse;
+        // For unlockLskfBasedProtector() this is set to true if the protector uses Weaver.
+        public boolean usedWeaver;
     }
 
     /**
@@ -532,6 +534,11 @@
                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
     }
 
+    private boolean isWeaverDisabledOnUnsecuredUsers() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers);
+    }
+
     @VisibleForTesting
     protected android.hardware.weaver.V1_0.IWeaver getWeaverHidlService() throws RemoteException {
         try {
@@ -1011,7 +1018,13 @@
 
         Slogf.i(TAG, "Creating LSKF-based protector %016x for user %d", protectorId, userId);
 
-        final IWeaver weaver = getWeaverService();
+        final IWeaver weaver;
+        if (credential.isNone() && isWeaverDisabledOnUnsecuredUsers()) {
+            weaver = null;
+            Slog.w(TAG, "Not using Weaver for unsecured user (disabled by config)");
+        } else {
+            weaver = getWeaverService();
+        }
         if (weaver != null) {
             // Weaver is available, so make the protector use it to verify the LSKF.  Do this even
             // if the LSKF is empty, as that gives us support for securely deleting the protector.
@@ -1404,6 +1417,7 @@
         int weaverSlot = loadWeaverSlot(protectorId, userId);
         if (weaverSlot != INVALID_WEAVER_SLOT) {
             // Protector uses Weaver to verify the LSKF
+            result.usedWeaver = true;
             final IWeaver weaver = getWeaverService();
             if (weaver == null) {
                 Slog.e(TAG, "Protector uses Weaver, but Weaver is unavailable");
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 99e66a2..6c2d4f7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -12018,6 +12018,10 @@
         if (record != null && (record.getSbn().getNotification().flags
                 & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0
                 && !record.isCanceledAfterLifetimeExtension()) {
+            // Mark that the notification is being updated due to cancelation, so it won't
+            // be updated again if the app cancels multiple times.
+            record.setCanceledAfterLifetimeExtension(true);
+
             boolean isAppForeground = pkg != null && packageImportance == IMPORTANCE_FOREGROUND;
 
             // Save the original Record's post silently value, so we can restore it after we send
@@ -12033,9 +12037,6 @@
             PostNotificationTracker tracker = mPostNotificationTrackerFactory.newTracker(null);
             tracker.addCleanupRunnable(() -> {
                 synchronized (mNotificationLock) {
-                    // Mark that the notification has been updated due to cancelation, so it won't
-                    // be updated again if the app cancels multiple times.
-                    record.setCanceledAfterLifetimeExtension(true);
                     // Set the post silently status to the record's previous value.
                     record.setPostSilently(savedPostSilentlyState);
                     // Remove FLAG_ONLY_ALERT_ONCE if the notification did not previously have it.
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/WeaverBasedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/WeaverBasedSyntheticPasswordTests.java
index 50f3a88..5bcddc4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/WeaverBasedSyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/WeaverBasedSyntheticPasswordTests.java
@@ -1,6 +1,10 @@
 package com.android.server.locksettings;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
 
 import android.platform.test.annotations.Presubmit;
 
@@ -56,4 +60,44 @@
         mService.initializeSyntheticPassword(userId); // This should allocate a Weaver slot.
         assertEquals(Sets.newHashSet(0), mPasswordSlotManager.getUsedSlots());
     }
+
+    private int getNumUsedWeaverSlots() {
+        return mPasswordSlotManager.getUsedSlots().size();
+    }
+
+    @Test
+    public void testDisableWeaverOnUnsecuredUsers_false() {
+        final int userId = PRIMARY_USER_ID;
+        when(mResources.getBoolean(eq(
+                        com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers)))
+                .thenReturn(false);
+        assertEquals(0, getNumUsedWeaverSlots());
+        mService.initializeSyntheticPassword(userId);
+        assertEquals(1, getNumUsedWeaverSlots());
+        assertTrue(mService.setLockCredential(newPassword("password"), nonePassword(), userId));
+        assertEquals(1, getNumUsedWeaverSlots());
+        assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), userId));
+        assertEquals(1, getNumUsedWeaverSlots());
+    }
+
+    @Test
+    public void testDisableWeaverOnUnsecuredUsers_true() {
+        final int userId = PRIMARY_USER_ID;
+        when(mResources.getBoolean(eq(
+                        com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers)))
+                .thenReturn(true);
+        assertEquals(0, getNumUsedWeaverSlots());
+        mService.initializeSyntheticPassword(userId);
+        assertEquals(0, getNumUsedWeaverSlots());
+        assertTrue(mService.setLockCredential(newPassword("password"), nonePassword(), userId));
+        assertEquals(1, getNumUsedWeaverSlots());
+        assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), userId));
+        assertEquals(0, getNumUsedWeaverSlots());
+    }
+
+    @Test
+    public void testDisableWeaverOnUnsecuredUsers_defaultsToFalse() {
+        assertFalse(mResources.getBoolean(
+                    com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3c120e1..1349ee0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3206,7 +3206,6 @@
         // Send two cancelations.
         mBinderService.cancelNotificationWithTag(mPkg, mPkg, sbn.getTag(), sbn.getId(),
                 sbn.getUserId());
-        waitForIdle();
         mBinderService.cancelNotificationWithTag(mPkg, mPkg, sbn.getTag(), sbn.getId(),
                 sbn.getUserId());
         waitForIdle();
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index ff9cba2..f001232 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -33,6 +33,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.util.ArrayList;
@@ -185,11 +186,7 @@
                 if (hasPrivileges) {
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
-                    if (!isUpdatedSystemApp(ai) && enabledSetting
-                            == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
-                            || enabledSetting
-                            == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
-                            || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                    if (shouldUpdateEnabledState(ai, enabledSetting)) {
                         Log.i(TAG, "Update state (" + packageName + "): ENABLED for user "
                                 + userId);
                         context.createContextAsUser(UserHandle.of(userId), 0)
@@ -330,6 +327,21 @@
         }
     }
 
+    private static boolean shouldUpdateEnabledState(ApplicationInfo appInfo, int enabledSetting) {
+        if (Flags.cleanupCarrierAppUpdateEnabledStateLogic()) {
+            return !isUpdatedSystemApp(appInfo)
+                    && (enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            || enabledSetting
+                                    == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                            || (appInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0);
+        } else {
+            return !isUpdatedSystemApp(appInfo)
+                            && enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                    || enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                    || (appInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0;
+        }
+    }
+
     /**
      * Returns the list of "default" carrier apps.
      *
diff --git a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
index e3a129f..d03ad5c 100644
--- a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
+++ b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
@@ -61,8 +61,13 @@
     @Test
     public void canRead() {
         ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance();
-        instance.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
-        // Don't actually care about the value of the above.
+        try {
+            instance.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
+            // Don't actually care about the value of the above.
+        } catch (java.time.DateTimeException e) {
+            // This exception is okay during testing.  It means there was no time source, which
+            // could be because of network problems or a feature being flagged off.
+        }
     }
 
     /** Application processes should not have mutable access. */
diff --git a/tests/Internal/src/com/android/internal/os/OWNERS b/tests/Internal/src/com/android/internal/os/OWNERS
new file mode 100644
index 0000000..64ffa46
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/os/OWNERS
@@ -0,0 +1,2 @@
+# ApplicationSharedMemory
+per-file *ApplicationSharedMemory* = file:/PERFORMANCE_OWNERS