Merge changes Id1ecee47,I4a5e4f70 into main

* changes:
  Add logs for communal hub
  Ignore GlanceableHubContainerControllerTest for now
diff --git a/core/api/current.txt b/core/api/current.txt
index cf5a261..e957676 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -44544,6 +44544,7 @@
     method @Nullable public android.telephony.CellIdentity getCellIdentity();
     method public int getDomain();
     method @Nullable public String getRegisteredPlmn();
+    method @FlaggedApi("com.android.internal.telephony.flags.network_registration_info_reject_cause") public int getRejectCause();
     method public int getTransportType();
     method public boolean isNetworkRegistered();
     method public boolean isNetworkRoaming();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 86f2b628..6afc948 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14128,7 +14128,6 @@
     method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
     method public int getNetworkRegistrationState();
     method @Deprecated public int getRegistrationState();
-    method public int getRejectCause();
     method public int getRoamingType();
     method public boolean isEmergencyEnabled();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index bde562d..33b1134 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -409,6 +409,7 @@
                     "packageName = " + mAttributionSourceState.packageName + ", " +
                     "attributionTag = " + mAttributionSourceState.attributionTag + ", " +
                     "token = " + mAttributionSourceState.token + ", " +
+                    "deviceId = " + mAttributionSourceState.deviceId + ", " +
                     "next = " + (mAttributionSourceState.next != null
                                     && mAttributionSourceState.next.length > 0
                             ? mAttributionSourceState.next[0] : null) +
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 90cd471..1ca11e6 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -47,4 +47,11 @@
     name: "configurable_selector_ui_enabled"
     description: "Enables OEM configurable Credential Selector UI"
     bug: "319448437"
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "credential_manager"
+    name: "credman_biometric_api_enabled"
+    description: "Enables Credential Manager to work with the Biometric Authenticate API"
+    bug: "323211850"
+}
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
new file mode 100644
index 0000000..9f0b7c3
--- /dev/null
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.widget.flags"
+
+flag {
+   name: "notif_linearlayout_optimized"
+   namespace: "systemui"
+   description: "Enables notification specific LinearLayout optimization"
+   bug: "316110233"
+}
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_satellite_alt_24px.xml b/core/res/res/drawable/ic_satellite_alt_24px.xml
new file mode 100644
index 0000000..f9ca7dc
--- /dev/null
+++ b/core/res/res/drawable/ic_satellite_alt_24px.xml
@@ -0,0 +1,25 @@
+<!--
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?android:attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M560,928L560,848Q677,848 758.5,766.5Q840,685 840,568L920,568Q920,643 891.5,708.5Q863,774 814.5,822.5Q766,871 700.5,899.5Q635,928 560,928ZM560,768L560,688Q610,688 645,653Q680,618 680,568L760,568Q760,651 701.5,709.5Q643,768 560,768ZM222,903Q207,903 192,897Q177,891 165,880L23,738Q12,726 6,711Q0,696 0,681Q0,665 6,650.5Q12,636 23,625L150,498Q173,475 207,474.5Q241,474 264,497L314,547L342,519L292,469Q269,446 269,413Q269,380 292,357L349,300Q372,277 405.5,277Q439,277 462,300L512,350L540,322L490,272Q467,249 467,215.5Q467,182 490,159L617,32Q629,20 644,14Q659,8 674,8Q689,8 703.5,14Q718,20 730,32L872,174Q884,185 889.5,199.5Q895,214 895,230Q895,245 889.5,260Q884,275 872,287L745,414Q722,437 688.5,437Q655,437 632,414L582,364L554,392L604,442Q627,465 626.5,498.5Q626,532 603,555L547,611Q524,634 490.5,634Q457,634 434,611L384,561L356,589L406,639Q429,662 428.5,696Q428,730 405,753L278,880Q267,891 252.5,897Q238,903 222,903ZM222,824Q222,824 222,824Q222,824 222,824L264,782L122,640L80,682Q80,682 80,682Q80,682 80,682L222,824ZM307,739L349,697Q349,697 349,697Q349,697 349,697L207,555Q207,555 207,555Q207,555 207,555L165,597L307,739ZM491,555Q491,555 491,555Q491,555 491,555L547,499Q547,499 547,499Q547,499 547,499L405,357Q405,357 405,357Q405,357 405,357L349,413Q349,413 349,413Q349,413 349,413L491,555ZM689,357Q689,357 689,357Q689,357 689,357L731,315L589,173L547,215Q547,215 547,215Q547,215 547,215L689,357ZM774,272L816,230Q816,230 816,230Q816,230 816,230L674,88Q674,88 674,88Q674,88 674,88L632,130L774,272ZM448,456L448,456Q448,456 448,456Q448,456 448,456L448,456Q448,456 448,456Q448,456 448,456L448,456Q448,456 448,456Q448,456 448,456L448,456Q448,456 448,456Q448,456 448,456Z"/>
+</vector>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 558bae7..dd6e79e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6386,4 +6386,14 @@
     <string name="redacted_notification_message"></string>
     <!-- Notification action title used instead of a notification's normal title sensitive [CHAR_LIMIT=NOTIF_BODY] -->
     <string name="redacted_notification_action_title"></string>
+
+    <!-- Satellite related messages -->
+    <!-- Notification title when satellite service is connected. -->
+    <string name="satellite_notification_title">Auto connected to satellite</string>
+    <!-- Notification summary when satellite service is connected. [CHAR LIMIT=NONE] -->
+    <string name="satellite_notification_summary">You can send and receive messages without a mobile or Wi-Fi network</string>
+    <!-- Invoke "What to expect" dialog of messaging application -->
+    <string name="satellite_notification_open_message">Open Messages</string>
+    <!-- Invoke Satellite setting activity of Settings -->
+    <string name="satellite_notification_how_it_works">How it works</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8b74cbf..699c8ac 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5329,4 +5329,11 @@
   <java-symbol type="bool" name="config_showPercentageTextDuringRebootToUpdate" />
 
   <java-symbol type="integer" name="config_minMillisBetweenInputUserActivityEvents" />
+
+  <!-- System notification for satellite service -->
+  <java-symbol type="string" name="satellite_notification_title" />
+  <java-symbol type="string" name="satellite_notification_summary" />
+  <java-symbol type="string" name="satellite_notification_open_message" />
+  <java-symbol type="string" name="satellite_notification_how_it_works" />
+  <java-symbol type="drawable" name="ic_satellite_alt_24px" />
 </resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index f476799..513e022 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -203,7 +203,6 @@
         "androidx.test.uiautomator_uiautomator",
         "compatibility-device-util-axt",
         "flag-junit",
-        "mockito_ravenwood",
         "platform-test-annotations",
         "flag-junit",
         "testng",
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 2ccee71..f5563a7 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -60,7 +60,6 @@
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.test.rules",
-        "mockito_ravenwood",
         "frameworks-base-testutils",
         "servicestests-utils",
     ],
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 238e6b5..c5a0102 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -797,14 +797,20 @@
                     mPipBoundsAlgorithm.getMovementBounds(postChangeBounds),
                     mPipBoundsState.getStashedState());
 
-            updateDisplayLayout.run();
+            // Scale PiP on density dpi change, so it appears to be the same size physically.
+            final boolean densityDpiChanged =
+                    mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
+                    && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
+                            != layout.densityDpi());
+            if (densityDpiChanged) {
+                final float scale = (float) layout.densityDpi()
+                        / mPipDisplayLayoutState.getDisplayLayout().densityDpi();
+                postChangeBounds.set(0, 0,
+                        (int) (postChangeBounds.width() * scale),
+                        (int) (postChangeBounds.height() * scale));
+            }
 
-            // Resize the PiP bounds to be at the same scale relative to the new size spec. For
-            // example, if PiP was resized to 90% of the maximum size on the previous layout,
-            // make sure it is 90% of the new maximum size spec.
-            postChangeBounds.set(0, 0,
-                    (int) (mPipBoundsState.getMaxSize().x * mPipBoundsState.getBoundsScale()),
-                    (int) (mPipBoundsState.getMaxSize().y * mPipBoundsState.getBoundsScale()));
+            updateDisplayLayout.run();
 
             // Calculate the PiP bounds in the new orientation based on same fraction along the
             // rotated movement bounds.
@@ -821,10 +827,6 @@
             mPipBoundsState.setHasUserResizedPip(true);
             mTouchHandler.setUserResizeBounds(postChangeBounds);
 
-            final boolean densityDpiChanged =
-                    mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
-                            && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
-                            != layout.densityDpi());
             if (densityDpiChanged) {
                 // Using PipMotionHelper#movePip directly here may cause race condition since
                 // the app content in PiP mode may or may not be updated for the new density dpi.
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index d55d28d..b5f7caa 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -31,6 +31,8 @@
 #include <vk/GrVkExtensions.h>
 #include <vk/GrVkTypes.h>
 
+#include <sstream>
+
 #include "Properties.h"
 #include "RenderThread.h"
 #include "pipeline/skia/ShaderCache.h"
@@ -40,7 +42,8 @@
 namespace uirenderer {
 namespace renderthread {
 
-static std::array<std::string_view, 20> sEnableExtensions{
+// Not all of these are strictly required, but are all enabled if present.
+static std::array<std::string_view, 21> sEnableExtensions{
         VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
         VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
         VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
@@ -61,6 +64,7 @@
         VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
         VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
         VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME,
+        VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
 };
 
 static bool shouldEnableExtension(const std::string_view& extension) {
@@ -303,6 +307,15 @@
     *tailPNext = ycbcrFeature;
     tailPNext = &ycbcrFeature->pNext;
 
+    if (grExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
+        VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures =
+                new VkPhysicalDeviceFaultFeaturesEXT;
+        deviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
+        deviceFaultFeatures->pNext = nullptr;
+        *tailPNext = deviceFaultFeatures;
+        tailPNext = &deviceFaultFeatures->pNext;
+    }
+
     // query to get the physical device features
     mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
     // this looks like it would slow things down,
@@ -405,6 +418,79 @@
     });
 }
 
+namespace {
+void onVkDeviceFault(const std::string& contextLabel, const std::string& description,
+                     const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+                     const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
+                     const std::vector<std::byte>& vendorBinaryData) {
+    // The final crash string should contain as much differentiating info as possible, up to 1024
+    // bytes. As this final message is constructed, the same information is also dumped to the logs
+    // but in a more verbose format. Building the crash string is unsightly, so the clearer logging
+    // statement is always placed first to give context.
+    ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", contextLabel.c_str(), description.c_str());
+    std::stringstream crashMsg;
+    crashMsg << "VK_ERROR_DEVICE_LOST (" << contextLabel;
+
+    if (!addressInfos.empty()) {
+        ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size());
+        crashMsg << ", " << addressInfos.size() << " address info (";
+        for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) {
+            ALOGE(" addressType:       %d", (int)addressInfo.addressType);
+            ALOGE("  reportedAddress:  %" PRIu64, addressInfo.reportedAddress);
+            ALOGE("  addressPrecision: %" PRIu64, addressInfo.addressPrecision);
+            crashMsg << addressInfo.addressType << ":"
+                     << addressInfo.reportedAddress << ":"
+                     << addressInfo.addressPrecision << ", ";
+        }
+        crashMsg.seekp(-2, crashMsg.cur);  // Move back to overwrite trailing ", "
+        crashMsg << ")";
+    }
+
+    if (!vendorInfos.empty()) {
+        ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size());
+        crashMsg << ", " << vendorInfos.size() << " vendor info (";
+        for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) {
+            ALOGE(" description:      %s", vendorInfo.description);
+            ALOGE("  vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode);
+            ALOGE("  vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData);
+            // Omit descriptions for individual vendor info structs in the crash string, as the
+            // fault code and fault data fields should be enough for clustering, and the verbosity
+            // isn't worth it. Additionally, vendors may just set the general description field of
+            // the overall fault to the description of the first element in this list, and that
+            // overall description will be placed at the end of the crash string.
+            crashMsg << vendorInfo.vendorFaultCode << ":"
+                     << vendorInfo.vendorFaultData << ", ";
+        }
+        crashMsg.seekp(-2, crashMsg.cur);  // Move back to overwrite trailing ", "
+        crashMsg << ")";
+    }
+
+    if (!vendorBinaryData.empty()) {
+        // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports
+        ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics"
+              " Stack team if you observe this message).",
+              vendorBinaryData.size());
+        crashMsg << ", " << vendorBinaryData.size() << " bytes binary";
+    }
+
+    crashMsg << "): " << description;
+    LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str());
+}
+
+void deviceLostProcRenderThread(void* callbackContext, const std::string& description,
+                                const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+                                const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
+                                const std::vector<std::byte>& vendorBinaryData) {
+    onVkDeviceFault("RenderThread", description, addressInfos, vendorInfos, vendorBinaryData);
+}
+void deviceLostProcUploadThread(void* callbackContext, const std::string& description,
+                                const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+                                const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
+                                const std::vector<std::byte>& vendorBinaryData) {
+    onVkDeviceFault("UploadThread", description, addressInfos, vendorInfos, vendorBinaryData);
+}
+}  // anonymous namespace
+
 static void onGrContextReleased(void* context) {
     VulkanManager* manager = (VulkanManager*)context;
     manager->decStrong((void*)onGrContextReleased);
@@ -430,6 +516,10 @@
     backendContext.fVkExtensions = &mExtensions;
     backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
     backendContext.fGetProc = std::move(getProc);
+    backendContext.fDeviceLostContext = nullptr;
+    backendContext.fDeviceLostProc = (contextType == ContextType::kRenderThread)
+                                             ? deviceLostProcRenderThread
+                                             : deviceLostProcUploadThread;
 
     LOG_ALWAYS_FATAL_IF(options.fContextDeleteProc != nullptr, "Conflicting fContextDeleteProcs!");
     this->incStrong((void*)onGrContextReleased);
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 018eaf6..f110705 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -12,4 +12,11 @@
     namespace: "media_tv"
     description: "Enable the TV client-side AD framework."
     bug: "303506816"
+}
+
+flag {
+    name: "tiaf_v_apis"
+    namespace: "media_tv"
+    description: "TIAF V3.0 APIs for Android V"
+    bug: "303323657"
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/more_horiz_24px.xml b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
index 7b235f8..0100718 100644
--- a/packages/CredentialManager/res/drawable/more_horiz_24px.xml
+++ b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
@@ -3,6 +3,7 @@
     android:height="24dp"
     android:viewportWidth="960"
     android:viewportHeight="960"
+    android:contentDescription="@string/more_options_content_description"
     android:tint="?attr/colorControlNormal">
   <path
       android:fillColor="@android:color/white"
diff --git a/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml b/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
index b97e992..fdda9ea 100644
--- a/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
+++ b/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
@@ -28,7 +28,7 @@
         android:layout_height="wrap_content"
         android:layout_centerVertical="true"
         android:layout_alignParentStart="true"
-        android:contentDescription="@string/provider_icon_content_description"
+        android:contentDescription="@string/more_options_content_description"
         android:background="@null"/>
     <TextView
         android:id="@android:id/text1"
diff --git a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
index 261154f..c7c2fda 100644
--- a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
+++ b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
@@ -25,7 +25,6 @@
             android:id="@android:id/icon1"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:contentDescription="@string/dropdown_presentation_more_sign_in_options_text"
             android:layout_centerVertical="true"
             android:layout_alignParentStart="true"
             android:background="@null"/>
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index f98164b..82b47a9 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -172,5 +172,5 @@
   <!-- Strings for dropdown presentation. -->
   <!-- Text shown in the dropdown presentation to select more sign in options. [CHAR LIMIT=120] -->
   <string name="dropdown_presentation_more_sign_in_options_text">Sign-in options</string>
-  <string name="provider_icon_content_description">Credential provider icon</string>
+  <string name="more_options_content_description">More</string>
 </resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
index 68f1c86..02afc54 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
@@ -74,6 +74,8 @@
                     setMaxHeightMethodName,
                     context.resources.getDimensionPixelSize(
                             com.android.credentialmanager.R.dimen.autofill_icon_size));
+            remoteViews.setContentDescription(android.R.id.icon1, credentialEntryInfo
+                    .providerDisplayName);
             val drawableId =
                     com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one
             remoteViews.setInt(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index ee01bf9..c5485c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -642,6 +642,53 @@
         }
 
     @Test
+    fun isCommunalVisible() =
+        testScope.runTest {
+            val transitionState =
+                MutableStateFlow<ObservableCommunalTransitionState>(
+                    ObservableCommunalTransitionState.Idle(CommunalSceneKey.Blank)
+                )
+            communalRepository.setTransitionState(transitionState)
+
+            // isCommunalVisible is false when not on communal.
+            val isCommunalVisible by collectLastValue(underTest.isCommunalVisible)
+            assertThat(isCommunalVisible).isEqualTo(false)
+
+            // Start transition to communal.
+            transitionState.value =
+                ObservableCommunalTransitionState.Transition(
+                    fromScene = CommunalSceneKey.Blank,
+                    toScene = CommunalSceneKey.Communal,
+                    progress = flowOf(0f),
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                )
+
+            // isCommunalVisible is true once transition starts.
+            assertThat(isCommunalVisible).isEqualTo(true)
+
+            // Finish transition to communal
+            transitionState.value =
+                ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+
+            // isCommunalVisible is true since we're on communal.
+            assertThat(isCommunalVisible).isEqualTo(true)
+
+            // Start transition away from communal.
+            transitionState.value =
+                ObservableCommunalTransitionState.Transition(
+                    fromScene = CommunalSceneKey.Communal,
+                    toScene = CommunalSceneKey.Blank,
+                    progress = flowOf(1.0f),
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                )
+
+            // isCommunalVisible is still true as the false as soon as transition away runs.
+            assertThat(isCommunalVisible).isEqualTo(true)
+        }
+
+    @Test
     fun testShowWidgetEditorStartsActivity() =
         testScope.runTest {
             underTest.showWidgetEditor()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 74c1970..2a9bc4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -28,7 +28,6 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
 import org.junit.Before;
@@ -47,8 +46,6 @@
     @Mock
     CentralSurfaces mCentralSurfaces;
     @Mock
-    NotificationShadeWindowController mNotificationShadeWindowController;
-    @Mock
     DreamTouchHandler.TouchSession mTouchSession;
     CommunalTouchHandler mTouchHandler;
 
@@ -59,17 +56,10 @@
         MockitoAnnotations.initMocks(this);
         mTouchHandler = new CommunalTouchHandler(
                 Optional.of(mCentralSurfaces),
-                mNotificationShadeWindowController,
                 INITIATION_WIDTH);
     }
 
     @Test
-    public void testSessionStartForcesShadeOpen() {
-        mTouchHandler.onSessionStart(mTouchSession);
-        verify(mNotificationShadeWindowController).setForcePluginOpen(true, mTouchHandler);
-    }
-
-    @Test
     public void testEventPropagation() {
         final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
new file mode 100644
index 0000000..b7b3fdb
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.qs.tiles.impl.fontscaling.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.qs.tiles.impl.fontscaling.qsFontScalingTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FontScalingTileMapperTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val fontScalingTileConfig = kosmos.qsFontScalingTileConfig
+
+    private val mapper by lazy {
+        FontScalingTileMapper(
+            context.orCreateTestableResources
+                .apply { addOverride(R.drawable.ic_qs_font_scaling, TestStubDrawable()) }
+                .resources,
+            context.theme
+        )
+    }
+
+    @Test
+    fun activeStateMatchesEnabledModel() {
+        val inputModel = FontScalingTileModel
+
+        val outputState = mapper.map(fontScalingTileConfig, inputModel)
+
+        val expectedState = createFontScalingTileState()
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    private fun createFontScalingTileState(): QSTileState =
+        QSTileState(
+            {
+                Icon.Loaded(
+                    context.getDrawable(
+                        R.drawable.ic_qs_font_scaling,
+                    )!!,
+                    null
+                )
+            },
+            context.getString(R.string.quick_settings_font_scaling_label),
+            QSTileState.ActivationState.ACTIVE,
+            null,
+            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+            context.getString(R.string.quick_settings_font_scaling_label),
+            null,
+            QSTileState.SideViewIcon.Chevron,
+            QSTileState.EnabledState.ENABLED,
+            Switch::class.qualifiedName
+        )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
new file mode 100644
index 0000000..39bc8a6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor
+
+import android.os.UserHandle
+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.coroutines.collectValues
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FontScalingTileDataInteractorTest : SysuiTestCase() {
+    private val underTest: FontScalingTileDataInteractor = FontScalingTileDataInteractor()
+    private val testUser = UserHandle.of(1)
+
+    @Test
+    fun collectsExactlyOneValue() = runTest {
+        val flowValues by
+            collectValues(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+
+        runCurrent()
+
+        Truth.assertThat(flowValues.size).isEqualTo(1)
+    }
+
+    @Test
+    fun lastValueIsNotEmpty() = runTest {
+        val flowValue by
+            collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+
+        runCurrent()
+
+        Truth.assertThat(flowValue).isNotNull()
+    }
+
+    @Test
+    fun isAvailable() = runTest {
+        val availability by collectLastValue(underTest.availability(testUser))
+
+        Truth.assertThat(availability).isTrue()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
new file mode 100644
index 0000000..2384915
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.qs.tiles.impl.fontscaling.domain.interactor
+
+import android.provider.Settings
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.intentInputs
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.statusbar.phone.FakeKeyguardStateController
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FontScalingUserActionInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
+    private val keyguardStateController = FakeKeyguardStateController()
+
+    private lateinit var underTest: FontScalingTileUserActionInteractor
+
+    @Mock private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate
+    @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+    @Mock private lateinit var dialog: SystemUIDialog
+    @Mock private lateinit var activityStarter: ActivityStarter
+
+    @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
+
+    @Before
+    fun setup() {
+        activityStarter = mock<ActivityStarter>()
+        dialogLaunchAnimator = mock<DialogLaunchAnimator>()
+        dialog = mock<SystemUIDialog>()
+        fontScalingDialogDelegate =
+            mock<FontScalingDialogDelegate> { whenever(createDialog()).thenReturn(dialog) }
+        argumentCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+
+        underTest =
+            FontScalingTileUserActionInteractor(
+                kosmos.testScope.coroutineContext,
+                qsTileIntentUserActionHandler,
+                { fontScalingDialogDelegate },
+                keyguardStateController,
+                dialogLaunchAnimator,
+                activityStarter
+            )
+    }
+
+    @Test
+    fun clickTile_screenUnlocked_showDialogAnimationFromView() =
+        kosmos.testScope.runTest {
+            keyguardStateController.isShowing = false
+            val testView = View(context)
+
+            underTest.handleInput(click(FontScalingTileModel, view = testView))
+
+            verify(activityStarter)
+                .executeRunnableDismissingKeyguard(
+                    argumentCaptor.capture(),
+                    eq(null),
+                    eq(true),
+                    eq(true),
+                    eq(false)
+                )
+            argumentCaptor.value.run()
+            verify(dialogLaunchAnimator).showFromView(any(), eq(testView), nullable(), anyBoolean())
+        }
+
+    @Test
+    fun clickTile_onLockScreen_neverShowDialogAnimationFromView_butShowsDialog() =
+        kosmos.testScope.runTest {
+            keyguardStateController.isShowing = true
+            val testView = View(context)
+
+            underTest.handleInput(click(FontScalingTileModel, view = testView))
+
+            verify(activityStarter)
+                .executeRunnableDismissingKeyguard(
+                    argumentCaptor.capture(),
+                    eq(null),
+                    eq(true),
+                    eq(true),
+                    eq(false)
+                )
+            argumentCaptor.value.run()
+            verify(dialogLaunchAnimator, never())
+                .showFromView(any(), eq(testView), nullable(), anyBoolean())
+            verify(dialog).show()
+        }
+
+    @Test
+    fun handleLongClick() =
+        kosmos.testScope.runTest {
+            underTest.handleInput(QSTileInputTestKtx.longClick(FontScalingTileModel))
+
+            Truth.assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)
+            val intentInput = qsTileIntentUserActionHandler.intentInputs.last()
+            val actualIntentAction = intentInput.intent.action
+            val expectedIntentAction = Settings.ACTION_TEXT_READING_SETTINGS
+            Truth.assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 504ded3..d6d2509 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -76,6 +76,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
@@ -262,6 +264,7 @@
                 simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
                 authenticationInteractor = dagger.Lazy { kosmos.authenticationInteractor },
                 windowController = mock(),
+                deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
             )
         startable.start()
 
@@ -518,6 +521,17 @@
             assertCurrentScene(SceneKey.Lockscreen)
         }
 
+    @Test
+    fun factoryResetProtectionActive_isNotVisible() =
+        testScope.runTest {
+            val isVisible by collectLastValue(sceneContainerViewModel.isVisible)
+            assertThat(isVisible).isTrue()
+
+            kosmos.fakeDeviceProvisioningRepository.setFactoryResetProtectionActive(isActive = true)
+
+            assertThat(isVisible).isFalse()
+        }
+
     /**
      * Asserts that the current scene in the view-model matches what's expected.
      *
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 16cb623..1abbc92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -49,6 +49,7 @@
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -112,6 +113,7 @@
                 simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
                 authenticationInteractor = dagger.Lazy { authenticationInteractor },
                 windowController = windowController,
+                deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
index c669c6f..1f6ba29 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
@@ -33,6 +33,10 @@
         mOccluded = occluded;
     }
 
+    public void setShowing(boolean isShowing) {
+        mShowing = isShowing;
+    }
+
     @Override
     public boolean isShowing() {
         return mShowing;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index 135ab35..4047623 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -31,6 +31,10 @@
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.FontScalingTileMapper
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
 import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
@@ -93,6 +97,7 @@
     companion object {
         const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
         const val COLOR_INVERSION_TILE_SPEC = "inversion"
+        const val FONT_SCALING_TILE_SPEC = "font_scaling"
 
         @Provides
         @IntoMap
@@ -155,5 +160,36 @@
                 stateInteractor,
                 mapper,
             )
+
+        @Provides
+        @IntoMap
+        @StringKey(FONT_SCALING_TILE_SPEC)
+        fun provideFontScalingTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+            QSTileConfig(
+                tileSpec = TileSpec.create(FONT_SCALING_TILE_SPEC),
+                uiConfig =
+                    QSTileUIConfig.Resource(
+                        iconRes = R.drawable.ic_qs_font_scaling,
+                        labelRes = R.string.quick_settings_font_scaling_label,
+                    ),
+                instanceId = uiEventLogger.getNewInstanceId(),
+            )
+
+        /** Inject FontScaling Tile into tileViewModelMap in QSModule */
+        @Provides
+        @IntoMap
+        @StringKey(FONT_SCALING_TILE_SPEC)
+        fun provideFontScalingTileViewModel(
+            factory: QSTileViewModelFactory.Static<FontScalingTileModel>,
+            mapper: FontScalingTileMapper,
+            stateInteractor: FontScalingTileDataInteractor,
+            userActionInteractor: FontScalingTileUserActionInteractor
+        ): QSTileViewModel =
+            factory.create(
+                TileSpec.create(FONT_SCALING_TILE_SPEC),
+                userActionInteractor,
+                stateInteractor,
+                mapper,
+            )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index fc6af55..9658401 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -161,9 +161,18 @@
             .distinctUntilChanged()
 
     /**
-     * Flow that emits a boolean if the communal UI is showing, ie. the [desiredScene] is the
-     * [CommunalSceneKey.Communal].
+     * Flow that emits a boolean if the communal UI is the target scene, ie. the [desiredScene] is
+     * the [CommunalSceneKey.Communal].
+     *
+     * This will be true as soon as the desired scene is set programmatically or at whatever point
+     * during a fling that SceneTransitionLayout determines that the end state will be the communal
+     * scene. The value also does not change while flinging away until the target scene is no longer
+     * communal.
+     *
+     * If you need a flow that is only true when communal is fully showing and not in transition,
+     * use [isIdleOnCommunal].
      */
+    // TODO(b/323215860): rename to something more appropriate after cleaning up usages
     val isCommunalShowing: Flow<Boolean> =
         communalRepository.desiredScene
             .map { it == CommunalSceneKey.Communal }
@@ -194,6 +203,16 @@
             it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Communal
         }
 
+    /**
+     * Flow that emits a boolean if any portion of the communal UI is visible at all.
+     *
+     * This flow will be true during any transition and when idle on the communal scene.
+     */
+    val isCommunalVisible: Flow<Boolean> =
+        communalRepository.transitionState.map {
+            !(it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Blank)
+        }
+
     /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
     fun onSceneChanged(newScene: CommunalSceneKey) {
         communalRepository.setDesiredScene(newScene)
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index c9b56a2..05279fc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -23,7 +23,6 @@
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
-import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
 import java.util.Optional;
@@ -34,17 +33,14 @@
 /** {@link DreamTouchHandler} responsible for handling touches to open communal hub. **/
 public class CommunalTouchHandler implements DreamTouchHandler {
     private final int mInitiationWidth;
-    private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final Optional<CentralSurfaces> mCentralSurfaces;
 
     @Inject
     public CommunalTouchHandler(
             Optional<CentralSurfaces> centralSurfaces,
-            NotificationShadeWindowController notificationShadeWindowController,
             @Named(COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth) {
         mInitiationWidth = initiationWidth;
         mCentralSurfaces = centralSurfaces;
-        mNotificationShadeWindowController = notificationShadeWindowController;
     }
 
     @Override
@@ -60,9 +56,8 @@
     }
 
     private void handleSessionStart(CentralSurfaces surfaces, TouchSession session) {
-        // Force the notification shade window open (otherwise the hub won't show while swiping).
-        mNotificationShadeWindowController.setForcePluginOpen(true, this);
-
+        // Notification shade window has its own logic to be visible if the hub is open, no need to
+        // do anything here other than send touch events over.
         session.registerInputListener(ev -> {
             surfaces.handleDreamTouch((MotionEvent) ev);
             if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c69c9ef..6eff792 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -585,10 +585,6 @@
     @JvmField
     val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button")
 
-    // TODO(b/287205379): Tracking bug
-    @JvmField
-    val QS_CONTAINER_GRAPH_OPTIMIZER = releasedFlag( "qs_container_graph_optimizer")
-
     /** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
     @JvmField
     val BLUETOOTH_QS_TILE_DIALOG = releasedFlag("bluetooth_qs_tile_dialog")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 310ec95..ad6a36c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.res.R
 import javax.inject.Inject
+import kotlin.math.roundToInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -74,7 +75,18 @@
                 isTransitionToAod && isUdfps
             }
             .distinctUntilChanged()
-    private val padding: Flow<Int> = udfpsOverlayInteractor.iconPadding
+
+    private val padding: Flow<Int> =
+        deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { udfpsSupported ->
+            if (udfpsSupported) {
+                udfpsOverlayInteractor.iconPadding
+            } else {
+                configurationInteractor.scaleForResolution.map { scale ->
+                    (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+                        .roundToInt()
+                }
+            }
+        }
 
     val viewModel: Flow<ForegroundIconViewModel> =
         combine(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
new file mode 100644
index 0000000..26069c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.qs.tiles.impl.fontscaling.domain
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [FontScalingTileModel] to [QSTileState]. */
+class FontScalingTileMapper
+@Inject
+constructor(
+    @Main private val resources: Resources,
+    private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<FontScalingTileModel> {
+
+    override fun map(config: QSTileConfig, data: FontScalingTileModel): QSTileState =
+        QSTileState.build(resources, theme, config.uiConfig) {
+            val icon =
+                Icon.Loaded(
+                    resources.getDrawable(
+                        R.drawable.ic_qs_font_scaling,
+                        theme,
+                    ),
+                    contentDescription = null
+                )
+            this.icon = { icon }
+            contentDescription = label
+            activationState = QSTileState.ActivationState.ACTIVE
+            sideViewIcon = QSTileState.SideViewIcon.Chevron
+            supportedActions =
+                setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
new file mode 100644
index 0000000..745e6a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.qs.tiles.impl.fontscaling.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Provides [FontScalingTileModel]. */
+class FontScalingTileDataInteractor @Inject constructor() :
+    QSTileDataInteractor<FontScalingTileModel> {
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>
+    ): Flow<FontScalingTileModel> = flowOf(FontScalingTileModel)
+
+    override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
new file mode 100644
index 0000000..b6f4afb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.qs.tiles.impl.fontscaling.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+/** Handles font scaling tile clicks. */
+class FontScalingTileUserActionInteractor
+@Inject
+constructor(
+    @Main private val coroutineContext: CoroutineContext,
+    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+    private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>,
+    private val keyguardStateController: KeyguardStateController,
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val activityStarter: ActivityStarter,
+) : QSTileUserActionInteractor<FontScalingTileModel> {
+
+    override suspend fun handleInput(input: QSTileInput<FontScalingTileModel>): Unit =
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click -> {
+                    // We animate from the touched view only if we are not on the keyguard
+                    val animateFromView: Boolean =
+                        action.view != null && !keyguardStateController.isShowing
+                    val runnable = Runnable {
+                        val dialog: SystemUIDialog =
+                            fontScalingDialogDelegateProvider.get().createDialog()
+                        if (animateFromView) {
+                            dialogLaunchAnimator.showFromView(
+                                dialog,
+                                action.view!!,
+                                DialogCuj(
+                                    InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                    INTERACTION_JANK_TAG
+                                )
+                            )
+                        } else {
+                            dialog.show()
+                        }
+                    }
+
+                    withContext(coroutineContext) {
+                        activityStarter.executeRunnableDismissingKeyguard(
+                            runnable,
+                            /* cancelAction= */ null,
+                            /* dismissShade= */ true,
+                            /* afterKeyguardGone= */ true,
+                            /* deferred= */ false
+                        )
+                    }
+                }
+                is QSTileUserAction.LongClick -> {
+                    qsTileIntentUserActionHandler.handle(
+                        action.view,
+                        Intent(Settings.ACTION_TEXT_READING_SETTINGS)
+                    )
+                }
+            }
+        }
+    companion object {
+        private const val INTERACTION_JANK_TAG = "font_scaling"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/model/FontScalingTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/model/FontScalingTileModel.kt
new file mode 100644
index 0000000..76042df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/model/FontScalingTileModel.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.qs.tiles.impl.fontscaling.domain.model
+
+/** FontScaling tile model. No data needed as the tile just opens a dialog. */
+data object FontScalingTileModel
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 246ccb1..37abc40 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
+import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.printSection
 import com.android.systemui.util.println
@@ -55,6 +56,7 @@
 import kotlinx.coroutines.flow.distinctUntilChangedBy
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.launch
@@ -81,6 +83,7 @@
     private val simBouncerInteractor: Lazy<SimBouncerInteractor>,
     private val authenticationInteractor: Lazy<AuthenticationInteractor>,
     private val windowController: NotificationShadeWindowController,
+    private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
 ) : CoreStartable {
 
     override fun start() {
@@ -112,26 +115,33 @@
     private fun hydrateVisibility() {
         applicationScope.launch {
             // TODO(b/296114544): Combine with some global hun state to make it visible!
-            sceneInteractor.transitionState
-                .mapNotNull { state ->
-                    when (state) {
-                        is ObservableTransitionState.Idle -> {
-                            if (state.scene != SceneKey.Gone) {
-                                true to "scene is not Gone"
-                            } else {
-                                false to "scene is Gone"
+            deviceProvisioningInteractor.isFactoryResetProtectionActive
+                .flatMapLatest { isFrpActive ->
+                    if (isFrpActive) {
+                        flowOf(false to "Factory Reset Protection is active")
+                    } else {
+                        sceneInteractor.transitionState
+                            .mapNotNull { state ->
+                                when (state) {
+                                    is ObservableTransitionState.Idle -> {
+                                        if (state.scene != SceneKey.Gone) {
+                                            true to "scene is not Gone"
+                                        } else {
+                                            false to "scene is Gone"
+                                        }
+                                    }
+                                    is ObservableTransitionState.Transition -> {
+                                        if (state.fromScene == SceneKey.Gone) {
+                                            true to "scene transitioning away from Gone"
+                                        } else {
+                                            null
+                                        }
+                                    }
+                                }
                             }
-                        }
-                        is ObservableTransitionState.Transition -> {
-                            if (state.fromScene == SceneKey.Gone) {
-                                true to "scene transitioning away from Gone"
-                            } else {
-                                null
-                            }
-                        }
+                            .distinctUntilChanged()
                     }
                 }
-                .distinctUntilChanged()
                 .collect { (isVisible, loggingReason) ->
                     sceneInteractor.setVisible(isVisible, loggingReason)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 0053474..a01ac70 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -331,8 +331,8 @@
         );
         collectFlow(
                 mWindowRootView,
-                mCommunalInteractor.get().isCommunalShowing(),
-                this::onCommunalShowingChanged
+                mCommunalInteractor.get().isCommunalVisible(),
+                this::onCommunalVisibleChanged
         );
     }
 
@@ -475,6 +475,9 @@
             }
             visible = true;
             mLogger.d("Visibility forced to be true");
+        } else if (state.communalVisible) {
+            visible = true;
+            mLogger.d("Visibility forced to be true by communal");
         }
         if (mWindowRootView != null) {
             if (visible) {
@@ -510,15 +513,15 @@
     }
 
     private void applyUserActivityTimeout(NotificationShadeWindowState state) {
-        final Boolean communalShowing = state.isCommunalShowingAndNotOccluded();
+        final Boolean communalVisible = state.isCommunalVisibleAndNotOccluded();
         final Boolean keyguardShowing = state.isKeyguardShowingAndNotOccluded();
         long timeout = -1;
-        if ((communalShowing || keyguardShowing)
+        if ((communalVisible || keyguardShowing)
                 && state.statusBarState == StatusBarState.KEYGUARD
                 && !state.qsExpanded) {
             if (state.bouncerShowing) {
                 timeout = KeyguardViewMediator.AWAKE_INTERVAL_BOUNCER_MS;
-            } else if (communalShowing) {
+            } else if (communalVisible) {
                 timeout = CommunalInteractor.AWAKE_INTERVAL_MS;
             } else if (keyguardShowing) {
                 timeout = mLockScreenDisplayTimeout;
@@ -624,7 +627,7 @@
                 state.dozing,
                 state.scrimsVisibility,
                 state.backgroundBlurRadius,
-                state.communalShowing
+                state.communalVisible
         );
     }
 
@@ -749,8 +752,8 @@
     }
 
     @VisibleForTesting
-    void onCommunalShowingChanged(Boolean showing) {
-        mCurrentState.communalShowing = showing;
+    void onCommunalVisibleChanged(Boolean visible) {
+        mCurrentState.communalVisible = visible;
         apply(mCurrentState);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index f9c9d83..e0a98b3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -58,15 +58,15 @@
     @JvmField var dreaming: Boolean = false,
     @JvmField var scrimsVisibility: Int = 0,
     @JvmField var backgroundBlurRadius: Int = 0,
-    @JvmField var communalShowing: Boolean = false,
+    @JvmField var communalVisible: Boolean = false,
 ) {
 
     fun isKeyguardShowingAndNotOccluded(): Boolean {
         return keyguardShowing && !keyguardOccluded
     }
 
-    fun isCommunalShowingAndNotOccluded(): Boolean {
-        return communalShowing && !keyguardOccluded
+    fun isCommunalVisibleAndNotOccluded(): Boolean {
+        return communalVisible && !keyguardOccluded
     }
 
     /** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
@@ -99,7 +99,7 @@
             dozing.toString(),
             scrimsVisibility.toString(),
             backgroundBlurRadius.toString(),
-            communalShowing.toString(),
+            communalVisible.toString(),
         )
     }
 
@@ -140,7 +140,7 @@
             dozing: Boolean,
             scrimsVisibility: Int,
             backgroundBlurRadius: Int,
-            communalShowing: Boolean,
+            communalVisible: Boolean,
         ) {
             buffer.advance().apply {
                 this.keyguardShowing = keyguardShowing
@@ -172,7 +172,7 @@
                 this.dozing = dozing
                 this.scrimsVisibility = scrimsVisibility
                 this.backgroundBlurRadius = backgroundBlurRadius
-                this.communalShowing = communalShowing
+                this.communalVisible = communalVisible
             }
         }
 
@@ -218,7 +218,7 @@
                 "dozing",
                 "scrimsVisibility",
                 "backgroundBlurRadius",
-                "communalShowing"
+                "communalVisible"
             )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 84cad1d..c0afa32 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -30,8 +30,6 @@
 import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.fragments.FragmentService
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -59,18 +57,17 @@
 
 @SysUISingleton
 class NotificationsQSContainerController @Inject constructor(
-        view: NotificationsQuickSettingsContainer,
-        private val navigationModeController: NavigationModeController,
-        private val overviewProxyService: OverviewProxyService,
-        private val shadeHeaderController: ShadeHeaderController,
-        private val shadeInteractor: ShadeInteractor,
-        private val fragmentService: FragmentService,
-        @Main private val delayableExecutor: DelayableExecutor,
-        private val featureFlags: FeatureFlags,
-        private val
-            notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
-        private val splitShadeStateController: SplitShadeStateController,
-        private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
+    view: NotificationsQuickSettingsContainer,
+    private val navigationModeController: NavigationModeController,
+    private val overviewProxyService: OverviewProxyService,
+    private val shadeHeaderController: ShadeHeaderController,
+    private val shadeInteractor: ShadeInteractor,
+    private val fragmentService: FragmentService,
+    @Main private val delayableExecutor: DelayableExecutor,
+    private val
+    notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
+    private val splitShadeStateController: SplitShadeStateController,
+    private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
     private var splitShadeEnabled = false
@@ -133,9 +130,6 @@
         isGestureNavigation = QuickStepContract.isGesturalMode(currentMode)
 
         mView.setStackScroller(notificationStackScrollLayoutController.getView())
-        if (featureFlags.isEnabled(Flags.QS_CONTAINER_GRAPH_OPTIMIZER)){
-            mView.enableGraphOptimization()
-        }
     }
 
     public override fun onViewAttached() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index de3d16a..25e558e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -32,10 +32,10 @@
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.constraintlayout.widget.ConstraintSet;
 
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
-import com.android.systemui.res.R;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
 import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 
 import java.util.ArrayList;
@@ -73,6 +73,7 @@
 
     public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setOptimizationLevel(getOptimizationLevel() | OPTIMIZATION_GRAPH);
     }
 
     @Override
@@ -180,10 +181,6 @@
         super.dispatchDraw(canvas);
     }
 
-    void enableGraphOptimization() {
-        setOptimizationLevel(getOptimizationLevel() | OPTIMIZATION_GRAPH);
-    }
-
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         return TouchLogger.logDispatchTouch("NotificationsQuickSettingsContainer", ev,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index a71cf95..e619806 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -25,9 +25,10 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
 import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
+import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
+import com.android.systemui.util.kotlin.combine
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -44,7 +45,7 @@
 @Inject
 constructor(
     @Application val scope: CoroutineScope,
-    deviceProvisioningRepository: DeviceProvisioningRepository,
+    deviceProvisioningInteractor: DeviceProvisioningInteractor,
     disableFlagsRepository: DisableFlagsRepository,
     dozeParams: DozeParameters,
     keyguardRepository: KeyguardRepository,
@@ -56,7 +57,7 @@
 ) : ShadeInteractor, BaseShadeInteractor by baseShadeInteractor {
     override val isShadeEnabled: StateFlow<Boolean> =
         combine(
-                deviceProvisioningRepository.isFactoryResetProtectionActive,
+                deviceProvisioningInteractor.isFactoryResetProtectionActive,
                 disableFlagsRepository.disableFlags,
             ) { isFrpActive, isDisabledByFlags ->
                 isDisabledByFlags.isShadeEnabled() && !isFrpActive
@@ -83,7 +84,7 @@
             powerInteractor.isAsleep,
             keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
             keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
-            deviceProvisioningRepository.isFactoryResetProtectionActive,
+            deviceProvisioningInteractor.isFactoryResetProtectionActive,
         ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
             when {
                 // Touches are disabled when Factory Reset Protection is active
@@ -103,7 +104,7 @@
             isShadeEnabled,
             keyguardRepository.isDozing,
             userSetupRepository.isUserSetUp,
-            deviceProvisioningRepository.isDeviceProvisioned,
+            deviceProvisioningInteractor.isDeviceProvisioned,
         ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
             isDeviceProvisioned &&
                 // Disallow QS during setup if it's a simple user switcher. (The user intends to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractor.kt
new file mode 100644
index 0000000..32cf86d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractor.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.policy.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Encapsulates device-provisioning related business logic. */
+@SysUISingleton
+class DeviceProvisioningInteractor
+@Inject
+constructor(
+    repository: DeviceProvisioningRepository,
+) {
+    /**
+     * Whether this device has been provisioned.
+     *
+     * @see android.provider.Settings.Global.DEVICE_PROVISIONED
+     */
+    val isDeviceProvisioned: Flow<Boolean> = repository.isDeviceProvisioned
+
+    /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */
+    val isFactoryResetProtectionActive: Flow<Boolean> = repository.isFactoryResetProtectionActive
+}
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 b3e386e..cc27cbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -189,7 +189,6 @@
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
@@ -411,7 +410,7 @@
 
         mShadeInteractor = new ShadeInteractorImpl(
                 mTestScope.getBackgroundScope(),
-                new FakeDeviceProvisioningRepository(),
+                mKosmos.getDeviceProvisioningInteractor(),
                 new FakeDisableFlagsRepository(),
                 mDozeParameters,
                 mFakeKeyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 461db8e..7f4508a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -99,7 +99,6 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
@@ -260,7 +259,7 @@
 
         mShadeInteractor = new ShadeInteractorImpl(
                 mTestScope.getBackgroundScope(),
-                new FakeDeviceProvisioningRepository(),
+                mKosmos.getDeviceProvisioningInteractor(),
                 new FakeDisableFlagsRepository(),
                 mock(DozeParameters.class),
                 keyguardRepository,
@@ -452,11 +451,11 @@
     }
 
     @Test
-    public void setCommunalShowing_userTimeout() {
+    public void setCommunalVisible_userTimeout() {
         setKeyguardShowing();
         clearInvocations(mWindowManager);
 
-        mNotificationShadeWindowController.onCommunalShowingChanged(true);
+        mNotificationShadeWindowController.onCommunalVisibleChanged(true);
         verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
         assertThat(mLayoutParameters.getValue().userActivityTimeout)
                 .isEqualTo(CommunalInteractor.AWAKE_INTERVAL_MS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 697b05a..c226121 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -28,8 +28,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.fragments.FragmentService
 import com.android.systemui.navigationbar.NavigationModeController
@@ -94,7 +92,6 @@
 
     lateinit var underTest: NotificationsQSContainerController
 
-    private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var navigationModeCallback: ModeChangedListener
     private lateinit var taskbarVisibilityCallback: OverviewProxyListener
     private lateinit var windowInsetsCallback: Consumer<WindowInsets>
@@ -106,7 +103,6 @@
         MockitoAnnotations.initMocks(this)
         fakeSystemClock = FakeSystemClock()
         delayableExecutor = FakeExecutor(fakeSystemClock)
-        featureFlags = FakeFeatureFlags().apply { set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, false) }
         mContext.ensureTestableResources()
         whenever(view.context).thenReturn(mContext)
         whenever(view.resources).thenReturn(mContext.resources)
@@ -123,7 +119,6 @@
                 shadeInteractor,
                 fragmentService,
                 delayableExecutor,
-                featureFlags,
                 notificationStackScrollLayoutController,
                 ResourcesSplitShadeStateController(),
                 largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
@@ -536,7 +531,6 @@
                 shadeInteractor,
                 fragmentService,
                 delayableExecutor,
-                featureFlags,
                 notificationStackScrollLayoutController,
                 ResourcesSplitShadeStateController(),
                 largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index e66251a..c326350 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -28,8 +28,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.fragments.FragmentService
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
@@ -91,7 +89,6 @@
 
     lateinit var underTest: NotificationsQSContainerController
 
-    private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var navigationModeCallback: ModeChangedListener
     private lateinit var taskbarVisibilityCallback: OverviewProxyListener
     private lateinit var windowInsetsCallback: Consumer<WindowInsets>
@@ -104,7 +101,6 @@
         fakeSystemClock = FakeSystemClock()
         delayableExecutor = FakeExecutor(fakeSystemClock)
         mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
-        featureFlags = FakeFeatureFlags().apply { set(Flags.QS_CONTAINER_GRAPH_OPTIMIZER, true) }
         mContext.ensureTestableResources()
         whenever(view.context).thenReturn(mContext)
         whenever(view.resources).thenReturn(mContext.resources)
@@ -122,7 +118,6 @@
                 shadeInteractor,
                 fragmentService,
                 delayableExecutor,
-                featureFlags,
                 notificationStackScrollLayoutController,
                 ResourcesSplitShadeStateController(),
                 largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
@@ -513,7 +508,6 @@
                 shadeInteractor,
                 fragmentService,
                 delayableExecutor,
-                featureFlags,
                 notificationStackScrollLayoutController,
                 ResourcesSplitShadeStateController(),
                 largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 3e0a647..7bd9d92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -103,7 +103,6 @@
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
@@ -205,9 +204,7 @@
         mStatusBarStateController = mKosmos.getStatusBarStateController();
         mInteractionJankMonitor = mKosmos.getInteractionJankMonitor();
 
-        FakeDeviceProvisioningRepository deviceProvisioningRepository =
-                new FakeDeviceProvisioningRepository();
-        deviceProvisioningRepository.setDeviceProvisioned(true);
+        mKosmos.getFakeDeviceProvisioningRepository().setDeviceProvisioned(true);
         FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
         FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
 
@@ -294,7 +291,7 @@
 
         mShadeInteractor = new ShadeInteractorImpl(
                 mTestScope.getBackgroundScope(),
-                deviceProvisioningRepository,
+                mKosmos.getDeviceProvisioningInteractor(),
                 mDisableFlagsRepository,
                 mDozeParameters,
                 mKeyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 8cb064d..5450537 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -56,8 +56,8 @@
 import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.flow.emptyFlow
@@ -192,7 +192,7 @@
         shadeInteractor =
             ShadeInteractorImpl(
                 testScope.backgroundScope,
-                FakeDeviceProvisioningRepository(),
+                kosmos.deviceProvisioningInteractor,
                 FakeDisableFlagsRepository(),
                 mock(),
                 keyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 925ac2a..9ea4142 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -494,7 +494,7 @@
         mShadeInteractor =
                 new ShadeInteractorImpl(
                         mTestScope.getBackgroundScope(),
-                        deviceProvisioningRepository,
+                        mKosmos.getDeviceProvisioningInteractor(),
                         new FakeDisableFlagsRepository(),
                         mDozeParameters,
                         keyguardRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 083de10..b9a3d38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -43,6 +43,8 @@
 import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.statusbar.phone.screenOffAnimationController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.util.time.systemClock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
@@ -80,6 +82,8 @@
     val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
     val communalInteractor by lazy { kosmos.communalInteractor }
     val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin }
+    val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor }
+    val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
 
     init {
         kosmos.applicationContext = testCase.context
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/fontscaling/FontScalingTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/fontscaling/FontScalingTileKosmos.kt
new file mode 100644
index 0000000..9410ce6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/fontscaling/FontScalingTileKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.tiles.impl.fontscaling
+
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsFontScalingTileConfig by
+    Kosmos.Fixture { QSAccessibilityModule.provideFontScalingTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index afd37b3..2bd76be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -29,8 +29,8 @@
 import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.statusbar.phone.dozeParameters
-import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository
 import com.android.systemui.statusbar.policy.data.repository.userSetupRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.user.domain.interactor.userSwitcherInteractor
 
 var Kosmos.baseShadeInteractor: BaseShadeInteractor by
@@ -63,7 +63,7 @@
     Kosmos.Fixture {
         ShadeInteractorImpl(
             scope = applicationCoroutineScope,
-            deviceProvisioningRepository = deviceProvisioningRepository,
+            deviceProvisioningInteractor = deviceProvisioningInteractor,
             disableFlagsRepository = disableFlagsRepository,
             dozeParams = dozeParameters,
             keyguardRepository = fakeKeyguardRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractorKosmos.kt
new file mode 100644
index 0000000..84bd3e8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository
+
+val Kosmos.deviceProvisioningInteractor by Fixture {
+    DeviceProvisioningInteractor(
+        repository = deviceProvisioningRepository,
+    )
+}
diff --git a/ravenwood/README-ravenwood+mockito.md b/ravenwood/README-ravenwood+mockito.md
deleted file mode 100644
index 6adb6144..0000000
--- a/ravenwood/README-ravenwood+mockito.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Ravenwood and Mockito
-
-Last update: 2023-11-13
-
-- As of 2023-11-13, `external/mockito` is based on version 2.x.
-- Mockito didn't support static mocking before 3.4.0.
-  See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48
-
-- Latest Mockito is 5.*. According to https://github.com/mockito/mockito:
-  `Mockito 3 does not introduce any breaking API changes, but now requires Java 8 over Java 6 for Mockito 2. Mockito 4 removes deprecated API. Mockito 5 switches the default mockmaker to mockito-inline, and now requires Java 11.`
-
-- Mockito now supports Android natively.
-  See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.1
-  - But it's unclear at this point to omakoto@ how the `mockito-android` module is built.
-
-- Potential plan:
-  - Ideal option:
-    - If we can update `external/mockito`, that'd be great, but it may not work because
-      Mockito has removed the deprecated APIs.
-  - Second option:
-    - Import the latest mockito as `external/mockito-new`, and require ravenwood
-      to use this one.
-    - The latest mockito needs be exposed to all of 1) device tests, 2) host tests, and 3) ravenwood tests.
-    - This probably will require the latest `bytebuddy` and `objenesis`.
\ No newline at end of file
diff --git a/ravenwood/list-ravenwood-tests.sh b/ravenwood/list-ravenwood-tests.sh
new file mode 100755
index 0000000..fb9b823
--- /dev/null
+++ b/ravenwood/list-ravenwood-tests.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# 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.
+
+# List all the ravenwood test modules.
+
+jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json"
diff --git a/ravenwood/mockito/Android.bp b/ravenwood/mockito/Android.bp
index 4135022..a74bca4 100644
--- a/ravenwood/mockito/Android.bp
+++ b/ravenwood/mockito/Android.bp
@@ -7,16 +7,6 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-// Ravenwood tests run on the hostside, so we need mockit of the host variant.
-// But we need to use it in modules of the android variant, so we "wash" the variant with it.
-java_host_for_device {
-    name: "mockito_ravenwood",
-    libs: [
-        "mockito",
-        "objenesis",
-    ],
-}
-
 android_ravenwood_test {
     name: "RavenwoodMockitoTest",
 
@@ -26,8 +16,6 @@
     static_libs: [
         "junit",
         "truth",
-
-        "mockito_ravenwood",
     ],
     libs: [
         "android.test.mock",
diff --git a/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
index 364a86a..1284d64 100644
--- a/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
+++ b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
@@ -22,6 +22,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.Parcel;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import org.junit.Rule;
@@ -78,4 +79,13 @@
 
         assertThat(object.getPackageName()).isEqualTo("android");
     }
+
+    @Test
+    public void testMockFinalClass() {
+        var object = mock(Parcel.class);
+
+        when(object.readInt()).thenReturn(123);
+
+        assertThat(object.readInt()).isEqualTo(123);
+    }
 }
diff --git a/ravenwood/run-ravenwood-tests.sh b/ravenwood/run-ravenwood-tests.sh
new file mode 100755
index 0000000..3f4b8a7
--- /dev/null
+++ b/ravenwood/run-ravenwood-tests.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# 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.
+
+# Run all the ravenwood tests.
+
+# "echo" is to remove the newlines
+all_tests=$(echo $(${0%/*}/list-ravenwood-tests.sh) )
+
+echo "Running tests: $all_tests"
+atest $all_tests
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index f031b7b..0f2af31 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3659,6 +3659,15 @@
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
         }
+        // The user represented by userId, must be running.
+        if (!mUserManagerInternal.isUserRunning(userId)) {
+            // There is a chance that we hit here because of race condition. Let's just
+            // return an error code instead of crashing the caller process, which at
+            // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
+            // important process.
+            Slog.w(TAG, "User #" + userId + " is not running.");
+            return InputBindResult.INVALID_USER;
+        }
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                     "IMMS.startInputOrWindowGainedFocus");
@@ -3666,20 +3675,43 @@
                     "InputMethodManagerService#startInputOrWindowGainedFocus");
             final InputBindResult result;
             synchronized (ImfLock.class) {
+                // If the system is not yet ready, we shouldn't be running third party code.
                 if (!mSystemReady) {
-                    // If the system is not yet ready, we shouldn't be running third arty code.
                     return new InputBindResult(
                             InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
                             null /* method */, null /* accessibilitySessions */, null /* channel */,
                             getSelectedMethodIdLocked(), getSequenceNumberLocked(),
                             false /* isInputMethodSuppressingSpellChecker */);
                 }
+                final ClientState cs = mClientController.getClient(client.asBinder());
+                if (cs == null) {
+                    throw new IllegalArgumentException("Unknown client " + client.asBinder());
+                }
                 final long ident = Binder.clearCallingIdentity();
                 try {
+                    // Verify if IMMS is in the process of switching user.
+                    if (mUserSwitchHandlerTask != null) {
+                        // There is already an on-going pending user switch task.
+                        final int nextUserId = mUserSwitchHandlerTask.mToUserId;
+                        if (userId == nextUserId) {
+                            scheduleSwitchUserTaskLocked(userId, cs.mClient);
+                            return InputBindResult.USER_SWITCHING;
+                        }
+                        final int[] profileIdsWithDisabled = mUserManagerInternal.getProfileIds(
+                                mSettings.getUserId(), false /* enabledOnly */);
+                        for (int profileId : profileIdsWithDisabled) {
+                            if (profileId == userId) {
+                                scheduleSwitchUserTaskLocked(userId, cs.mClient);
+                                return InputBindResult.USER_SWITCHING;
+                            }
+                        }
+                        return InputBindResult.INVALID_USER;
+                    }
+
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
                             client, windowToken, startInputFlags, softInputMode, windowFlags,
                             editorInfo, inputConnection, remoteAccessibilityInputConnection,
-                            unverifiedTargetSdkVersion, userId, imeDispatcher);
+                            unverifiedTargetSdkVersion, userId, imeDispatcher, cs);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -3708,7 +3740,7 @@
             IRemoteInputConnection inputContext,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, @NonNull ClientState cs) {
         if (DEBUG) {
             Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
                     + InputMethodDebug.startInputReasonToString(startInputReason)
@@ -3721,23 +3753,9 @@
                     + " windowFlags=#" + Integer.toHexString(windowFlags)
                     + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
                     + " userId=" + userId
-                    + " imeDispatcher=" + imeDispatcher);
+                    + " imeDispatcher=" + imeDispatcher
+                    + " cs=" + cs);
         }
-
-        if (!mUserManagerInternal.isUserRunning(userId)) {
-            // There is a chance that we hit here because of race condition. Let's just
-            // return an error code instead of crashing the caller process, which at
-            // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
-            // important process.
-            Slog.w(TAG, "User #" + userId + " is not running.");
-            return InputBindResult.INVALID_USER;
-        }
-
-        final ClientState cs = mClientController.getClient(client.asBinder());
-        if (cs == null) {
-            throw new IllegalArgumentException("Unknown client " + client.asBinder());
-        }
-
         final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
                 windowToken, cs.mUid, cs.mPid, cs.mSelfReportedDisplayId);
         switch (imeClientFocus) {
@@ -3759,24 +3777,6 @@
                 return InputBindResult.INVALID_DISPLAY_ID;
         }
 
-        if (mUserSwitchHandlerTask != null) {
-            // There is already an on-going pending user switch task.
-            final int nextUserId = mUserSwitchHandlerTask.mToUserId;
-            if (userId == nextUserId) {
-                scheduleSwitchUserTaskLocked(userId, cs.mClient);
-                return InputBindResult.USER_SWITCHING;
-            }
-            final int[] profileIdsWithDisabled = mUserManagerInternal.getProfileIds(
-                    mSettings.getUserId(), false /* enabledOnly */);
-            for (int profileId : profileIdsWithDisabled) {
-                if (profileId == userId) {
-                    scheduleSwitchUserTaskLocked(userId, cs.mClient);
-                    return InputBindResult.USER_SWITCHING;
-                }
-            }
-            return InputBindResult.INVALID_USER;
-        }
-
         final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.mUid);
         // In case mShowForced flag affects the next client to keep IME visible, when the current
         // client is leaving due to the next focused client, we clear mShowForced flag when the
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 474b590..339b1e7 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -19,7 +19,6 @@
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
 import static android.app.ActivityManager.START_PERMISSION_DENIED;
-import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
@@ -286,10 +285,16 @@
             Slog.e(TAG, TextUtils.formatSimple(
                     "Unexpected error occurred while unarchiving package %s: %s.", packageName,
                     t.getLocalizedMessage()));
-            return START_ABORTED;
         }
 
-        return START_SUCCESS;
+        // We return STATUS_ABORTED because:
+        // 1. Archived App is not actually present during activity start. Hence the unarchival
+        // start should be treated as an error code.
+        // 2. STATUS_ABORTED is not visible to the end consumers. Hence, it will not change user
+        // experience.
+        // 3. Returning STATUS_ABORTED helps us avoid manually handling of different cases like
+        // aborting activity options, animations etc in the Windows Manager.
+        return START_ABORTED;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 2e0546e..1128d0c 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1319,9 +1319,33 @@
         try {
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
-                if (r != null) {
+                if (r == null) return;
+                final TransitionController controller = r.mTransitionController;
+                if (!controller.isShellTransitionsEnabled()) {
                     r.setShowWhenLocked(showWhenLocked);
+                    return;
                 }
+                if (controller.isCollecting()
+                        && !mService.mKeyguardController.isKeyguardLocked(r.getDisplayId())) {
+                    // Keyguard isn't locked, so this can be done as part of the collecting
+                    // transition.
+                    r.setShowWhenLocked(showWhenLocked);
+                    return;
+                }
+                final Transition transition = new Transition(
+                        showWhenLocked ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
+                        0 /* flags */, controller, mService.mWindowManager.mSyncEngine);
+                r.mTransitionController.startCollectOrQueue(transition, (deferred) -> {
+                    transition.collect(r);
+                    r.setShowWhenLocked(showWhenLocked);
+                    if (transition.isNoOp()) {
+                        transition.abort();
+                        return;
+                    }
+                    controller.requestStartTransition(transition, null /* trigger */,
+                            null /* remoteTransition */, null /* displayChange */);
+                    transition.setReady(r, true);
+                });
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -1334,9 +1358,34 @@
         try {
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
-                if (r != null) {
+                if (r == null) return;
+                final TransitionController controller = r.mTransitionController;
+                // If shell transitions is not enabled just set it directly.
+                if (!controller.isShellTransitionsEnabled()) {
                     r.setInheritShowWhenLocked(inheritShowWhenLocked);
+                    return;
                 }
+                if (controller.isCollecting()
+                        && !mService.mKeyguardController.isKeyguardLocked(r.getDisplayId())) {
+                    // Keyguard isn't locked, so this can be done as part of the collecting
+                    // transition.
+                    r.setInheritShowWhenLocked(inheritShowWhenLocked);
+                    return;
+                }
+                final Transition transition = new Transition(
+                        inheritShowWhenLocked ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
+                        0 /* flags */, controller, mService.mWindowManager.mSyncEngine);
+                r.mTransitionController.startCollectOrQueue(transition, (deferred) -> {
+                    transition.collect(r);
+                    r.setInheritShowWhenLocked(inheritShowWhenLocked);
+                    if (transition.isNoOp()) {
+                        transition.abort();
+                        return;
+                    }
+                    controller.requestStartTransition(transition, null /* trigger */,
+                            null /* remoteTransition */, null /* displayChange */);
+                    transition.setReady(r, true);
+                });
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d6f52b8..85580ac 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1034,20 +1034,19 @@
         }
 
         if (err == ActivityManager.START_SUCCESS && aInfo == null) {
+            // We couldn't find the specific class specified in the Intent.
+            err = ActivityManager.START_CLASS_NOT_FOUND;
+
             if (isArchivingEnabled()) {
                 PackageArchiver packageArchiver = mService
                         .getPackageManagerInternalLocked()
                         .getPackageArchiver();
                 if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
-                    return packageArchiver
+                    err = packageArchiver
                             .requestUnarchiveOnActivityStart(
                                     intent, callingPackage, mRequest.userId, realCallingUid);
                 }
             }
-
-            // We couldn't find the specific class specified in the Intent.
-            // Also the end of the line.
-            err = ActivityManager.START_CLASS_NOT_FOUND;
         }
 
         if (err == ActivityManager.START_SUCCESS && sourceRecord != null
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 2accf9a..1e58306 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2917,6 +2917,26 @@
         Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_WINDOW_MANAGER, TAG, cookie);
     }
 
+    /**
+     * Get whether the transition, in its current state, is a no-op. This should be avoided. It is
+     * only here for legacy usages where we can't tell ahead-of-time whether something will
+     * generate a change.
+     */
+    boolean isNoOp() {
+        for (int i = mParticipants.size() - 1; i >= 0; --i) {
+            // This is the same criteria as the rejection logic in calculateTargets
+            final WindowContainer<?> wc = mParticipants.valueAt(i);
+            if (!wc.isAttached()) continue;
+            // The level of transition target should be at least window token.
+            if (wc.asWindowState() != null) continue;
+            final ChangeInfo changeInfo = mChanges.get(wc);
+            // Reject no-ops
+            if (!changeInfo.hasChanged()) continue;
+            return false;
+        }
+        return true;
+    }
+
     @VisibleForTesting
     static class ChangeInfo {
         private static final int FLAG_NONE = 0;
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 56423b9..897afbf 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -73,7 +73,6 @@
         "androidx.annotation_annotation",
         "androidx.test.rules",
         "framework",
-        "mockito_ravenwood",
         "ravenwood-runtime",
         "ravenwood-utils",
         "services",
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index f49f638..64fef68 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -81,7 +81,6 @@
         "androidx.annotation_annotation",
         "androidx.test.rules",
         "truth",
-        "mockito_ravenwood",
     ],
     srcs: [
         ":power_stats_ravenwood_tests",
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 5611415..ad6e2c6 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -153,7 +153,6 @@
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.test.rules",
-        "mockito_ravenwood",
         "services.core",
     ],
     srcs: [
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 7ccc27e..0c324e6 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -649,12 +649,19 @@
     }
 
     /**
-     * @return Reason for denial if the registration state is {@link #REGISTRATION_STATE_DENIED}.
-     * Depending on {@code accessNetworkTechnology}, the values are defined in 3GPP TS 24.008
-     * 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 A.S0001 6.2.2.44 for CDMA
-     * @hide
+     * Get the 3GPP/3GPP2 reason code indicating why registration failed.
+     *
+     * Returns the reason code for non-transient registration failures. Typically this method will
+     * only return the last reason code received during a network selection procedure. The reason
+     * code is system-specific; however, the reason codes for both 3GPP and 3GPP2 systems are
+     * largely equivalent across generations.
+     *
+     * @return registration reject cause if available, otherwise 0. Depending on
+     * {@link #getAccessNetworkTechnology}, the values are defined in 3GPP TS 24.008 10.5.3.6 for
+     * WCDMA/UMTS, 3GPP TS 24.301 9.9.3.9 for LTE/EPS, 3GPP 24.501 Annex A for NR/5GS, or 3GPP2
+     * A.S0001 6.2.2.44 for CDMA.
      */
-    @SystemApi
+    @FlaggedApi(Flags.FLAG_NETWORK_REGISTRATION_INFO_REJECT_CAUSE)
     public int getRejectCause() {
         return mRejectCause;
     }