Merge "Add flags for com.android.internal.widget" into main
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 58763a7..d9ff190 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -165,6 +165,7 @@
     ],
     host_supported: true,
     test_suites: ["general-tests"],
+    require_root: true,
     srcs: [
         "tests/BinaryStreamVisitorTests.cpp",
         "tests/CommandLineOptionsTests.cpp",
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
index 57ae354..f22a481 100644
--- a/cmds/idmap2/libidmap2/ResourceContainer.cpp
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -22,6 +22,7 @@
 #include <utility>
 #include <vector>
 
+#include "android-base/scopeguard.h"
 #include "androidfw/ApkAssets.h"
 #include "androidfw/AssetManager.h"
 #include "androidfw/Util.h"
@@ -269,27 +270,40 @@
   std::unique_ptr<AssetManager2> am;
   ZipAssetsProvider* zip_assets;
 
-  static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip,
+  static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider>&& zip,
                                      package_property_t flags) {
     ResState state;
     state.zip_assets = zip.get();
     if ((state.apk_assets = ApkAssets::Load(std::move(zip), flags)) == nullptr) {
-      return Error("failed to load apk asset");
+      return Error("failed to load apk asset for '%s'",
+                   state.zip_assets->GetDebugName().c_str());
     }
 
+    // Make sure we put ZipAssetsProvider where we took it if initialization fails, so the
+    // original object stays valid for any next call it may get.
+    auto scoped_restore_zip_assets = android::base::ScopeGuard([&zip, &state]() {
+      zip = std::unique_ptr<ZipAssetsProvider>(
+          static_cast<ZipAssetsProvider*>(
+              std::move(const_cast<ApkAssets&>(*state.apk_assets)).TakeAssetsProvider().release()));
+    });
+
     if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) {
-      return Error("failed to retrieve loaded arsc");
+      return Error("failed to retrieve loaded arsc for '%s'",
+                   state.zip_assets->GetDebugName().c_str());
     }
 
     if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) {
-      return Error("failed to retrieve loaded package at index 0");
+      return Error("failed to retrieve loaded package at index 0 for '%s'",
+                   state.zip_assets->GetDebugName().c_str());
     }
 
     state.am = std::make_unique<AssetManager2>();
     if (!state.am->SetApkAssets({state.apk_assets}, false)) {
-      return Error("failed to create asset manager");
+      return Error("failed to create asset manager for '%s'",
+                   state.zip_assets->GetDebugName().c_str());
     }
 
+    scoped_restore_zip_assets.Disable();
     return state;
   }
 };
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 1b656e8..7093614 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -214,6 +214,20 @@
   ASSERT_EQ(idmap->GetHeader()->GetOverlayName(), TestConstants::OVERLAY_NAME_ALL_POLICIES);
 }
 
+TEST(IdmapTests, TargetContainerWorksAfterError) {
+  auto target = TargetResourceContainer::FromPath(GetTestDataPath() + "/target/target-bad.apk");
+  ASSERT_TRUE(target);
+
+  auto crc = target->get()->GetCrc();
+  ASSERT_TRUE(crc);
+
+  // This call tries to construct the full ApkAssets state, and fails.
+  ASSERT_FALSE(target->get()->DefinesOverlayable());
+  auto crc2 = target->get()->GetCrc();
+  ASSERT_TRUE(crc2);
+  EXPECT_EQ(*crc, *crc2);
+}
+
 TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
   std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
   std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
diff --git a/cmds/idmap2/tests/data/target/target-bad.apk b/cmds/idmap2/tests/data/target/target-bad.apk
new file mode 100644
index 0000000..fd86782
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/target-bad.apk
Binary files differ
diff --git a/core/api/current.txt b/core/api/current.txt
index f3d19ca..ac6e293 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40754,8 +40754,10 @@
     method @NonNull public java.util.List<java.security.cert.X509Certificate> getGrantedCertificateChainFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
     method @NonNull public java.security.Key getGrantedKeyFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
     method @NonNull public java.security.KeyPair getGrantedKeyPairFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
+    method @FlaggedApi("android.security.keystore2.attest_modules") @NonNull public byte[] getSupplementaryAttestationInfo(int) throws android.security.KeyStoreException;
     method public long grantKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
     method public void revokeKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
+    field public static final int MODULE_HASH = -1879047468; // 0x900002d4
   }
 
   public class SecureKeyImportUnavailableException extends java.security.ProviderException {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d1de19e..a593970 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5264,9 +5264,9 @@
   }
 
   @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable {
-    method public void close();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void close();
     method @Nullable public android.hardware.contexthub.HubServiceInfo getServiceInfo();
-    method @NonNull public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
   }
 
   @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSessionResult {
diff --git a/core/java/android/app/admin/UnknownAuthority.java b/core/java/android/app/admin/UnknownAuthority.java
index 82dcf7e..bebffde 100644
--- a/core/java/android/app/admin/UnknownAuthority.java
+++ b/core/java/android/app/admin/UnknownAuthority.java
@@ -74,14 +74,14 @@
     @Override
     public boolean equals(@Nullable Object o) {
         if (this == o) return true;
-        if (o != null && getClass() == o.getClass()) return false;
+        if (o == null || getClass() != o.getClass()) return false;
         UnknownAuthority other = (UnknownAuthority) o;
         return Objects.equals(mName, other.mName);
     }
 
     @Override
     public int hashCode() {
-        return mName.hashCode();
+        return Objects.hashCode(mName);
     }
 
     @Override
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index cf952cb..b3d65c1 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -19,6 +19,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.chre.flags.Flags;
 import android.hardware.location.ContextHubTransaction;
@@ -70,6 +71,7 @@
      *     receiving the response for the message.
      */
     @NonNull
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public ContextHubTransaction<Void> sendMessage(@NonNull HubMessage message) {
         if (mIsClosed.get()) {
             throw new IllegalStateException("Session is already closed.");
@@ -120,6 +122,7 @@
      * <p>When this function is invoked, the messaging associated with this session is invalidated.
      * All futures messages targeted for this client are dropped.
      */
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public void close() {
         if (!mIsClosed.getAndSet(true)) {
             mCloseGuard.close();
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index d45d5c4..5053aa8 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -467,4 +467,11 @@
     namespace: "lse_desktop_experience"
     description: "Enable drag-and-drop between connected displays."
     bug: "381793841"
+}
+
+flag {
+    name: "enable_connected_displays_window_drag"
+    namespace: "lse_desktop_experience"
+    description: "Enable window drag between connected displays."
+    bug: "381172172"
 }
\ No newline at end of file
diff --git a/keystore/java/Android.bp b/keystore/java/Android.bp
index 21edff1..264ac5ff 100644
--- a/keystore/java/Android.bp
+++ b/keystore/java/Android.bp
@@ -13,5 +13,13 @@
         "**/*.java",
         "**/*.aidl",
     ],
+    exclude_srcs: select(release_flag("RELEASE_ATTEST_MODULES"), {
+        true: [
+            "android/security/KeyStore2HalCurrent.java",
+        ],
+        default: [
+            "android/security/KeyStore2HalLatest.java",
+        ],
+    }),
     visibility: ["//frameworks/base"],
 }
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index dd703f5..f5cf571 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -101,7 +101,7 @@
         R execute(IKeystoreService service) throws RemoteException;
     }
 
-    private <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
+    <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
             throws KeyStoreException {
         IKeystoreService service = getService(false /* retryLookup */);
         boolean firstTry = true;
@@ -369,6 +369,18 @@
         }
     }
 
+    /**
+     * Returns tag-specific info required to interpret a tag's attested value.
+     * @see IKeystoreService#getSupplementaryAttestationInfo(Tag) for more details.
+     * @param tag
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public byte[] getSupplementaryAttestationInfo(int tag) throws KeyStoreException {
+        return KeyStore2HalVersion.getSupplementaryAttestationInfoHelper(tag, this);
+    }
+
     static KeyStoreException getKeyStoreException(int errorCode, String serviceErrorMessage) {
         if (errorCode > 0) {
             // KeyStore layer error
diff --git a/keystore/java/android/security/KeyStore2HalCurrent.java b/keystore/java/android/security/KeyStore2HalCurrent.java
new file mode 100644
index 0000000..f4d8fe6
--- /dev/null
+++ b/keystore/java/android/security/KeyStore2HalCurrent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * @hide This class is necessary to allow the version of the AIDL interface for Keystore and
+* KeyMint used in KeyStore2.java to differ by BUILD flag `RELEASE_ATTEST_MODULES`. When
+* `RELEASE_ATTEST_MODULES` is not set, this file is included, and the current HALs for Keystore
+* (V4) and KeyMint (V3) are used.
+*/
+class KeyStore2HalVersion {
+    public static byte[] getSupplementaryAttestationInfoHelper(int tag, KeyStore2 ks)
+            throws KeyStoreException {
+        return new byte[0];
+    }
+}
diff --git a/keystore/java/android/security/KeyStore2HalLatest.java b/keystore/java/android/security/KeyStore2HalLatest.java
new file mode 100644
index 0000000..123f1c0
--- /dev/null
+++ b/keystore/java/android/security/KeyStore2HalLatest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * @hide This class is necessary to allow the version of the AIDL interface for Keystore and
+* KeyMint used in KeyStore2.java to differ by BUILD flag `RELEASE_ATTEST_MODULES`. When
+* `RELEASE_ATTEST_MODULES` is set, this file is included, and the latest HALs for Keystore (V5)
+* and KeyMint (V4) are used.
+*/
+class KeyStore2HalVersion {
+    public static byte[] getSupplementaryAttestationInfoHelper(int tag, KeyStore2 ks)
+            throws KeyStoreException {
+        return ks.handleRemoteExceptionWithRetry(
+            (service) -> service.getSupplementaryAttestationInfo(tag));
+    }
+}
diff --git a/keystore/java/android/security/keystore/KeyStoreManager.java b/keystore/java/android/security/keystore/KeyStoreManager.java
index e6091c1..740ccb5 100644
--- a/keystore/java/android/security/keystore/KeyStoreManager.java
+++ b/keystore/java/android/security/keystore/KeyStoreManager.java
@@ -17,9 +17,11 @@
 package android.security.keystore;
 
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.hardware.security.keymint.TagType;
 import android.security.KeyStore2;
 import android.security.KeyStoreException;
 import android.security.keystore2.AndroidKeyStoreProvider;
@@ -32,6 +34,8 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.ByteArrayInputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.Key;
 import java.security.KeyPair;
 import java.security.PublicKey;
@@ -299,6 +303,37 @@
         return Collections.emptyList();
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {MODULE_HASH})
+    public @interface SupplementaryAttestationInfoTagEnum {}
+
+    /**
+     * When passed into getSupplementaryAttestationInfo, getSupplementaryAttestationInfo returns the
+     * DER-encoded structure corresponding to the `Modules` schema described in the KeyMint HAL's
+     * KeyCreationResult.aidl. The SHA-256 hash of this encoded structure is what's included with
+     * the tag in attestations.
+     */
+    // TODO(b/369375199): Replace with Tag.MODULE_HASH when flagging is removed.
+    public static final int MODULE_HASH = TagType.BYTES | 724;
+
+    /**
+     * Returns tag-specific data required to interpret a tag's attested value.
+     *
+     * When performing key attestation, the obtained attestation certificate contains a list of tags
+     * and their corresponding attested values. For some tags, additional information about the
+     * attested value can be queried via this API. See individual tags for specifics.
+     *
+     * @param tag tag for which info is being requested
+     * @return tag-specific info
+     * @throws KeyStoreException if the requested info is not available
+     */
+    @FlaggedApi(android.security.keystore2.Flags.FLAG_ATTEST_MODULES)
+    public @NonNull byte[] getSupplementaryAttestationInfo(
+            @SupplementaryAttestationInfoTagEnum int tag) throws KeyStoreException {
+        return mKeyStore2.getSupplementaryAttestationInfo(tag);
+    }
+
     /**
      * Returns a new {@link KeyDescriptor} instance in the app domain / namespace with the {@code
      * alias} set to the provided value.
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index f6d2cc0..f4f60d7 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -272,9 +272,11 @@
                         TaggedCujTransitionMatcher(associatedTransitionRequired = false)
                     )
                     .build(),
-                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
-                        listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP))
-                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+                    AppWindowCoversLeftHalfScreenAtEnd(
+                        DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+                    )
+                ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
             )
 
         val SNAP_RESIZE_RIGHT_WITH_DRAG =
@@ -287,9 +289,11 @@
                         TaggedCujTransitionMatcher(associatedTransitionRequired = false)
                     )
                     .build(),
-                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
-                        listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP))
-                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+                    AppWindowCoversRightHalfScreenAtEnd(
+                        DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+                    )
+                ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
             )
 
         val SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE =
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 2ccffa85..a3d6020 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -66,5 +66,6 @@
     fun teardown() {
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
+        Utils.resetFreezeRecentTaskList()
     }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
index 8673c46..9c7de05 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
@@ -65,5 +65,6 @@
     fun teardown() {
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
+        Utils.resetFreezeRecentTaskList()
     }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
index 22adf6c..9eb2972 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
@@ -68,5 +68,6 @@
     fun teardown() {
         primaryApp.exit(wmHelper)
         secondaryApp.exit(wmHelper)
+        Utils.resetFreezeRecentTaskList()
     }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
index 4ded148..d833d91 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
@@ -68,5 +68,6 @@
         secondaryApp.exit(wmHelper)
         thirdApp.exit(wmHelper)
         fourthApp.exit(wmHelper)
+        Utils.resetFreezeRecentTaskList()
     }
 }
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
index c0fafef..4a9e73b 100644
--- a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
@@ -28,7 +28,10 @@
 import android.tools.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.flicker.rules.LaunchAppRule
 import android.tools.flicker.rules.RemoveAllTasksButHomeRule
+import android.util.Log
 import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import java.io.IOException
 import org.junit.rules.RuleChain
 
 object Utils {
@@ -52,4 +55,17 @@
             .around(PressHomeRule())
             .around(EnsureDeviceSettingsRule())
     }
+
+    /**
+     * Resets the frozen recent tasks list (ie. commits the quickswitch to the current task and
+     * reorders the current task to the end of the recents list).
+     */
+    fun resetFreezeRecentTaskList() {
+        try {
+            UiDevice.getInstance(instrumentation)
+                .executeShellCommand("wm reset-freeze-recent-tasks")
+        } catch (e: IOException) {
+            Log.e("TestUtils", "Failed to reset frozen recent tasks list", e)
+        }
+    }
 }
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 49254d1..dbb8914 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -40,20 +40,21 @@
 }
 
 ApkAssetsPtr ApkAssets::Load(const std::string& path, package_property_t flags) {
-  return Load(ZipAssetsProvider::Create(path, flags), flags);
+  return LoadImpl(ZipAssetsProvider::Create(path, flags), flags);
 }
 
 ApkAssetsPtr ApkAssets::LoadFromFd(base::unique_fd fd, const std::string& debug_name,
                                    package_property_t flags, off64_t offset, off64_t len) {
-  return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
+  return LoadImpl(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
 }
 
-ApkAssetsPtr ApkAssets::Load(std::unique_ptr<AssetsProvider> assets, package_property_t flags) {
+ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
+                                 package_property_t flags) {
   return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */);
 }
 
-ApkAssetsPtr ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset,
-                                  std::unique_ptr<AssetsProvider> assets,
+ApkAssetsPtr ApkAssets::LoadTable(std::unique_ptr<Asset>&& resources_asset,
+                                  std::unique_ptr<AssetsProvider>&& assets,
                                   package_property_t flags) {
   if (resources_asset == nullptr) {
     return {};
@@ -97,10 +98,10 @@
                   std::move(loaded_idmap));
 }
 
-ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
                                  package_property_t property_flags,
-                                 std::unique_ptr<Asset> idmap_asset,
-                                 std::unique_ptr<LoadedIdmap> loaded_idmap) {
+                                 std::unique_ptr<Asset>&& idmap_asset,
+                                 std::unique_ptr<LoadedIdmap>&& loaded_idmap) {
   if (assets == nullptr) {
     return {};
   }
@@ -119,11 +120,11 @@
                   std::move(idmap_asset), std::move(loaded_idmap));
 }
 
-ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
-                                 std::unique_ptr<AssetsProvider> assets,
+ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset>&& resources_asset,
+                                 std::unique_ptr<AssetsProvider>&& assets,
                                  package_property_t property_flags,
-                                 std::unique_ptr<Asset> idmap_asset,
-                                 std::unique_ptr<LoadedIdmap> loaded_idmap) {
+                                 std::unique_ptr<Asset>&& idmap_asset,
+                                 std::unique_ptr<LoadedIdmap>&& loaded_idmap) {
   if (assets == nullptr ) {
     return {};
   }
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 1fa6752..231808b 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -47,13 +47,37 @@
                                  package_property_t flags = 0U, off64_t offset = 0,
                                  off64_t len = AssetsProvider::kUnknownLength);
 
+  //
   // Creates an ApkAssets from an AssetProvider.
-  // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed.
-  static ApkAssetsPtr Load(std::unique_ptr<AssetsProvider> assets, package_property_t flags = 0U);
+  // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed;
+  // the original argument is not moved from if loading fails.
+  //
+  // Note: this function takes care of the case when you pass a move(unique_ptr<Derived>)
+  //    that would create a temporary unique_ptr<AssetsProvider> by moving your pointer into
+  //    it before the function call, making it impossible to not move from the parameter
+  //    on loading failure. The two overloads take care of moving the pointer back if needed.
+  //
+
+  template <class T>
+  static ApkAssetsPtr Load(std::unique_ptr<T>&& assets, package_property_t flags = 0U)
+      requires(std::is_same_v<T, AssetsProvider>) {
+    return LoadImpl(std::move(assets), flags);
+  }
+
+  template <class T>
+  static ApkAssetsPtr Load(std::unique_ptr<T>&& assets, package_property_t flags = 0U)
+      requires(!std::is_same_v<T, AssetsProvider> && std::is_base_of_v<AssetsProvider, T>) {
+    std::unique_ptr<AssetsProvider> base_assets(std::move(assets));
+    auto res = LoadImpl(std::move(base_assets), flags);
+    if (!res) {
+      assets.reset(static_cast<T*>(base_assets.release()));
+    }
+    return res;
+  }
 
   // Creates an ApkAssets from the given asset file representing a resources.arsc.
-  static ApkAssetsPtr LoadTable(std::unique_ptr<Asset> resources_asset,
-                                std::unique_ptr<AssetsProvider> assets,
+  static ApkAssetsPtr LoadTable(std::unique_ptr<Asset>&& resources_asset,
+                                std::unique_ptr<AssetsProvider>&& assets,
                                 package_property_t flags = 0U);
 
   // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
@@ -94,17 +118,29 @@
 
   bool IsUpToDate() const;
 
- private:
-  static ApkAssetsPtr LoadImpl(std::unique_ptr<AssetsProvider> assets,
-                               package_property_t property_flags,
-                               std::unique_ptr<Asset> idmap_asset,
-                               std::unique_ptr<LoadedIdmap> loaded_idmap);
+  // DANGER!
+  // This is a destructive method that rips the assets provider out of ApkAssets object.
+  // It is only useful when one knows this assets object can't be used anymore, and they
+  // need the underlying assets provider back (e.g. when initialization fails for some
+  // reason).
+  std::unique_ptr<AssetsProvider> TakeAssetsProvider() && {
+    return std::move(assets_provider_);
+  }
 
-  static ApkAssetsPtr LoadImpl(std::unique_ptr<Asset> resources_asset,
-                               std::unique_ptr<AssetsProvider> assets,
+ private:
+  static ApkAssetsPtr LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
                                package_property_t property_flags,
-                               std::unique_ptr<Asset> idmap_asset,
-                               std::unique_ptr<LoadedIdmap> loaded_idmap);
+                               std::unique_ptr<Asset>&& idmap_asset,
+                               std::unique_ptr<LoadedIdmap>&& loaded_idmap);
+
+  static ApkAssetsPtr LoadImpl(std::unique_ptr<Asset>&& resources_asset,
+                               std::unique_ptr<AssetsProvider>&& assets,
+                               package_property_t property_flags,
+                               std::unique_ptr<Asset>&& idmap_asset,
+                               std::unique_ptr<LoadedIdmap>&& loaded_idmap);
+
+  static ApkAssetsPtr LoadImpl(std::unique_ptr<AssetsProvider>&& assets,
+                               package_property_t flags = 0U);
 
   // Allows us to make it possible to call make_shared from inside the class but still keeps the
   // ctor 'private' for all means and purposes.
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 70326b7..c36d990 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -28,6 +28,7 @@
 using ::com::android::basic::R;
 using ::testing::Eq;
 using ::testing::Ge;
+using ::testing::IsNull;
 using ::testing::NotNull;
 using ::testing::SizeIs;
 using ::testing::StrEq;
@@ -108,4 +109,26 @@
   EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n"));
 }
 
+TEST(ApkAssetsTest, TakeAssetsProviderNotCrashing) {
+  // Make sure the apk assets object can survive taking its assets provider and doesn't crash
+  // the process.
+  {
+    auto loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+    ASSERT_THAT(loaded_apk, NotNull());
+
+    auto provider = std::move(*loaded_apk).TakeAssetsProvider();
+    ASSERT_THAT(provider, NotNull());
+  }
+  // If this test doesn't crash by this point we're all good.
+}
+
+TEST(ApkAssetsTest, AssetsProviderNotMovedOnError) {
+  auto assets_provider
+      = ZipAssetsProvider::Create(GetTestDataPath() + "/bad/bad.apk", 0);
+  ASSERT_THAT(assets_provider, NotNull());
+  auto loaded_apk = ApkAssets::Load(std::move(assets_provider));
+  ASSERT_THAT(loaded_apk, IsNull());
+  ASSERT_THAT(assets_provider, NotNull());
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/data/bad/bad.apk b/libs/androidfw/tests/data/bad/bad.apk
new file mode 100644
index 0000000..3226bcd5
--- /dev/null
+++ b/libs/androidfw/tests/data/bad/bad.apk
Binary files differ
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 28d96e3..65663d0 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -49,8 +49,7 @@
     mLocked.lastFrameUpdatedTime = 0;
 
     mLocked.pointerFadeDirection = 0;
-    mLocked.pointerX = 0;
-    mLocked.pointerY = 0;
+    mLocked.pointerPosition = {0, 0};
     mLocked.pointerAlpha = 0.0f; // pointer is initially faded
     mLocked.pointerSprite = mContext.getSpriteController().createSprite();
     mLocked.updatePointerIcon = false;
@@ -66,11 +65,11 @@
     mLocked.pointerSprite.clear();
 }
 
-FloatPoint MouseCursorController::move(float deltaX, float deltaY) {
+vec2 MouseCursorController::move(vec2 delta) {
 #if DEBUG_MOUSE_CURSOR_UPDATES
     ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
 #endif
-    if (deltaX == 0.0f && deltaY == 0.0f) {
+    if (delta.x == 0.0f && delta.y == 0.0f) {
         return {0, 0};
     }
 
@@ -78,19 +77,19 @@
     // if there's another display on the other side of the transition. At this point we still need
     // to move the cursor to the boundary.
     std::scoped_lock lock(mLock);
-    const FloatPoint position{mLocked.pointerX + deltaX, mLocked.pointerY + deltaY};
-    setPositionLocked(position.x, position.y);
+    const vec2 targetPosition = mLocked.pointerPosition + delta;
+    setPositionLocked(targetPosition);
     // The amount of the delta that was not consumed as a result of the cursor
     // hitting the edge of the display.
-    return {position.x - mLocked.pointerX, position.y - mLocked.pointerY};
+    return targetPosition - mLocked.pointerPosition;
 }
 
-void MouseCursorController::setPosition(float x, float y) {
+void MouseCursorController::setPosition(vec2 position) {
 #if DEBUG_MOUSE_CURSOR_UPDATES
     ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
 #endif
     std::scoped_lock lock(mLock);
-    setPositionLocked(x, y);
+    setPositionLocked(position);
 }
 
 FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) {
@@ -105,21 +104,21 @@
     };
 }
 
-void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
+void MouseCursorController::setPositionLocked(vec2 position) REQUIRES(mLock) {
     const auto& v = mLocked.viewport;
     if (!v.isValid()) return;
 
     const FloatRect bounds = getBoundsLocked();
-    mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x));
-    mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y));
+    mLocked.pointerPosition.x = std::max(bounds.left, std::min(bounds.right, position.x));
+    mLocked.pointerPosition.y = std::max(bounds.top, std::min(bounds.bottom, position.y));
 
     updatePointerLocked();
 }
 
-FloatPoint MouseCursorController::getPosition() const {
+vec2 MouseCursorController::getPosition() const {
     std::scoped_lock lock(mLock);
 
-    return {mLocked.pointerX, mLocked.pointerY};
+    return mLocked.pointerPosition;
 }
 
 ui::LogicalDisplayId MouseCursorController::getDisplayId() const {
@@ -221,20 +220,21 @@
         if (viewport.isValid()) {
             // Use integer coordinates as the starting point for the cursor location.
             // We usually expect display sizes to be even numbers, so the flooring is precautionary.
-            mLocked.pointerX = std::floor((viewport.logicalLeft + viewport.logicalRight) / 2);
-            mLocked.pointerY = std::floor((viewport.logicalTop + viewport.logicalBottom) / 2);
+            mLocked.pointerPosition.x =
+                    std::floor((viewport.logicalLeft + viewport.logicalRight) / 2);
+            mLocked.pointerPosition.y =
+                    std::floor((viewport.logicalTop + viewport.logicalBottom) / 2);
             // Reload icon resources for density may be changed.
             loadResourcesLocked(getAdditionalMouseResources);
         } else {
-            mLocked.pointerX = 0;
-            mLocked.pointerY = 0;
+            mLocked.pointerPosition = {0, 0};
         }
     } else if (oldViewport.orientation != viewport.orientation) {
         // Apply offsets to convert from the pixel top-left corner position to the pixel center.
         // This creates an invariant frame of reference that we can easily rotate when
         // taking into account that the pointer may be located at fractional pixel offsets.
-        float x = mLocked.pointerX + 0.5f;
-        float y = mLocked.pointerY + 0.5f;
+        float x = mLocked.pointerPosition.x + 0.5f;
+        float y = mLocked.pointerPosition.y + 0.5f;
         float temp;
 
         // Undo the previous rotation.
@@ -279,8 +279,8 @@
 
         // Apply offsets to convert from the pixel center to the pixel top-left corner position
         // and save the results.
-        mLocked.pointerX = x - 0.5f;
-        mLocked.pointerY = y - 0.5f;
+        mLocked.pointerPosition.x = x - 0.5f;
+        mLocked.pointerPosition.y = y - 0.5f;
     }
 
     updatePointerLocked();
@@ -366,7 +366,7 @@
     spriteController.openTransaction();
 
     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
-    mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+    mLocked.pointerSprite->setPosition(mLocked.pointerPosition.x, mLocked.pointerPosition.y);
     mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
     mLocked.pointerSprite->setSkipScreenshot(mLocked.skipScreenshot);
 
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index e14a55d..7c674b5 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -41,9 +41,9 @@
     ~MouseCursorController();
 
     // Move the pointer and return unconsumed delta
-    FloatPoint move(float deltaX, float deltaY);
-    void setPosition(float x, float y);
-    FloatPoint getPosition() const;
+    vec2 move(vec2 delta);
+    void setPosition(vec2 position);
+    vec2 getPosition() const;
     ui::LogicalDisplayId getDisplayId() const;
     void fade(PointerControllerInterface::Transition transition);
     void unfade(PointerControllerInterface::Transition transition);
@@ -81,8 +81,7 @@
         nsecs_t lastFrameUpdatedTime;
 
         int32_t pointerFadeDirection;
-        float pointerX;
-        float pointerY;
+        vec2 pointerPosition;
         float pointerAlpha;
         sp<Sprite> pointerSprite;
         SpriteIcon pointerIcon;
@@ -101,7 +100,7 @@
 
     } mLocked GUARDED_BY(mLock);
 
-    void setPositionLocked(float x, float y);
+    void setPositionLocked(vec2 position);
 
     void updatePointerLocked();
 
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index a713f1d..0b81211 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -138,7 +138,7 @@
     return mDisplayInfoListener->mLock;
 }
 
-FloatPoint PointerController::move(float deltaX, float deltaY) {
+vec2 PointerController::move(float deltaX, float deltaY) {
     const ui::LogicalDisplayId displayId = mCursorController.getDisplayId();
     ui::Transform transform;
     {
@@ -147,10 +147,7 @@
     }
 
     const vec2 transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
-
-    const FloatPoint unconsumedDelta = mCursorController.move(transformed.x, transformed.y);
-    return FloatPoint(transformWithoutTranslation(transform.inverse(),
-                                                  {unconsumedDelta.x, unconsumedDelta.y}));
+    return transformWithoutTranslation(transform.inverse(), mCursorController.move(transformed));
 }
 
 void PointerController::setPosition(float x, float y) {
@@ -161,16 +158,15 @@
         const auto& transform = getTransformForDisplayLocked(displayId);
         transformed = transform.transform(x, y);
     }
-    mCursorController.setPosition(transformed.x, transformed.y);
+    mCursorController.setPosition(transformed);
 }
 
-FloatPoint PointerController::getPosition() const {
+vec2 PointerController::getPosition() const {
     const ui::LogicalDisplayId displayId = mCursorController.getDisplayId();
     const auto p = mCursorController.getPosition();
     {
         std::scoped_lock lock(getLock());
-        const auto& transform = getTransformForDisplayLocked(displayId);
-        return FloatPoint{transform.inverse().transform(p.x, p.y)};
+        return getTransformForDisplayLocked(displayId).inverse().transform(p.x, p.y);
     }
 }
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 8b33190..afd7215 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -51,9 +51,9 @@
 
     ~PointerController() override;
 
-    FloatPoint move(float deltaX, float deltaY) override;
+    vec2 move(float deltaX, float deltaY) override;
     void setPosition(float x, float y) override;
-    FloatPoint getPosition() const override;
+    vec2 getPosition() const override;
     ui::LogicalDisplayId getDisplayId() const override;
     void fade(Transition transition) override;
     void unfade(Transition transition) override;
@@ -166,13 +166,13 @@
 
     ~TouchPointerController() override;
 
-    FloatPoint move(float, float) override {
+    vec2 move(float, float) override {
         LOG_ALWAYS_FATAL("Should not be called");
     }
     void setPosition(float, float) override {
         LOG_ALWAYS_FATAL("Should not be called");
     }
-    FloatPoint getPosition() const override {
+    vec2 getPosition() const override {
         LOG_ALWAYS_FATAL("Should not be called");
     }
     ui::LogicalDisplayId getDisplayId() const override {
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 6a4bb21..a3b06e8 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -16,8 +16,10 @@
 
 package com.android.localtransport;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.backup.BackupAgent;
+import android.app.backup.BackupAnnotations;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupManagerMonitor;
@@ -52,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * Backup transport for stashing stuff into a known location on disk, and
@@ -939,4 +942,15 @@
             }
         }
     }
+
+    @NonNull
+    @Override
+    public List<String> getPackagesThatShouldNotUseRestrictedMode(
+            @NonNull List<String> packageNames,
+            @BackupAnnotations.OperationType int operationType) {
+        if (DEBUG) {
+            Log.d(TAG, "No restricted mode packages: " + mParameters.noRestrictedModePackages());
+        }
+        return mParameters.noRestrictedModePackages();
+    }
 }
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index aaa18bf..c980913 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -16,26 +16,33 @@
 
 package com.android.localtransport;
 
-import android.util.KeyValueSettingObserver;
 import android.content.ContentResolver;
 import android.os.Handler;
 import android.provider.Settings;
 import android.util.KeyValueListParser;
+import android.util.KeyValueSettingObserver;
+
+import java.util.Arrays;
+import java.util.List;
 
 public class LocalTransportParameters extends KeyValueSettingObserver {
-    private static final String TAG = "LocalTransportParams";
     private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
     private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
     private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
     private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer";
     private static final String KEY_IS_ENCRYPTED = "is_encrypted";
     private static final String KEY_LOG_AGENT_RESULTS = "log_agent_results";
+    // This needs to be a list of package names separated by semicolons. For example:
+    // "com.package1;com.package2;com.package3". We can't use commas because the base class uses
+    // commas to split Key/Value pairs.
+    private static final String KEY_NO_RESTRICTED_MODE_PACKAGES = "no_restricted_mode_packages";
 
     private boolean mFakeEncryptionFlag;
     private boolean mIsNonIncrementalOnly;
     private boolean mIsDeviceTransfer;
     private boolean mIsEncrypted;
     private boolean mLogAgentResults;
+    private String mNoRestrictedModePackages;
 
     public LocalTransportParameters(Handler handler, ContentResolver resolver) {
         super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -61,6 +68,13 @@
         return mLogAgentResults;
     }
 
+    List<String> noRestrictedModePackages() {
+        if (mNoRestrictedModePackages == null) {
+            return List.of();
+        }
+        return Arrays.stream(mNoRestrictedModePackages.split(";")).toList();
+    }
+
     public String getSettingValue(ContentResolver resolver) {
         return Settings.Secure.getString(resolver, SETTING);
     }
@@ -71,5 +85,6 @@
         mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false);
         mIsEncrypted = parser.getBoolean(KEY_IS_ENCRYPTED, false);
         mLogAgentResults = parser.getBoolean(KEY_LOG_AGENT_RESULTS, /* def */ false);
+        mNoRestrictedModePackages = parser.getString(KEY_NO_RESTRICTED_MODE_PACKAGES, /* def */ "");
     }
 }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 5b280486..c1f7868 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1879,3 +1879,10 @@
     description: "Implement the depth push scaling effect on the current app when users pull down shade."
     bug: "370560660"
 }
+
+flag {
+    name: "expanded_privacy_indicators_on_large_screen"
+    namespace: "systemui"
+    description: "Larger privacy indicators on large screen"
+    bug: "381864715"
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 7a8d20a..caf5e41 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -124,7 +124,7 @@
         return newDragController
     }
 
-    internal fun createSwipeAnimation(swipes: Swipes, result: UserActionResult): SwipeAnimation<*> {
+    private fun createSwipeAnimation(swipes: Swipes, result: UserActionResult): SwipeAnimation<*> {
         val upOrLeftResult = swipes.upOrLeftResult
         val downOrRightResult = swipes.downOrRightResult
         val isUpOrLeft =
@@ -248,38 +248,8 @@
                 else -> desiredOffset.fastCoerceIn(distance, 0f)
             }
 
-        val consumedDelta = newOffset - previousOffset
-
         swipeAnimation.dragOffset = newOffset
-        val result = swipes.findUserActionResult(directionOffset = newOffset)
-
-        if (result == null) {
-            onCancel(canChangeContent = true)
-            return 0f
-        }
-
-        val currentTransitionIrreversible =
-            if (swipeAnimation.isUpOrLeft) {
-                swipes.upOrLeftResult?.isIrreversible ?: false
-            } else {
-                swipes.downOrRightResult?.isIrreversible ?: false
-            }
-
-        val needNewTransition =
-            !currentTransitionIrreversible &&
-                (result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
-                    result.transitionKey != swipeAnimation.contentTransition.key)
-
-        if (needNewTransition) {
-            // Make sure the current transition will finish to the right current scene.
-            swipeAnimation.currentContent = swipeAnimation.fromContent
-
-            val newSwipeAnimation = draggableHandler.createSwipeAnimation(swipes, result)
-            newSwipeAnimation.dragOffset = newOffset
-            updateTransition(newSwipeAnimation)
-        }
-
-        return consumedDelta
+        return newOffset - previousOffset
     }
 
     override suspend fun onStop(velocity: Float, canChangeContent: Boolean): Float {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 759100b..a14b2b3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -554,12 +554,6 @@
      * bigger than 100% when the user released their finger. `
      */
     open val requiresFullDistanceSwipe: Boolean,
-
-    /**
-     * Whether swiping back in the opposite direction past the origin point of the swipe can replace
-     * the action with the action for the opposite direction.
-     */
-    open val isIrreversible: Boolean = false,
 ) {
     internal abstract fun toContent(currentScene: SceneKey): ContentKey
 
@@ -569,7 +563,6 @@
         val toScene: SceneKey,
         override val transitionKey: TransitionKey? = null,
         override val requiresFullDistanceSwipe: Boolean = false,
-        override val isIrreversible: Boolean = false,
     ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
         override fun toContent(currentScene: SceneKey): ContentKey = toScene
     }
@@ -579,7 +572,6 @@
         val overlay: OverlayKey,
         override val transitionKey: TransitionKey? = null,
         override val requiresFullDistanceSwipe: Boolean = false,
-        override val isIrreversible: Boolean = false,
     ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
         override fun toContent(currentScene: SceneKey): ContentKey = overlay
     }
@@ -622,14 +614,7 @@
              * the user released their finger.
              */
             requiresFullDistanceSwipe: Boolean = false,
-
-            /**
-             * Whether swiping back in the opposite direction past the origin point of the swipe can
-             * replace the action with the action for the opposite direction.
-             */
-            isIrreversible: Boolean = false,
-        ): UserActionResult =
-            ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe, isIrreversible)
+        ): UserActionResult = ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe)
 
         /** A [UserActionResult] that shows [toOverlay]. */
         operator fun invoke(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 394568d..2c8dc32 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -418,37 +418,6 @@
     }
 
     @Test
-    fun onDragReversedDirection_changeToScene() = runGestureTest {
-        // Drag A -> B with progress 0.6
-        val dragController = onDragStarted(overSlop = -60f)
-        assertTransition(
-            currentScene = SceneA,
-            fromScene = SceneA,
-            toScene = SceneB,
-            progress = 0.6f,
-        )
-
-        // Reverse direction such that A -> C now with 0.4
-        dragController.onDragDelta(pixels = 100f)
-        assertTransition(
-            currentScene = SceneA,
-            fromScene = SceneA,
-            toScene = SceneC,
-            progress = 0.4f,
-        )
-
-        // After the drag stopped scene C should be committed
-        dragController.onDragStoppedAnimateNow(
-            velocity = velocityThreshold,
-            onAnimationStart = {
-                assertTransition(currentScene = SceneC, fromScene = SceneA, toScene = SceneC)
-            },
-            expectedConsumedVelocity = velocityThreshold,
-        )
-        assertIdle(currentScene = SceneC)
-    }
-
-    @Test
     fun onDragStartedWithoutActionsInBothDirections_stayIdle() = runGestureTest {
         onDragStarted(
             horizontalDraggableHandler,
@@ -498,31 +467,9 @@
     }
 
     @Test
-    fun onDragWithActionsInBothDirections_dragToOppositeDirectionReplacesAction() = runGestureTest {
-        // We are on SceneA. UP -> B, DOWN-> C.
-        val dragController = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
-        assertTransition(
-            currentScene = SceneA,
-            fromScene = SceneA,
-            toScene = SceneB,
-            progress = 0.2f,
-        )
-
-        // Reverse drag direction, it will replace the previous transition
-        dragController.onDragDelta(pixels = down(fractionOfScreen = 0.5f))
-        assertTransition(
-            currentScene = SceneA,
-            fromScene = SceneA,
-            toScene = SceneC,
-            progress = 0.3f,
-        )
-    }
-
-    @Test
     fun onDragWithActionsInBothDirections_dragToOppositeDirectionNotReplaceable() = runGestureTest {
         // We are on SceneA. UP -> B, DOWN-> C. The up swipe is not replaceable though.
-        mutableUserActionsA =
-            mapOf(Swipe.Up to UserActionResult(SceneB, isIrreversible = true), Swipe.Down to SceneC)
+        mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB), Swipe.Down to SceneC)
         val dragController =
             onDragStarted(
                 pointersInfo =
@@ -536,7 +483,7 @@
             progress = 0.2f,
         )
 
-        // Reverse drag direction, it cannot replace the previous transition
+        // Reverse drag direction, it does not replace the previous transition.
         dragController.onDragDelta(pixels = down(fractionOfScreen = 0.5f))
         assertTransition(
             currentScene = SceneA,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index b3a3261..fe7b5b6 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -664,17 +664,11 @@
             }
         }
 
-        // Swipe down for the default transition from A to B.
-        rule.onRoot().performTouchInput {
-            down(middle)
-            moveBy(Offset(0f, touchSlop), delayMillis = 1_000)
-        }
-
-        assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isTrue()
-        assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(1)
-
         // Move the pointer up to swipe to scene B using the new transition.
-        rule.onRoot().performTouchInput { moveBy(Offset(0f, -1.dp.toPx()), delayMillis = 1_000) }
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy(Offset(0f, -touchSlop - 1.dp.toPx()), delayMillis = 1_000)
+        }
         assertThat(state.isTransitioning(from = SceneA, to = SceneB)).isTrue()
         assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(2)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
index 1b07241..7fb879c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
@@ -31,7 +31,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.animation.AnimatorTestRule;
-import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -49,7 +49,7 @@
     @Rule
     public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
 
-    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+    private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
     private ExpandableNotificationRow mRow;
     private ExpandHelper mExpandHelper;
     private ExpandHelper.Callback mCallback;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
index 755c4eb..ca7e203 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt
@@ -85,8 +85,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
-            assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+            assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
 
             setUpState(
                 isShadeTouchable = false,
@@ -103,8 +102,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
-            assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+            assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
         }
 
     @Test
@@ -122,7 +120,7 @@
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
 
             setUpState(
                 isShadeTouchable = false,
@@ -140,7 +138,7 @@
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
         }
 
     @Test
@@ -158,9 +156,7 @@
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(
-                    UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
-                )
+                .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
 
             setUpState(
                 isShadeTouchable = false,
@@ -174,9 +170,7 @@
             assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home))
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(
-                    UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
-                )
+                .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
         }
 
     private fun TestScope.setUpState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
index 55b87db..d6daa79 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt
@@ -86,8 +86,7 @@
             )
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
-            assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+            assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
 
@@ -105,8 +104,7 @@
             )
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
-            assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+            assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
         }
@@ -127,7 +125,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
 
@@ -146,7 +144,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
         }
@@ -167,9 +165,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(
-                    UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
-                )
+                .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
 
@@ -184,9 +180,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(
-                    UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
-                )
+                .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
             assertThat(actions?.get(Swipe.Start)).isNull()
             assertThat(actions?.get(Swipe.End)).isNull()
         }
@@ -206,8 +200,7 @@
             )
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
-            assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+            assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
 
@@ -225,8 +218,7 @@
             )
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
-            assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true))
+            assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
         }
@@ -247,7 +239,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
 
@@ -266,7 +258,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
         }
@@ -287,9 +279,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(
-                    UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
-                )
+                .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
 
@@ -304,9 +294,7 @@
             assertThat(actions).isNotEmpty()
             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
             assertThat(actions?.get(Swipe.Down))
-                .isEqualTo(
-                    UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
-                )
+                .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
             assertThat(actions?.get(Swipe.End)).isNull()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index 407bdf8..cc718c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -74,6 +74,27 @@
             /* modifiers = */ KeyEvent.META_SHIFT_ON,
         )
 
+    private val ShortcutsWithDiffSizeOfKeys =
+        KeyboardShortcutInfo(
+            /* label = */ "Shortcuts with diff size of keys",
+            /* keycode = */ KeyEvent.KEYCODE_HOME,
+            /* modifiers = */ 0,
+        )
+
+    private val ShortcutsWithDiffSizeOfKeys2 =
+        KeyboardShortcutInfo(
+            /* label = */ ShortcutsWithDiffSizeOfKeys.label,
+            /* keycode = */ KeyEvent.KEYCODE_1,
+            /* modifiers = */ META_META_ON,
+        )
+
+    private val ShortcutsWithDiffSizeOfKeys3 =
+        KeyboardShortcutInfo(
+            /* label = */ ShortcutsWithDiffSizeOfKeys.label,
+            /* keycode = */ KeyEvent.KEYCODE_2,
+            /* modifiers = */ META_META_ON or META_FUNCTION_ON,
+        )
+
     private val shortcutWithGroupedRepeatedLabel =
         shortcut(shortcutInfoWithRepeatedLabel.label!!.toString()) {
             command {
@@ -381,6 +402,16 @@
             groupWithSupportedAndUnsupportedModifierShortcut,
         )
 
+    val groupWithDifferentSizeOfShortcutKeys =
+        KeyboardShortcutGroup(
+            "Group with different size of shortcut keys",
+            listOf(
+                ShortcutsWithDiffSizeOfKeys3,
+                ShortcutsWithDiffSizeOfKeys,
+                ShortcutsWithDiffSizeOfKeys2,
+            ),
+        )
+
     val subCategoriesWithUnsupportedModifiersRemoved =
         listOf(subCategoryWithStandardShortcut, subCategoryWithUnsupportedShortcutsRemoved)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
index c3cedba..8f0bc64 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -369,6 +369,20 @@
         }
     }
 
+    @Test
+    fun categories_showShortcutsInAscendingOrderOfKeySize() =
+        testScope.runTest {
+            systemShortcutsSource.setGroups(TestShortcuts.groupWithDifferentSizeOfShortcutKeys)
+            val categories by collectLastValue(interactor.shortcutCategories)
+
+            helper.showFromActivity()
+
+            val systemCategoryShortcuts = categories?.get(0)?.subCategories?.get(0)?.shortcuts
+            val shortcutKeyCount =
+                systemCategoryShortcuts?.flatMap { it.commands }?.map { it.keys.size }
+            assertThat(shortcutKeyCount).containsExactly(1, 2, 3).inOrder()
+        }
+
     private fun setCustomInputGestures(customInputGestures: List<InputGestureData>) {
         whenever(fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()))
             .thenReturn(customInputGestures)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
index 62cc763..97e6763 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
@@ -306,8 +306,7 @@
                 // Top edge is not applicable in dual shade, as well as two-finger swipe.
                 assertThat(downDestination).isNull()
             } else {
-                assertThat(downDestination)
-                    .isEqualTo(ShowOverlay(Overlays.NotificationsShade, isIrreversible = true))
+                assertThat(downDestination).isEqualTo(ShowOverlay(Overlays.NotificationsShade))
                 assertThat(downDestination?.transitionKey).isNull()
             }
 
@@ -323,7 +322,7 @@
                 downWithTwoPointers -> assertThat(downFromTopRightDestination).isNull()
                 else -> {
                     assertThat(downFromTopRightDestination)
-                        .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true))
+                        .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade))
                     assertThat(downFromTopRightDestination?.transitionKey).isNull()
                 }
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
index bb2e941..fc915ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
@@ -104,7 +104,7 @@
             runCurrent()
 
             assertThat(userActions?.get(swipeDownFromTopWithTwoFingers()))
-                .isEqualTo(UserActionResult(Scenes.QuickSettings, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.QuickSettings))
         }
 
     @Test
@@ -116,7 +116,7 @@
             runCurrent()
 
             assertThat(userActions?.get(swipeDownFromTopWithTwoFingers()))
-                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true))
+                .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index ca0f9ef..8bca17f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -28,7 +28,7 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.PluginManager
@@ -57,9 +57,7 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.time.SystemClock
-import com.android.systemui.wmshell.BubblesManager
 import com.google.android.msdl.domain.MSDLPlayer
-import java.util.Optional
 import junit.framework.Assert
 import org.junit.After
 import org.junit.Before
@@ -103,9 +101,8 @@
     private val gutsManager: NotificationGutsManager = mock()
     private val onUserInteractionCallback: OnUserInteractionCallback = mock()
     private val falsingManager: FalsingManager = mock()
-    private val featureFlags: FeatureFlags = mock()
+    private val featureFlags: FeatureFlagsClassic = mock()
     private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
-    private val bubblesManager: BubblesManager = mock()
     private val settingsController: NotificationSettingsController = mock()
     private val dragController: ExpandableNotificationRowDragController = mock()
     private val dismissibilityProvider: NotificationDismissibilityProvider = mock()
@@ -147,7 +144,6 @@
                 falsingManager,
                 featureFlags,
                 peopleNotificationIdentifier,
-                Optional.of(bubblesManager),
                 settingsController,
                 dragController,
                 dismissibilityProvider,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 080ac3f..b323ef8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -25,7 +25,6 @@
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
 import static com.android.systemui.util.Assert.runWithCurrentThreadAsMainThread;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -59,8 +58,8 @@
 import com.android.keyguard.TestScopeProvider;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
 import com.android.systemui.media.dialog.MediaOutputDialogManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,7 +70,6 @@
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
-import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -79,6 +77,7 @@
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.notification.icon.IconBuilder;
 import com.android.systemui.statusbar.notification.icon.IconManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -89,7 +88,6 @@
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
@@ -99,7 +97,6 @@
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.util.time.SystemClockImpl;
-import com.android.systemui.wmshell.BubblesManager;
 import com.android.systemui.wmshell.BubblesTestActivity;
 
 import kotlin.coroutines.CoroutineContext;
@@ -109,7 +106,6 @@
 import org.mockito.ArgumentCaptor;
 
 import java.util.Objects;
-import java.util.Optional;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -149,7 +145,7 @@
     private final NotificationDismissibilityProvider mDismissibilityProvider;
     public final Runnable mFutureDismissalRunnable;
     private @InflationFlag int mDefaultInflationFlags;
-    private final FakeFeatureFlags mFeatureFlags;
+    private final FakeFeatureFlagsClassic mFeatureFlags;
     private final SystemClock mSystemClock;
     private final RowInflaterTaskLogger mRowInflaterTaskLogger;
     private final TestScope mTestScope = TestScopeProvider.getTestScope();
@@ -167,17 +163,17 @@
             Context context,
             TestableDependency dependency,
             @Nullable TestableLooper testLooper) {
-        this(context, dependency, testLooper, new FakeFeatureFlags());
+        this(context, dependency, testLooper, new FakeFeatureFlagsClassic());
     }
 
     public NotificationTestHelper(
             Context context,
             TestableDependency dependency,
             @Nullable TestableLooper testLooper,
-            @NonNull FakeFeatureFlags featureFlags) {
+            @NonNull FakeFeatureFlagsClassic featureFlags) {
         mContext = context;
         mFeatureFlags = Objects.requireNonNull(featureFlags);
-        dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
+        dependency.injectTestDependency(FeatureFlagsClassic.class, mFeatureFlags);
         dependency.injectMockDependency(NotificationMediaManager.class);
         dependency.injectMockDependency(NotificationShadeWindowController.class);
         dependency.injectMockDependency(MediaOutputDialogManager.class);
@@ -280,24 +276,6 @@
     }
 
     /**
-     * Creates a generic row with rounded border.
-     *
-     * @return a generic row with the set roundness.
-     * @throws Exception
-     */
-    public ExpandableNotificationRow createRowWithRoundness(
-            float topRoundness,
-            float bottomRoundness,
-            SourceType sourceType
-    ) throws Exception {
-        ExpandableNotificationRow row = createRow();
-        row.requestRoundness(topRoundness, bottomRoundness, sourceType, /*animate = */ false);
-        assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f);
-        assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f);
-        return row;
-    }
-
-    /**
      * Creates a generic row.
      *
      * @return a generic row with no special properties.
@@ -400,9 +378,8 @@
                 null /* groupKey */,
                 makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
         n.flags |= FLAG_FSI_REQUESTED_BUT_DENIED;
-        ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
+        return generateRow(n, PKG, UID, USER_HANDLE,
                 mDefaultInflationFlags, IMPORTANCE_HIGH);
-        return row;
     }
 
 
@@ -668,7 +645,6 @@
                 mStatusBarStateController,
                 mPeopleNotificationIdentifier,
                 mOnUserInteractionCallback,
-                Optional.of(mock(BubblesManager.class)),
                 mock(NotificationGutsManager.class),
                 mDismissibilityProvider,
                 mock(MetricsLogger.class),
@@ -676,7 +652,6 @@
                 mock(ColorUpdateLogger.class),
                 mock(SmartReplyConstants.class),
                 mock(SmartReplyController.class),
-                mFeatureFlags,
                 mock(IStatusBarService.class),
                 mock(UiEventLogger.class));
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 660eb30..87833d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -5,7 +5,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
 import com.android.systemui.util.mockito.mock
 import junit.framework.Assert.assertEquals
@@ -18,7 +18,7 @@
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class NotificationTargetsHelperTest : SysuiTestCase() {
-    private val featureFlags = FakeFeatureFlags()
+    private val featureFlags = FakeFeatureFlagsClassic()
     lateinit var notificationTestHelper: NotificationTestHelper
     private val sectionsManager: NotificationSectionsManager = mock()
     private val stackScrollLayout: NotificationStackScrollLayout = mock()
@@ -90,11 +90,7 @@
                 )
 
         val expected =
-            RoundableTargets(
-                before = children.attachedChildren[1],
-                swiped = swiped,
-                after = null,
-            )
+            RoundableTargets(before = children.attachedChildren[1], swiped = swiped, after = null)
         assertEquals(expected, actual)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
index 687ad95..5060abd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -54,7 +54,7 @@
         listOf(
             KeyboardShortcutGroup(
                 resources.getString(R.string.shortcut_helper_category_system_controls),
-                hardwareShortcuts(deviceId) + systemControlsShortcuts(),
+                systemControlsShortcuts() + hardwareShortcuts(deviceId),
             ),
             KeyboardShortcutGroup(
                 resources.getString(R.string.shortcut_helper_category_system_apps),
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
index 2385cc6..d625846 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -95,7 +95,7 @@
                 Shortcut(
                     label = commonLabel,
                     icon = groupedShortcuts.firstOrNull()?.icon,
-                    commands = groupedShortcuts.flatMap { it.commands },
+                    commands = groupedShortcuts.flatMap { it.commands }.sortedBy { it.keys.size },
                     contentDescription =
                         toContentDescription(commonLabel, groupedShortcuts.flatMap { it.commands }),
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index 6f5d262..e17255a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -53,7 +53,22 @@
     private lateinit var cancelButton: TextView
     private lateinit var screenShareModeSpinner: Spinner
     protected lateinit var dialog: AlertDialog
-    private lateinit var viewBinder: BaseMediaProjectionPermissionViewBinder
+    protected lateinit var viewBinder: BaseMediaProjectionPermissionViewBinder
+
+    /**
+     * Create the view binder for the permission dialog, this can be override by child classes to
+     * support a different type of view binder
+     */
+    open fun createViewBinder(): BaseMediaProjectionPermissionViewBinder {
+        return BaseMediaProjectionPermissionViewBinder(
+            screenShareOptions,
+            appName,
+            hostUid,
+            mediaProjectionMetricsLogger,
+            defaultSelectedMode,
+            dialog,
+        )
+    }
 
     @CallSuper
     override fun onStop(dialog: T) {
@@ -71,15 +86,7 @@
         updateIcon()
         createOptionsView(getOptionsViewLayoutId())
         if (!::viewBinder.isInitialized) {
-            viewBinder =
-                BaseMediaProjectionPermissionViewBinder(
-                    screenShareOptions,
-                    appName,
-                    hostUid,
-                    mediaProjectionMetricsLogger,
-                    defaultSelectedMode,
-                    dialog,
-                )
+            viewBinder = createViewBinder()
         }
         viewBinder.bind()
         initScreenShareSpinner()
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
index 70b25f5..728255d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -61,7 +61,7 @@
         startButton.text = startButtonText
     }
 
-    fun onItemSelected(pos: Int) {
+    open fun onItemSelected(pos: Int) {
         selectedScreenShareOption = screenShareOptions[pos]
         setOptionSpecificFields()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index d9cc0c8..1da4c1d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -21,7 +21,6 @@
 import android.content.Context
 import android.content.Intent
 import android.hardware.display.DisplayManager
-import android.os.Build
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
@@ -30,8 +29,6 @@
 import android.view.Display
 import android.view.MotionEvent.ACTION_MOVE
 import android.view.View
-import android.view.View.GONE
-import android.view.View.VISIBLE
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.AdapterView
 import android.widget.ArrayAdapter
@@ -44,10 +41,10 @@
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionDialogDelegate
+import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionViewBinder
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
 import com.android.systemui.mediaprojection.permission.SINGLE_APP
 import com.android.systemui.mediaprojection.permission.ScreenShareMode
-import com.android.systemui.mediaprojection.permission.ScreenShareOption
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserContextProvider
@@ -64,15 +61,15 @@
     private val activityStarter: ActivityStarter,
     private val userContextProvider: UserContextProvider,
     private val onStartRecordingClicked: Runnable?,
-    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+    private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
     private val systemUIDialogFactory: SystemUIDialog.Factory,
     @ScreenShareMode defaultSelectedMode: Int,
     @StyleRes private val theme: Int,
     private val context: Context,
-    displayManager: DisplayManager,
+    private val displayManager: DisplayManager,
 ) :
     BaseMediaProjectionPermissionDialogDelegate<SystemUIDialog>(
-        createOptionList(displayManager),
+        ScreenRecordPermissionViewBinder.createOptionList(displayManager),
         appName = null,
         hostUid = hostUid,
         mediaProjectionMetricsLogger,
@@ -119,10 +116,19 @@
     }
 
     private lateinit var tapsSwitch: Switch
-    private lateinit var tapsView: View
     private lateinit var audioSwitch: Switch
     private lateinit var options: Spinner
 
+    override fun createViewBinder(): BaseMediaProjectionPermissionViewBinder {
+        return ScreenRecordPermissionViewBinder(
+            hostUid,
+            mediaProjectionMetricsLogger,
+            defaultSelectedMode,
+            displayManager,
+            dialog,
+        )
+    }
+
     override fun createDialog(): SystemUIDialog {
         return systemUIDialogFactory.create(this, context, theme)
     }
@@ -169,6 +175,7 @@
 
     @SuppressLint("ClickableViewAccessibility")
     private fun initRecordOptionsView() {
+        // TODO(b/378514312): Move this function to ScreenRecordPermissionViewBinder
         audioSwitch = dialog.requireViewById(R.id.screenrecord_audio_switch)
         tapsSwitch = dialog.requireViewById(R.id.screenrecord_taps_switch)
 
@@ -177,9 +184,6 @@
         audioSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
         tapsSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
 
-        tapsView = dialog.requireViewById(R.id.show_taps)
-        updateTapsViewVisibility()
-
         options = dialog.requireViewById(R.id.screen_recording_options)
         val a: ArrayAdapter<*> =
             ScreenRecordingAdapter(
@@ -207,16 +211,6 @@
         options.isLongClickable = false
     }
 
-    override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
-        super.onItemSelected(adapterView, view, pos, id)
-        updateTapsViewVisibility()
-    }
-
-    private fun updateTapsViewVisibility() {
-        tapsView.visibility =
-            if (getSelectedScreenShareOption().mode == SINGLE_APP) GONE else VISIBLE
-    }
-
     /**
      * Starts screen capture after some countdown
      *
@@ -281,81 +275,5 @@
             )
         private const val DELAY_MS: Long = 3000
         private const val INTERVAL_MS: Long = 1000
-
-        private val RECORDABLE_DISPLAY_TYPES =
-            intArrayOf(
-                Display.TYPE_OVERLAY,
-                Display.TYPE_EXTERNAL,
-                Display.TYPE_INTERNAL,
-                Display.TYPE_WIFI,
-            )
-
-        private val filterDeviceTypeFlag: Boolean =
-            com.android.media.projection.flags.Flags
-                .mediaProjectionConnectedDisplayNoVirtualDevice()
-
-        private fun createOptionList(displayManager: DisplayManager): List<ScreenShareOption> {
-            if (!com.android.media.projection.flags.Flags.mediaProjectionConnectedDisplay()) {
-                return listOf(
-                    ScreenShareOption(
-                        SINGLE_APP,
-                        R.string.screenrecord_permission_dialog_option_text_single_app,
-                        R.string.screenrecord_permission_dialog_warning_single_app,
-                        startButtonText =
-                            R.string
-                                .media_projection_entry_generic_permission_dialog_continue_single_app,
-                    ),
-                    ScreenShareOption(
-                        ENTIRE_SCREEN,
-                        R.string.screenrecord_permission_dialog_option_text_entire_screen,
-                        R.string.screenrecord_permission_dialog_warning_entire_screen,
-                        startButtonText =
-                            R.string.screenrecord_permission_dialog_continue_entire_screen,
-                        displayId = Display.DEFAULT_DISPLAY,
-                        displayName = Build.MODEL,
-                    ),
-                )
-            }
-
-            return listOf(
-                ScreenShareOption(
-                    SINGLE_APP,
-                    R.string.screenrecord_permission_dialog_option_text_single_app,
-                    R.string.screenrecord_permission_dialog_warning_single_app,
-                    startButtonText =
-                        R.string
-                            .media_projection_entry_generic_permission_dialog_continue_single_app,
-                ),
-                ScreenShareOption(
-                    ENTIRE_SCREEN,
-                    R.string.screenrecord_permission_dialog_option_text_entire_screen_for_display,
-                    R.string.screenrecord_permission_dialog_warning_entire_screen,
-                    startButtonText =
-                        R.string.screenrecord_permission_dialog_continue_entire_screen,
-                    displayId = Display.DEFAULT_DISPLAY,
-                    displayName = Build.MODEL,
-                ),
-            ) +
-                displayManager.displays
-                    .filter {
-                        it.displayId != Display.DEFAULT_DISPLAY &&
-                            (!filterDeviceTypeFlag || it.type in RECORDABLE_DISPLAY_TYPES)
-                    }
-                    .map {
-                        ScreenShareOption(
-                            ENTIRE_SCREEN,
-                            R.string
-                                .screenrecord_permission_dialog_option_text_entire_screen_for_display,
-                            warningText =
-                                R.string
-                                    .media_projection_entry_app_permission_dialog_warning_entire_screen,
-                            startButtonText =
-                                R.string
-                                    .media_projection_entry_app_permission_dialog_continue_entire_screen,
-                            displayId = it.displayId,
-                            displayName = it.name,
-                        )
-                    }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
new file mode 100644
index 0000000..91c6b47
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.screenrecord
+
+import android.annotation.SuppressLint
+import android.app.AlertDialog
+import android.hardware.display.DisplayManager
+import android.os.Build
+import android.view.Display
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionViewBinder
+import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
+import com.android.systemui.mediaprojection.permission.SINGLE_APP
+import com.android.systemui.mediaprojection.permission.ScreenShareMode
+import com.android.systemui.mediaprojection.permission.ScreenShareOption
+import com.android.systemui.res.R
+
+class ScreenRecordPermissionViewBinder(
+    hostUid: Int,
+    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+    @ScreenShareMode defaultSelectedMode: Int,
+    displayManager: DisplayManager,
+    private val dialog: AlertDialog,
+) :
+    BaseMediaProjectionPermissionViewBinder(
+        createOptionList(displayManager),
+        appName = null,
+        hostUid = hostUid,
+        mediaProjectionMetricsLogger,
+        defaultSelectedMode,
+        dialog,
+    ) {
+    private lateinit var tapsView: View
+
+    override fun bind() {
+        super.bind()
+        initRecordOptionsView()
+    }
+
+    @SuppressLint("ClickableViewAccessibility")
+    private fun initRecordOptionsView() {
+        tapsView = dialog.requireViewById(R.id.show_taps)
+        updateTapsViewVisibility()
+    }
+
+    override fun onItemSelected(pos: Int) {
+        super.onItemSelected(pos)
+        updateTapsViewVisibility()
+    }
+
+    private fun updateTapsViewVisibility() {
+        tapsView.visibility = if (selectedScreenShareOption.mode == SINGLE_APP) GONE else VISIBLE
+    }
+
+    companion object {
+        private val RECORDABLE_DISPLAY_TYPES =
+            intArrayOf(
+                Display.TYPE_OVERLAY,
+                Display.TYPE_EXTERNAL,
+                Display.TYPE_INTERNAL,
+                Display.TYPE_WIFI,
+            )
+
+        private val filterDeviceTypeFlag: Boolean =
+            com.android.media.projection.flags.Flags
+                .mediaProjectionConnectedDisplayNoVirtualDevice()
+
+        fun createOptionList(displayManager: DisplayManager): List<ScreenShareOption> {
+            if (!com.android.media.projection.flags.Flags.mediaProjectionConnectedDisplay()) {
+                return listOf(
+                    ScreenShareOption(
+                        SINGLE_APP,
+                        R.string.screenrecord_permission_dialog_option_text_single_app,
+                        R.string.screenrecord_permission_dialog_warning_single_app,
+                        startButtonText =
+                            R.string
+                                .media_projection_entry_generic_permission_dialog_continue_single_app,
+                    ),
+                    ScreenShareOption(
+                        ENTIRE_SCREEN,
+                        R.string.screenrecord_permission_dialog_option_text_entire_screen,
+                        R.string.screenrecord_permission_dialog_warning_entire_screen,
+                        startButtonText =
+                            R.string.screenrecord_permission_dialog_continue_entire_screen,
+                        displayId = Display.DEFAULT_DISPLAY,
+                        displayName = Build.MODEL,
+                    ),
+                )
+            }
+
+            return listOf(
+                ScreenShareOption(
+                    SINGLE_APP,
+                    R.string.screenrecord_permission_dialog_option_text_single_app,
+                    R.string.screenrecord_permission_dialog_warning_single_app,
+                    startButtonText =
+                        R.string
+                            .media_projection_entry_generic_permission_dialog_continue_single_app,
+                ),
+                ScreenShareOption(
+                    ENTIRE_SCREEN,
+                    R.string.screenrecord_permission_dialog_option_text_entire_screen_for_display,
+                    R.string.screenrecord_permission_dialog_warning_entire_screen,
+                    startButtonText =
+                        R.string.screenrecord_permission_dialog_continue_entire_screen,
+                    displayId = Display.DEFAULT_DISPLAY,
+                    displayName = Build.MODEL,
+                ),
+            ) +
+                displayManager.displays
+                    .filter {
+                        it.displayId != Display.DEFAULT_DISPLAY &&
+                            (!filterDeviceTypeFlag || it.type in RECORDABLE_DISPLAY_TYPES)
+                    }
+                    .map {
+                        ScreenShareOption(
+                            ENTIRE_SCREEN,
+                            R.string
+                                .screenrecord_permission_dialog_option_text_entire_screen_for_display,
+                            warningText =
+                                R.string
+                                    .media_projection_entry_app_permission_dialog_warning_entire_screen,
+                            startButtonText =
+                                R.string
+                                    .media_projection_entry_app_permission_dialog_continue_entire_screen,
+                            displayId = it.displayId,
+                            displayName = it.name,
+                        )
+                    }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
index dd1b58c..c6752f8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
@@ -30,8 +30,8 @@
     isDownFromTopEdgeEnabled: Boolean = true,
     requireTwoPointersForTopEdgeForQs: Boolean = false,
 ): Array<Pair<UserAction, UserActionResult>> {
-    val shadeUserActionResult = UserActionResult(Scenes.Shade, isIrreversible = true)
-    val qsSceneUserActionResult = UserActionResult(Scenes.QuickSettings, isIrreversible = true)
+    val shadeUserActionResult = UserActionResult(Scenes.Shade)
+    val qsSceneUserActionResult = UserActionResult(Scenes.QuickSettings)
     return buildList {
             // Swiping down, not from the edge, always goes to shade.
             add(Swipe.Down to shadeUserActionResult)
@@ -53,7 +53,7 @@
 
 /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the split shade. */
 fun splitShadeActions(): Array<Pair<UserAction, UserActionResult>> {
-    val shadeUserActionResult = UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)
+    val shadeUserActionResult = UserActionResult(Scenes.Shade, ToSplitShade)
     return arrayOf(
         // Swiping down, not from the edge, always goes to shade.
         Swipe.Down to shadeUserActionResult,
@@ -66,10 +66,8 @@
 
 /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the dual shade. */
 fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> {
-    val notifShadeUserActionResult =
-        UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)
-    val qsShadeuserActionResult =
-        UserActionResult.ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true)
+    val notifShadeUserActionResult = UserActionResult.ShowOverlay(Overlays.NotificationsShade)
+    val qsShadeuserActionResult = UserActionResult.ShowOverlay(Overlays.QuickSettingsShade)
     return arrayOf(
         Swipe.Down to notifShadeUserActionResult,
         Swipe.Down(fromSource = SceneContainerEdge.TopRight) to qsShadeuserActionResult,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index d71b1b2..c8811fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -79,7 +79,6 @@
 import com.android.internal.widget.CachingIconView;
 import com.android.internal.widget.CallLayout;
 import com.android.systemui.Flags;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.RefactorFlag;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
@@ -130,14 +129,12 @@
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.DumpUtilsKt;
 import com.android.systemui.util.ListenerSet;
-import com.android.systemui.wmshell.BubblesManager;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
@@ -151,7 +148,6 @@
         NotificationFadeAware.FadeOptimizedNotification {
 
     private static final String TAG = "ExpandableNotifRow";
-    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
     private static final boolean DEBUG_ONMEASURE =
             Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
     private static final int MENU_VIEW_INDEX = 0;
@@ -186,12 +182,10 @@
     private LayoutListener mLayoutListener;
     private RowContentBindStage mRowContentBindStage;
     private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-    private Optional<BubblesManager> mBubblesManagerOptional;
     private MetricsLogger mMetricsLogger;
     private NotificationChildrenContainerLogger mChildrenContainerLogger;
     private ColorUpdateLogger mColorUpdateLogger;
     private NotificationDismissibilityProvider mDismissibilityProvider;
-    private FeatureFlags mFeatureFlags;
     private int mIconTransformContentShift;
     private int mMaxHeadsUpHeightBeforeN;
     private int mMaxHeadsUpHeightBeforeP;
@@ -342,7 +336,7 @@
      */
     private boolean mIgnoreLockscreenConstraints;
 
-    private OnClickListener mExpandClickListener = new OnClickListener() {
+    private final OnClickListener mExpandClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
             toggleExpansionState(v, /* shouldLogExpandClickMetric = */true);
@@ -431,13 +425,10 @@
     @Nullable
     private Runnable mOnIntrinsicHeightReachedRunnable;
 
-    private float mTopRoundnessDuringLaunchAnimation;
-    private float mBottomRoundnessDuringLaunchAnimation;
-    private float mSmallRoundness;
+    private final float mSmallRoundness;
 
-    private ListenerSet<DismissButtonTargetVisibilityListener>
-            mDismissButtonTargetVisibilityListeners
-            = new ListenerSet();
+    private final ListenerSet<DismissButtonTargetVisibilityListener>
+            mDismissButtonTargetVisibilityListeners = new ListenerSet<>();
 
     public NotificationContentView[] getLayouts() {
         return Arrays.copyOf(mLayouts, mLayouts.length);
@@ -788,7 +779,7 @@
         if (targetVisible != null) {
             for (DismissButtonTargetVisibilityListener listener :
                     mDismissButtonTargetVisibilityListeners) {
-                listener.onTargetVisibilityChanged(targetVisible.booleanValue());
+                listener.onTargetVisibilityChanged(targetVisible);
             }
         }
 
@@ -2024,7 +2015,6 @@
             StatusBarStateController statusBarStateController,
             PeopleNotificationIdentifier peopleNotificationIdentifier,
             OnUserInteractionCallback onUserInteractionCallback,
-            Optional<BubblesManager> bubblesManagerOptional,
             NotificationGutsManager gutsManager,
             NotificationDismissibilityProvider dismissibilityProvider,
             MetricsLogger metricsLogger,
@@ -2032,7 +2022,6 @@
             ColorUpdateLogger colorUpdateLogger,
             SmartReplyConstants smartReplyConstants,
             SmartReplyController smartReplyController,
-            FeatureFlags featureFlags,
             IStatusBarService statusBarService,
             UiEventLogger uiEventLogger) {
         mEntry = entry;
@@ -2067,13 +2056,11 @@
             );
         }
         mOnUserInteractionCallback = onUserInteractionCallback;
-        mBubblesManagerOptional = bubblesManagerOptional;
         mNotificationGutsManager = gutsManager;
         mMetricsLogger = metricsLogger;
         mChildrenContainerLogger = childrenContainerLogger;
         mColorUpdateLogger = colorUpdateLogger;
         mDismissibilityProvider = dismissibilityProvider;
-        mFeatureFlags = featureFlags;
         setHapticFeedbackEnabled(!Flags.msdlFeedback());
     }
 
@@ -2470,10 +2457,6 @@
                 : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
     }
 
-    public CharSequence getActiveRemoteInputText() {
-        return mPrivateLayout.getActiveRemoteInputText();
-    }
-
     /**
      * Reset the translation with an animation.
      */
@@ -2548,7 +2531,7 @@
             return getTranslationX();
         }
 
-        if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
+        if (mTranslateableViews != null && !mTranslateableViews.isEmpty()) {
             // All of the views in the list should have same translation, just use first one.
             return mTranslateableViews.get(0).getTranslationX();
         }
@@ -2683,7 +2666,7 @@
         int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
         if (mNotificationParent != null) {
             float parentTranslationY = mNotificationParent.getTranslationY();
-            top -= parentTranslationY;
+            top -= (int) parentTranslationY;
             mNotificationParent.setTranslationZ(translationZ);
 
             // When the expanding notification is below its parent, the parent must be clipped
@@ -2710,9 +2693,6 @@
         float absoluteCenterX = getLocationOnScreen()[0] + getWidth() / 2f - getTranslationX();
         setTranslationX(params.getCenterX() - absoluteCenterX);
 
-        final float maxRadius = getMaxRadius();
-        mTopRoundnessDuringLaunchAnimation = params.getTopCornerRadius() / maxRadius;
-        mBottomRoundnessDuringLaunchAnimation = params.getBottomCornerRadius() / maxRadius;
         invalidateOutline();
 
         mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight);
@@ -2922,7 +2902,6 @@
         }
     }
 
-
     @Override
     public int getHeightWithoutLockscreenConstraints() {
         mIgnoreLockscreenConstraints = true;
@@ -3248,7 +3227,7 @@
         notifyHeightChanged(/* needsAnimation= */ false);
     }
 
-    public void setChildrenExpanded(boolean expanded, boolean animate) {
+    public void setChildrenExpanded(boolean expanded) {
         mChildrenExpanded = expanded;
         if (mChildrenContainer != null) {
             mChildrenContainer.setChildrenExpanded(expanded);
@@ -3900,10 +3879,6 @@
         return mEntry.getSbn().getNotification().isMediaNotification();
     }
 
-    public boolean isGroupNotFullyVisible() {
-        return getClipTopAmount() > 0 || getTranslationY() < 0;
-    }
-
     public void setAboveShelf(boolean aboveShelf) {
         boolean wasAboveShelf = isAboveShelf();
         mAboveShelf = aboveShelf;
@@ -4132,10 +4107,6 @@
         mTargetPoint = p;
     }
 
-    public Point getTargetPoint() {
-        return mTargetPoint;
-    }
-
     /** Update the minimum roundness based on current state */
     private void updateBaseRoundness() {
         if (isChildInGroup()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index a150f7f..e06280e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -35,7 +35,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginManager;
@@ -51,6 +51,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.collection.render.NotifViewController;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.dagger.AppName;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
@@ -59,17 +60,14 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
 import com.android.systemui.util.time.SystemClock;
-import com.android.systemui.wmshell.BubblesManager;
 
 import com.google.android.msdl.data.model.MSDLToken;
 import com.google.android.msdl.domain.MSDLPlayer;
 
 import java.util.List;
-import java.util.Optional;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -108,10 +106,9 @@
     private final NotificationGutsManager mNotificationGutsManager;
     private final OnUserInteractionCallback mOnUserInteractionCallback;
     private final FalsingManager mFalsingManager;
-    private final FeatureFlags mFeatureFlags;
+    private final FeatureFlagsClassic mFeatureFlags;
     private final boolean mAllowLongPress;
     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-    private final Optional<BubblesManager> mBubblesManagerOptional;
     private final SmartReplyConstants mSmartReplyConstants;
     private final SmartReplyController mSmartReplyController;
     private final ExpandableNotificationRowDragController mDragController;
@@ -271,9 +268,8 @@
             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
             OnUserInteractionCallback onUserInteractionCallback,
             FalsingManager falsingManager,
-            FeatureFlags featureFlags,
+            FeatureFlagsClassic featureFlags,
             PeopleNotificationIdentifier peopleNotificationIdentifier,
-            Optional<BubblesManager> bubblesManagerOptional,
             NotificationSettingsController settingsController,
             ExpandableNotificationRowDragController dragController,
             NotificationDismissibilityProvider dismissibilityProvider,
@@ -303,7 +299,6 @@
         mAllowLongPress = allowLongPress;
         mFeatureFlags = featureFlags;
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
-        mBubblesManagerOptional = bubblesManagerOptional;
         mSettingsController = settingsController;
         mDragController = dragController;
         mMetricsLogger = metricsLogger;
@@ -340,7 +335,6 @@
                 mStatusBarStateController,
                 mPeopleNotificationIdentifier,
                 mOnUserInteractionCallback,
-                mBubblesManagerOptional,
                 mNotificationGutsManager,
                 mDismissibilityProvider,
                 mMetricsLogger,
@@ -348,7 +342,6 @@
                 mColorUpdateLogger,
                 mSmartReplyConstants,
                 mSmartReplyController,
-                mFeatureFlags,
                 mStatusBarService,
                 mUiEventLogger
         );
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 6293640..c8e18a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1128,7 +1128,7 @@
         final int count = mAttachedChildren.size();
         for (int childIdx = 0; childIdx < count; childIdx++) {
             ExpandableNotificationRow child = mAttachedChildren.get(childIdx);
-            child.setChildrenExpanded(childrenExpanded, false);
+            child.setChildrenExpanded(childrenExpanded);
         }
         updateHeaderTouchability();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c7b3fd7..b9e38ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -99,7 +99,6 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -110,6 +109,8 @@
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -126,7 +127,6 @@
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.Assert;
@@ -6860,7 +6860,7 @@
             mExpandedGroupView = changedRow;
             mNeedsAnimation = true;
         }
-        changedRow.setChildrenExpanded(expanded, animated);
+        changedRow.setChildrenExpanded(expanded);
         onChildHeightChanged(changedRow, false /* needsAnimation */);
 
         runAfterAnimationFinished(new Runnable() {
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 df561ab..192d66c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -113,7 +113,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
 import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -368,7 +368,7 @@
     private TestableLooper mTestableLooper;
 
     private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+    private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
 
     private UserHandle mUser0;
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 2d3f68f..7126933 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.media.controls.util.MediaFeatureFlag
@@ -92,8 +92,6 @@
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.wmshell.BubblesManager
-import java.util.Optional
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
@@ -106,7 +104,7 @@
 class ExpandableNotificationRowBuilder(
     private val context: Context,
     dependency: TestableDependency,
-    private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
+    featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
 ) {
 
     private val mMockLogger: ExpandableNotificationRowLogger
@@ -137,7 +135,7 @@
         featureFlags.setDefault(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE)
         featureFlags.setDefault(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)
 
-        dependency.injectTestDependency(FeatureFlags::class.java, featureFlags)
+        dependency.injectTestDependency(FeatureFlagsClassic::class.java, featureFlags)
         dependency.injectMockDependency(NotificationMediaManager::class.java)
         dependency.injectMockDependency(NotificationShadeWindowController::class.java)
         dependency.injectMockDependency(MediaOutputDialogManager::class.java)
@@ -299,7 +297,7 @@
     }
 
     private fun getNotifRemoteViewsFactoryContainer(
-        featureFlags: FeatureFlags
+        featureFlags: FeatureFlagsClassic
     ): NotifRemoteViewsFactoryContainer {
         return NotifRemoteViewsFactoryContainerImpl(
             featureFlags,
@@ -380,7 +378,6 @@
             mStatusBarStateController,
             mPeopleNotificationIdentifier,
             mOnUserInteractionCallback,
-            Optional.of(Mockito.mock(BubblesManager::class.java, STUB_ONLY)),
             Mockito.mock(NotificationGutsManager::class.java, STUB_ONLY),
             mDismissibilityProvider,
             Mockito.mock(MetricsLogger::class.java, STUB_ONLY),
@@ -388,11 +385,10 @@
             Mockito.mock(ColorUpdateLogger::class.java, STUB_ONLY),
             mSmartReplyConstants,
             mSmartReplyController,
-            featureFlags,
             Mockito.mock(IStatusBarService::class.java, STUB_ONLY),
             Mockito.mock(UiEventLogger::class.java, STUB_ONLY),
         )
-        row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
+        row.setAboveShelfChangedListener {}
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
         inflateAndWait(entry)
         return row
@@ -410,7 +406,7 @@
         private const val PKG = "com.android.systemui"
         private const val UID = 1000
         private val USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser())
-        private val INFLATION_FLAGS =
+        private const val INFLATION_FLAGS =
             FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
         private const val IS_CONVERSATION_FLAG = "test.isConversation"
 
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index a90b693..3025e2e 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -314,8 +314,6 @@
 
     private static final String SERIAL_ID_FILE = "serial_id";
 
-    private static final String SKIP_USER_FACING_PACKAGES = "backup_skip_user_facing_packages";
-
     private final @UserIdInt int mUserId;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final TransportManager mTransportManager;
@@ -3503,40 +3501,6 @@
         }
     }
 
-    /**
-     * We want to skip backup/restore of certain packages if 'backup_skip_user_facing_packages' is
-     * set to true in secure settings. See b/153940088 for details.
-     *
-     * TODO(b/154822946): Remove this logic in the next release.
-     */
-    public List<PackageInfo> filterUserFacingPackages(List<PackageInfo> packages) {
-        if (!shouldSkipUserFacingData()) {
-            return packages;
-        }
-
-        List<PackageInfo> filteredPackages = new ArrayList<>(packages.size());
-        for (PackageInfo packageInfo : packages)  {
-            if (!shouldSkipPackage(packageInfo.packageName)) {
-                filteredPackages.add(packageInfo);
-            } else {
-                Slog.i(TAG, "Will skip backup/restore for " + packageInfo.packageName);
-            }
-        }
-
-        return filteredPackages;
-    }
-
-    @VisibleForTesting
-    public boolean shouldSkipUserFacingData() {
-        return Settings.Secure.getInt(mContext.getContentResolver(), SKIP_USER_FACING_PACKAGES,
-                /* def */ 0) != 0;
-    }
-
-    @VisibleForTesting
-    public boolean shouldSkipPackage(String packageName) {
-        return WALLPAPER_PACKAGE.equals(packageName);
-    }
-
     private void updateStateForTransport(String newTransportName) {
         // Publish the name change
         Settings.Secure.putStringForUser(mContext.getContentResolver(),
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 7994948..990c941 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -272,8 +272,6 @@
             }
         }
 
-        mPackages = backupManagerService.filterUserFacingPackages(mPackages);
-
         Set<String> packageNames = Sets.newHashSet();
         for (PackageInfo pkgInfo : mPackages) {
             packageNames.add(pkgInfo.packageName);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index dad84c8..ec9d340 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -315,8 +315,6 @@
             }
         }
 
-        mAcceptSet = backupManagerService.filterUserFacingPackages(mAcceptSet);
-
         if (MORE_DEBUG) {
             Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
             for (PackageInfo info : mAcceptSet) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 4454dd4..9eba926 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -181,6 +181,7 @@
     private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
     private static final int MSG_CURRENT_USER_CHANGED = 4;
+    private static final int MSG_SYSTEM_READY = 5;
 
     private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
     private static final AdditionalDisplayInputProperties
@@ -564,6 +565,14 @@
         Watchdog.getInstance().addMonitor(this);
     }
 
+    private void onBootPhase(int phase) {
+        // On ActivityManager thread, shift to handler to avoid blocking other system services in
+        // this boot phase.
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
+        }
+    }
+
     // TODO(BT) Pass in parameter for bluetooth system
     public void systemRunning() {
         if (DEBUG) {
@@ -3220,6 +3229,9 @@
                 case MSG_CURRENT_USER_CHANGED:
                     handleCurrentUserChanged((int) msg.obj);
                     break;
+                case MSG_SYSTEM_READY:
+                    systemRunning();
+                    break;
             }
         }
     }
@@ -3424,10 +3436,7 @@
 
         @Override
         public void onBootPhase(int phase) {
-            // Called on ActivityManager thread.
-            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
-                mService.systemRunning();
-            }
+            mService.onBootPhase(phase);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 53389ca..aa215ce 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -24,6 +24,11 @@
 import android.hardware.contexthub.IContextHubEndpoint;
 import android.hardware.contexthub.IContextHubEndpointCallback;
 import android.hardware.location.IContextHubTransactionCallback;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * A class that represents a broker for the endpoint registered by the client app. This class
@@ -31,7 +36,8 @@
  *
  * @hide
  */
-public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub {
+public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+        implements IBinder.DeathRecipient {
     private static final String TAG = "ContextHubEndpointBroker";
 
     /** The context of the service. */
@@ -52,6 +58,9 @@
     /** The remote callback interface for this endpoint. */
     private final IContextHubEndpointCallback mContextHubEndpointCallback;
 
+    /** True if this endpoint is registered with the service. */
+    private AtomicBoolean mIsRegistered = new AtomicBoolean(true);
+
     /* package */ ContextHubEndpointBroker(
             Context context,
             IContextHubWrapper contextHubProxy,
@@ -72,27 +81,55 @@
     }
 
     @Override
-    public int openSession(HubEndpointInfo destination, HubServiceInfo serviceInfo) {
-        // TODO(b/378487799): Implement this
-        return 0;
+    public int openSession(HubEndpointInfo destination, HubServiceInfo serviceInfo)
+            throws RemoteException {
+        ContextHubServiceUtil.checkPermissions(mContext);
+        if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
+        int sessionId = mEndpointManager.reserveSessionId();
+        EndpointInfo halEndpointInfo = ContextHubServiceUtil.convertHalEndpointInfo(destination);
+        try {
+            mContextHubProxy.openEndpointSession(
+                    sessionId,
+                    halEndpointInfo.id,
+                    mHalEndpointInfo.id,
+                    serviceInfo == null ? null : serviceInfo.getServiceDescriptor());
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            Log.e(TAG, "Exception while calling HAL openEndpointSession", e);
+            mEndpointManager.returnSessionId(sessionId);
+            throw e;
+        }
+
+        return sessionId;
     }
 
     @Override
-    public void closeSession(int sessionId, int reason) {
-        // TODO(b/378487799): Implement this
-
+    public void closeSession(int sessionId, int reason) throws RemoteException {
+        ContextHubServiceUtil.checkPermissions(mContext);
+        if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
+        try {
+            mContextHubProxy.closeEndpointSession(sessionId, (byte) reason);
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+            throw e;
+        }
     }
 
     @Override
     public void openSessionRequestComplete(int sessionId) {
         // TODO(b/378487799): Implement this
-
     }
 
     @Override
     public void unregister() {
-        // TODO(b/378487799): Implement this
-
+        ContextHubServiceUtil.checkPermissions(mContext);
+        mIsRegistered.set(false);
+        try {
+            mContextHubProxy.unregisterEndpoint(mHalEndpointInfo);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while calling HAL unregisterEndpoint", e);
+        }
+        // TODO(b/378487799): Release reserved session IDs
+        mEndpointManager.unregisterEndpoint(mEndpointInfo.getIdentifier().getEndpoint());
     }
 
     @Override
@@ -105,4 +142,16 @@
     public void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode) {
         // TODO(b/381102453): Implement this
     }
+
+    /** Invoked when the underlying binder of this broker has died at the client process. */
+    @Override
+    public void binderDied() {
+        unregister();
+    }
+
+    /* package */ void attachDeathRecipient() throws RemoteException {
+        if (mContextHubEndpointCallback != null) {
+            mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 54ce74f..fb64f99 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -22,11 +22,15 @@
 import android.hardware.contexthub.IContextHubEndpoint;
 import android.hardware.contexthub.IContextHubEndpointCallback;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -40,6 +44,12 @@
     /** The hub ID of the Context Hub Service. */
     private static final long SERVICE_HUB_ID = 0x416e64726f696400L;
 
+    /** The range of session IDs to use for endpoints */
+    private static final int SERVICE_SESSION_RANGE = 1024;
+
+    /** The length of the array that should be returned by HAL requestSessionIdRange */
+    private static final int SERVICE_SESSION_RANGE_LENGTH = 2;
+
     /** The context of the service. */
     private final Context mContext;
 
@@ -55,9 +65,57 @@
     @GuardedBy("mEndpointLock")
     private long mNextEndpointId = 0;
 
+    /** The minimum session ID reservable by endpoints (retrieved from HAL) */
+    private final int mMinSessionId;
+
+    /** The minimum session ID reservable by endpoints (retrieved from HAL) */
+    private final int mMaxSessionId;
+
+    /** Variables for managing session ID creation */
+    private final Object mSessionIdLock = new Object();
+
+    /** A set of session IDs that have been reserved by an endpoint. */
+    @GuardedBy("mSessionIdLock")
+    private final Set<Integer> mReservedSessionIds =
+            Collections.newSetFromMap(new HashMap<Integer, Boolean>());
+
+    @GuardedBy("mSessionIdLock")
+    private int mNextSessionId = 0;
+
+    /** Initialized to true if all initialization in the constructor succeeds. */
+    private final boolean mSessionIdsValid;
+
     /* package */ ContextHubEndpointManager(Context context, IContextHubWrapper contextHubProxy) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
+        int[] range = null;
+        try {
+            range = mContextHubProxy.requestSessionIdRange(SERVICE_SESSION_RANGE);
+            if (range != null && range.length < SERVICE_SESSION_RANGE_LENGTH) {
+                Log.e(TAG, "Invalid session ID range: range array size = " + range.length);
+                range = null;
+            }
+        } catch (RemoteException | IllegalArgumentException | ServiceSpecificException e) {
+            Log.e(TAG, "Exception while calling HAL requestSessionIdRange", e);
+        }
+
+        if (range == null) {
+            mMinSessionId = -1;
+            mMaxSessionId = -1;
+            mSessionIdsValid = false;
+        } else {
+            mMinSessionId = range[0];
+            mMaxSessionId = range[1];
+            if (!isSessionIdRangeValid(mMinSessionId, mMaxSessionId)) {
+                Log.e(
+                        TAG,
+                        "Invalid session ID range: max=" + mMaxSessionId + " min=" + mMinSessionId);
+                mSessionIdsValid = false;
+            } else {
+                mNextSessionId = mMinSessionId;
+                mSessionIdsValid = true;
+            }
+        }
     }
 
     /**
@@ -71,6 +129,9 @@
     /* package */ IContextHubEndpoint registerEndpoint(
             HubEndpointInfo pendingEndpointInfo, IContextHubEndpointCallback callback)
             throws RemoteException {
+        if (!mSessionIdsValid) {
+            throw new IllegalStateException("ContextHubEndpointManager failed to initialize");
+        }
         ContextHubEndpointBroker broker;
         long endpointId = getNewEndpointId();
         EndpointInfo halEndpointInfo =
@@ -91,15 +152,65 @@
                         callback);
         mEndpointMap.put(endpointId, broker);
 
-        // TODO(b/378487799): Add death recipient
+        try {
+            broker.attachDeathRecipient();
+        } catch (RemoteException e) {
+            // The client process has died, so we close the connection and return null
+            Log.e(TAG, "Failed to attach death recipient to client", e);
+            broker.unregister();
+            return null;
+        }
 
         Log.d(TAG, "Registered endpoint with ID = " + endpointId);
         return IContextHubEndpoint.Stub.asInterface(broker);
     }
 
     /**
-     * @return an available endpoint ID
+     * Reserves an available session ID for an endpoint.
+     *
+     * @throws IllegalStateException if no session ID was available.
+     * @return The reserved session ID.
      */
+    /* package */ int reserveSessionId() {
+        int id = -1;
+        synchronized (mSessionIdLock) {
+            final int maxCapacity = mMaxSessionId - mMinSessionId + 1;
+            if (mReservedSessionIds.size() >= maxCapacity) {
+                throw new IllegalStateException("Too many sessions reserved");
+            }
+
+            id = mNextSessionId;
+            for (int i = mMinSessionId; i <= mMaxSessionId; i++) {
+                if (!mReservedSessionIds.contains(id)) {
+                    mNextSessionId = (id == mMaxSessionId) ? mMinSessionId : id + 1;
+                    break;
+                }
+
+                id = (id == mMaxSessionId) ? mMinSessionId : id + 1;
+            }
+
+            mReservedSessionIds.add(id);
+        }
+        return id;
+    }
+
+    /** Returns a previously reserved session ID through {@link #reserveSessionId()}. */
+    /* package */ void returnSessionId(int sessionId) {
+        synchronized (mSessionIdLock) {
+            mReservedSessionIds.remove(sessionId);
+        }
+    }
+
+    /**
+     * Unregisters an endpoint given its ID.
+     *
+     * @param endpointId The ID of the endpoint to unregister.
+     */
+    /* package */ void unregisterEndpoint(long endpointId) {
+        mEndpointMap.remove(endpointId);
+    }
+
+    /** @return an available endpoint ID */
     private long getNewEndpointId() {
         synchronized (mEndpointLock) {
             if (mNextEndpointId == Long.MAX_VALUE) {
@@ -108,4 +219,8 @@
             return mNextEndpointId++;
         }
     }
+
+    private boolean isSessionIdRangeValid(int minId, int maxId) {
+        return (minId <= maxId) && (minId >= 0) && (maxId >= 0);
+    }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 14d75b0..c79dc84 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.chre.flags.Flags;
+import android.hardware.contexthub.EndpointId;
 import android.hardware.contexthub.HostEndpointInfo;
 import android.hardware.contexthub.HubEndpointInfo;
 import android.hardware.contexthub.MessageDeliveryStatus;
@@ -243,6 +244,23 @@
     public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
             throws RemoteException {}
 
+    /** Unregisters a previously registered endpoint */
+    public int[] requestSessionIdRange(int size) throws RemoteException {
+        return null;
+    }
+
+    /** Opens an endpoint session between two endpoints */
+    public void openEndpointSession(
+            int sessionId, EndpointId destination, EndpointId initiator, String serviceDescriptor)
+            throws RemoteException {}
+
+    /** Closes a previously opened endpoint */
+    public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {}
+
+    /** Unregisters a previously registered endpoint */
+    public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
+            throws RemoteException {}
+
     /**
      * @return True if this version of the Contexthub HAL supports Location setting notifications.
      */
@@ -685,6 +703,48 @@
             hub.registerEndpoint(info);
         }
 
+        @Override
+        public int[] requestSessionIdRange(int size) throws RemoteException {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return null;
+            }
+            return hub.requestSessionIdRange(size);
+        }
+
+        @Override
+        public void openEndpointSession(
+                int sessionId,
+                EndpointId destination,
+                EndpointId initiator,
+                String serviceDescriptor)
+                throws RemoteException {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return;
+            }
+            hub.openEndpointSession(sessionId, destination, initiator, serviceDescriptor);
+        }
+
+        @Override
+        public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return;
+            }
+            hub.closeEndpointSession(sessionId, reason);
+        }
+
+        @Override
+        public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
+                throws RemoteException {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return;
+            }
+            hub.unregisterEndpoint(info);
+        }
+
         public boolean supportsLocationSettingNotifications() {
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 20481f2..1fc609b7 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1429,7 +1429,16 @@
         // Commit wallpaper visibility after activity, because usually the wallpaper target token is
         // an activity, and wallpaper's visibility depends on activity's visibility.
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
-            final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
+            final WindowContainer<?> wc = mParticipants.valueAt(i);
+            WallpaperWindowToken wt = wc.asWallpaperToken();
+            if (!Flags.ensureWallpaperInTransitions()) {
+                if (wt == null) {
+                    final WindowState windowState = wc.asWindowState();
+                    if (windowState != null) {
+                        wt = windowState.mToken.asWallpaperToken();
+                    }
+                }
+            }
             if (wt == null) continue;
             final WindowState target = wt.mDisplayContent.mWallpaperController.getWallpaperTarget();
             final boolean isTargetInvisible = target == null || !target.mToken.isVisible();
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9430194..5a45730 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -362,7 +362,7 @@
     void setMotionClassifierEnabled(bool enabled);
     std::optional<std::string> getBluetoothAddress(int32_t deviceId);
     void setStylusButtonMotionEventsEnabled(bool enabled);
-    FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId);
+    vec2 getMouseCursorPosition(ui::LogicalDisplayId displayId);
     void setStylusPointerIconEnabled(bool enabled);
     void setInputMethodConnectionIsActive(bool isActive);
     void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping);
@@ -441,7 +441,7 @@
     std::shared_ptr<PointerControllerInterface> createPointerController(
             PointerControllerInterface::ControllerType type) override;
     void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
-                                       const FloatPoint& position) override;
+                                       const vec2& position) override;
     void notifyMouseCursorFadedOnTyping() override;
 
     /* --- InputFilterPolicyInterface implementation --- */
@@ -871,7 +871,7 @@
 }
 
 void NativeInputManager::notifyPointerDisplayIdChanged(ui::LogicalDisplayId pointerDisplayId,
-                                                       const FloatPoint& position) {
+                                                       const vec2& position) {
     // Notify the Reader so that devices can be reconfigured.
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -2023,7 +2023,7 @@
             InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
 }
 
-FloatPoint NativeInputManager::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
+vec2 NativeInputManager::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
     return mInputManager->getChoreographer().getMouseCursorPosition(displayId);
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ac1219c..3ccde06 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9085,11 +9085,13 @@
         }
         CallerIdentity caller = getCallerIdentity(who);
 
-        if (!Flags.setAutoTimeEnabledCoexistence()) {
+        if (Flags.setAutoTimeEnabledCoexistence()) {
+            Preconditions.checkCallAuthorization(hasPermission(SET_TIME, caller.getPackageName()));
+        } else {
             Objects.requireNonNull(who, "ComponentName is null");
-        }
-        Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+            Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
                 || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
+        }
 
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
     }
@@ -9166,10 +9168,7 @@
         }
 
         CallerIdentity caller = getCallerIdentity(who);
-
-        if (!Flags.setAutoTimeZoneEnabledCoexistence()) {
-            Objects.requireNonNull(who, "ComponentName is null");
-        }
+        Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
                 || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
                 caller));
@@ -9193,10 +9192,15 @@
         }
 
         CallerIdentity caller = getCallerIdentity(who);
-        Objects.requireNonNull(who, "ComponentName is null");
-        Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
+            Preconditions.checkCallAuthorization(
+                hasPermission(SET_TIME_ZONE, caller.getPackageName()));
+        } else {
+            Objects.requireNonNull(who, "ComponentName is null");
+            Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
                 || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
                 caller));
+        }
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
     }
 
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 02e0bbf..eb61a40 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -30,13 +30,10 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.expectThrows;
 
 import android.app.backup.BackupManager;
@@ -90,7 +87,6 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -110,7 +106,6 @@
     private static final String TAG = "BMSTest";
     private static final String PACKAGE_1 = "some.package.1";
     private static final String PACKAGE_2 = "some.package.2";
-    private static final String USER_FACING_PACKAGE = "user.facing.package";
     private static final int USER_ID = 10;
 
     @Mock private TransportManager mTransportManager;
@@ -1213,47 +1208,6 @@
                 eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any());
     }
 
-    @Test
-    public void testFilterUserFacingPackages_shouldSkipUserFacing_filtersUserFacing() {
-        List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
-                getPackageInfo(PACKAGE_1));
-        UserBackupManagerService backupManagerService = spy(
-                createUserBackupManagerServiceAndRunTasks());
-        when(backupManagerService.shouldSkipUserFacingData()).thenReturn(true);
-        when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);
-
-        List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages(
-                packages);
-
-        assertFalse(containsPackage(filteredPackages, USER_FACING_PACKAGE));
-        assertTrue(containsPackage(filteredPackages, PACKAGE_1));
-    }
-
-    @Test
-    public void testFilterUserFacingPackages_shouldNotSkipUserFacing_doesNotFilterUserFacing() {
-        List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
-                getPackageInfo(PACKAGE_1));
-        UserBackupManagerService backupManagerService = spy(
-                createUserBackupManagerServiceAndRunTasks());
-        when(backupManagerService.shouldSkipUserFacingData()).thenReturn(false);
-        when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);
-
-        List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages(
-                packages);
-
-        assertTrue(containsPackage(filteredPackages, USER_FACING_PACKAGE));
-        assertTrue(containsPackage(filteredPackages, PACKAGE_1));
-    }
-
-    private static boolean containsPackage(List<PackageInfo> packages, String targetPackage) {
-        for (PackageInfo packageInfo : packages) {
-            if (targetPackage.equals(packageInfo.packageName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
         return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
                 USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index d7b60cf..2b15231 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -39,7 +39,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.connectivity.WifiActivityEnergyInfo;
-import android.platform.test.ravenwood.RavenwoodConfig;
 import android.power.PowerStatsInternal;
 import android.util.IntArray;
 import android.util.SparseArray;
@@ -66,9 +65,6 @@
 @SuppressWarnings("GuardedBy")
 @android.platform.test.annotations.DisabledOnRavenwood
 public class BatteryExternalStatsWorkerTest {
-    @RavenwoodConfig.Config
-    public final RavenwoodConfig mRavenwood = new RavenwoodConfig.Builder().build();
-
     private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
     private TestPowerStatsInternal mPowerStatsInternal;
     private Handler mHandler;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
index f64dc08..ed3cda0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
@@ -34,7 +34,6 @@
 import android.os.BatteryConsumer;
 import android.os.PersistableBundle;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodConfig;
 
 import com.android.internal.os.BatteryStatsHistory;
 import com.android.internal.os.MonotonicClock;
@@ -48,11 +47,6 @@
 import org.junit.Test;
 
 public class WakelockPowerStatsProcessorTest {
-    @RavenwoodConfig.Config
-    public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CPU_IDLE, 720);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7cfdec6..59cb5ff 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10061,6 +10061,13 @@
             "satellite_nidd_apn_name_string";
 
     /**
+     * The display name that will be used for satellite functionality within the UI.
+     * The default string value for this is "Satellite".
+     * @hide
+     */
+    public static final String KEY_SATELLITE_DISPLAY_NAME_STRING = "satellite_display_name_string";
+
+    /**
      * Default value {@code true}, meaning when an emergency call request comes in, if the device is
      * in emergency satellite mode but hasn't sent the first satellite datagram, then exits
      * satellite mode to allow the emergency call to go through.
@@ -11343,6 +11350,7 @@
         sDefaults.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, false);
         sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
                 (int) TimeUnit.SECONDS.toMillis(30));
+        sDefaults.putString(KEY_SATELLITE_DISPLAY_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, false);
         sDefaults.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index db5689b7..1025c15 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -292,6 +292,13 @@
 
     /**
      * Bundle key to get the response from
+     * {@link #requestSatelliteDisplayName(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_SATELLITE_DISPLAY_NAME = "satellite_display_name";
+
+    /**
+     * Bundle key to get the response from
      * {@link #requestSelectedNbIotSatelliteSubscriptionId(Executor, OutcomeReceiver)}.
      * @hide
      */
@@ -3594,6 +3601,65 @@
     }
 
     /**
+     * Request to get the display name of satellite feature in the UI.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return display name of the satellite feature in string format. Defaults
+     *                 to satellite. If the request is not successful,
+     *                 {@link OutcomeReceiver#onError(Throwable)} will return an error with
+     *                 a SatelliteException.
+     *
+     * @throws SecurityException     if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void requestSatelliteDisplayName(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<String, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_DISPLAY_NAME)) {
+                                String satelliteDisplayName =
+                                        resultData.getString(KEY_SATELLITE_DISPLAY_NAME);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(satelliteDisplayName)));
+                            } else {
+                                loge("KEY_SATELLITE_DISPLAY_NAME does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestSatelliteDisplayName(receiver);
+            } else {
+                loge("requestSatelliteDisplayName() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestSatelliteDisplayName() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
      * Deliver the list of provisioned satellite subscriber infos.
      *
      * @param list The list of provisioned satellite subscriber infos.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 131f46b..da7669f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3471,6 +3471,17 @@
     void requestSatelliteSubscriberProvisionStatus(in ResultReceiver result);
 
     /**
+     * Request to get the name to display for Satellite subscription.
+     *
+     * @param receiver The result receiver that returns the diplay name to be used for the satellite
+     * subscription.
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestSatelliteDisplayName(in ResultReceiver receiver);
+
+    /**
      * Deliver the list of provisioned satellite subscriber infos.
      *
      * @param list The list of provisioned satellite subscriber infos.