Merge "Add KB BL user inactivity time to config" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index ad84900..6e37b7e 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -72,6 +72,7 @@
         "android.service.dreams.flags-aconfig-java",
         "android.service.notification.flags-aconfig-java",
         "android.service.appprediction.flags-aconfig-java",
+        "android.service.quickaccesswallet.flags-aconfig-java",
         "android.service.voice.flags-aconfig-java",
         "android.speech.flags-aconfig-java",
         "android.systemserver.flags-aconfig-java",
@@ -1774,3 +1775,18 @@
     aconfig_declarations: "aconfig_settingslib_flags",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Quick Access Wallet
+aconfig_declarations {
+    name: "android.service.quickaccesswallet.flags-aconfig",
+    package: "android.service.quickaccesswallet",
+    exportable: true,
+    container: "system",
+    srcs: ["core/java/android/service/quickaccesswallet/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.service.quickaccesswallet.flags-aconfig-java",
+    aconfig_declarations: "android.service.quickaccesswallet.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 62c8a34..ed95fdd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1378,7 +1378,8 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
     method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
-    method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+    method @Deprecated @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+    method @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(boolean, @Nullable android.os.PersistableBundle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification();
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
@@ -12552,8 +12553,19 @@
   }
 
   @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
+    method @NonNull public android.content.Intent createSupportIntent(@NonNull String, @Nullable String);
     method @NonNull @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures();
     method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean);
+    field @FlaggedApi("android.security.aapm_api") public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
+    field public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
+    field public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
+    field public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = "android.security.advancedprotection.feature_disallow_2g";
+    field public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = "android.security.advancedprotection.feature_disallow_install_unknown_sources";
+    field public static final String FEATURE_ID_DISALLOW_USB = "android.security.advancedprotection.feature_disallow_usb";
+    field public static final String FEATURE_ID_DISALLOW_WEP = "android.security.advancedprotection.feature_disallow_wep";
+    field public static final String FEATURE_ID_ENABLE_MTE = "android.security.advancedprotection.feature_enable_mte";
+    field public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = "android.security.advancedprotection.type_blocked_interaction";
+    field public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = "android.security.advancedprotection.type_disabled_setting";
   }
 
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0381ee0..3d9c55c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5003,7 +5003,7 @@
 
         /**
          * Sets a very short string summarizing the most critical information contained in the
-         * notification. Suggested max length is 5 characters, and there is no guarantee how much or
+         * notification. Suggested max length is 7 characters, and there is no guarantee how much or
          * how little of this text will be shown.
          */
         @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a458b4e..f702b85 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -174,22 +174,54 @@
     }
 
     /**
-     * Apply the registered library paths to the passed impl object
-     * @return the hash code for the current version of the registered paths
+     * Apply the registered library paths to the passed AssetManager. If may create a new
+     * AssetManager if any changes are needed and it isn't allowed to reuse the old one.
+     *
+     * @return new AssetManager and the hash code for the current version of the registered paths
      */
-    public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) {
+    public @NonNull Pair<AssetManager, Integer> updateResourceImplAssetsWithRegisteredLibs(
+            @NonNull AssetManager assets, boolean reuseAssets) {
         if (!Flags.registerResourcePaths()) {
-            return 0;
+            return new Pair<>(assets, 0);
         }
 
-        final var collector = new PathCollector(null);
-        final int size = mSharedLibAssetsMap.size();
-        for (int i = 0; i < size; i++) {
-            final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
-            collector.appendKey(libraryKey);
+        final int size;
+        final PathCollector collector;
+
+        synchronized (mLock) {
+            size = mSharedLibAssetsMap.size();
+            if (assets == AssetManager.getSystem()) {
+                return new Pair<>(assets, size);
+            }
+            collector = new PathCollector(resourcesKeyFromAssets(assets));
+            for (int i = 0; i < size; i++) {
+                final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
+                collector.appendKey(libraryKey);
+            }
         }
-        impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey()));
-        return size;
+        if (collector.isSameAsOriginal()) {
+            return new Pair<>(assets, size);
+        }
+        if (reuseAssets) {
+            assets.addPresetApkKeys(extractApkKeys(collector.collectedKey()));
+            return new Pair<>(assets, size);
+        }
+        final var newAssetsBuilder = new AssetManager.Builder();
+        for (final var asset : assets.getApkAssets()) {
+            if (!asset.isForLoader()) {
+                newAssetsBuilder.addApkAssets(asset);
+            }
+        }
+        for (final var key : extractApkKeys(collector.collectedKey())) {
+            try {
+                final var asset = loadApkAssets(key);
+                newAssetsBuilder.addApkAssets(asset);
+            } catch (IOException e) {
+                Log.e(TAG, "Couldn't load assets for key " + key, e);
+            }
+        }
+        assets.getLoaders().forEach(newAssetsBuilder::addLoader);
+        return new Pair<>(newAssetsBuilder.build(), size);
     }
 
     public static class ApkKey {
@@ -624,6 +656,23 @@
         return apkKeys;
     }
 
+    private ResourcesKey resourcesKeyFromAssets(@NonNull AssetManager assets) {
+        final var libs = new ArrayList<String>();
+        final var overlays = new ArrayList<String>();
+        for (final ApkAssets asset : assets.getApkAssets()) {
+            if (asset.isSystem() || asset.isForLoader()) {
+                continue;
+            }
+            if (asset.isOverlay()) {
+                overlays.add(asset.getAssetPath());
+            } else if (asset.isSharedLib()) {
+                libs.add(asset.getAssetPath());
+            }
+        }
+        return new ResourcesKey(null, null, overlays.toArray(new String[0]),
+                libs.toArray(new String[0]), 0, null, null);
+    }
+
     /**
      * Creates an AssetManager from the paths within the ResourcesKey.
      *
@@ -752,7 +801,7 @@
 
         final Configuration config = generateConfig(key);
         final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
-        final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
+        final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true);
 
         if (DEBUG) {
             Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
@@ -1835,31 +1884,32 @@
         for (int i = 0; i < resourcesCount; i++) {
             final WeakReference<Resources> ref = mAllResourceReferences.get(i);
             final Resources r = ref != null ? ref.get() : null;
-            if (r != null) {
-                final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
-                if (key != null) {
-                    final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
-                    if (impl == null) {
-                        throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
-                    }
-                    r.setImpl(impl);
-                } else {
-                    // ResourcesKey is null which means the ResourcesImpl could belong to a
-                    // Resources created by application through Resources constructor and was not
-                    // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
-                    // have shared library asset paths appended if there are any.
-                    if (r.getImpl() != null) {
-                        final ResourcesImpl oldImpl = r.getImpl();
-                        final AssetManager oldAssets = oldImpl.getAssets();
-                        // ResourcesImpl constructor will help to append shared library asset paths.
-                        if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) {
-                            final ResourcesImpl newImpl = new ResourcesImpl(oldAssets,
-                                    oldImpl.getMetrics(), oldImpl.getConfiguration(),
-                                    oldImpl.getDisplayAdjustments());
+            if (r == null) {
+                continue;
+            }
+            final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+            if (key != null) {
+                final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+                if (impl == null) {
+                    throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
+                }
+                r.setImpl(impl);
+            } else {
+                // ResourcesKey is null which means the ResourcesImpl could belong to a
+                // Resources created by application through Resources constructor and was not
+                // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
+                // have shared library asset paths appended if there are any.
+                final ResourcesImpl oldImpl = r.getImpl();
+                if (oldImpl != null) {
+                    final AssetManager oldAssets = oldImpl.getAssets();
+                    // ResourcesImpl constructor will help to append shared library asset paths.
+                    if (oldAssets != AssetManager.getSystem()) {
+                        if (oldAssets.isUpToDate()) {
+                            final ResourcesImpl newImpl = new ResourcesImpl(oldImpl);
                             r.setImpl(newImpl);
                         } else {
-                            Slog.w(TAG, "Skip appending shared library asset paths for the "
-                                    + "Resource as its assets are not up to date.");
+                            Slog.w(TAG, "Skip appending shared library asset paths for "
+                                    + "the Resources as its assets are not up to date.");
                         }
                     }
                 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6939bb6..bff77f9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -58,6 +58,7 @@
 import static android.app.admin.flags.Flags.FLAG_REMOVE_MANAGED_PROFILE_ENABLED;
 import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
 import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
+import static android.app.admin.flags.Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED;
 import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
 import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -12642,28 +12643,43 @@
      * @param enabled Whether or not the lockscreen needs to be shown.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      * @see #isSecondaryLockscreenEnabled
+     * @deprecated Use {@link #setSecondaryLockscreenEnabled(boolean,PersistableBundle)} instead.
      * @hide
-     **/
+     */
+    @Deprecated
     @SystemApi
+    @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
     public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) {
-        setSecondaryLockscreenEnabled(admin, enabled, null);
+        throwIfParentInstance("setSecondaryLockscreenEnabled");
+        if (mService != null) {
+            try {
+                mService.setSecondaryLockscreenEnabled(admin, enabled, null);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
      * Called by the system supervision app to set whether a secondary lockscreen needs to be shown.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
-     *              caller is not a device admin.
+     * <p>The secondary lockscreen will by displayed after the primary keyguard security screen
+     * requirements are met.
+     *
+     * <p>This API, and associated APIs, can only be called by the default supervision app.
+     *
      * @param enabled Whether or not the lockscreen needs to be shown.
      * @param options A {@link PersistableBundle} to supply options to the lock screen.
      * @hide
      */
-    public void setSecondaryLockscreenEnabled(@Nullable ComponentName admin, boolean enabled,
+    @SystemApi
+    @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
+    public void setSecondaryLockscreenEnabled(boolean enabled,
             @Nullable PersistableBundle options) {
         throwIfParentInstance("setSecondaryLockscreenEnabled");
         if (mService != null) {
             try {
-                mService.setSecondaryLockscreenEnabled(admin, enabled, options);
+                mService.setSecondaryLockscreenEnabled(null, enabled, options);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 5f868be..404471e 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -381,3 +381,11 @@
   description: "Split up existing create and provision managed profile API."
   bug: "375382324"
 }
+
+flag {
+  name: "secondary_lockscreen_api_enabled"
+  is_exported: true
+  namespace: "enterprise"
+  description: "Add new API for secondary lockscreen"
+  bug: "336297680"
+}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 68b5d78..908999b 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -124,11 +124,13 @@
 
     @Nullable
     @GuardedBy("this")
-    private final StringBlock mStringBlock;  // null or closed if mNativePtr = 0.
+    private StringBlock mStringBlock;  // null or closed if mNativePtr = 0.
 
     @PropertyFlags
     private final int mFlags;
 
+    private final boolean mIsOverlay;
+
     @Nullable
     private final AssetsProvider mAssets;
 
@@ -302,40 +304,43 @@
 
     private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
             @Nullable AssetsProvider assets) throws IOException {
+        this(format, flags, assets);
         Objects.requireNonNull(path, "path");
-        mFlags = flags;
         mNativePtr = nativeLoad(format, path, flags, assets);
         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
-        mAssets = assets;
     }
 
     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
             @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
             throws IOException {
+        this(format, flags, assets);
         Objects.requireNonNull(fd, "fd");
         Objects.requireNonNull(friendlyName, "friendlyName");
-        mFlags = flags;
         mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
-        mAssets = assets;
     }
 
     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
             @Nullable AssetsProvider assets) throws IOException {
+        this(format, flags, assets);
         Objects.requireNonNull(fd, "fd");
         Objects.requireNonNull(friendlyName, "friendlyName");
-        mFlags = flags;
         mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
-        mAssets = assets;
     }
 
     private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
-        mFlags = flags;
+        this(FORMAT_APK, flags, assets);
         mNativePtr = nativeLoadEmpty(flags, assets);
         mStringBlock = null;
+    }
+
+    private ApkAssets(@FormatType int format, @PropertyFlags int flags,
+            @Nullable AssetsProvider assets) {
+        mFlags = flags;
         mAssets = assets;
+        mIsOverlay = format == FORMAT_IDMAP;
     }
 
     @UnsupportedAppUsage
@@ -425,6 +430,18 @@
         }
     }
 
+    public boolean isSystem() {
+        return (mFlags & PROPERTY_SYSTEM) != 0;
+    }
+
+    public boolean isSharedLib() {
+        return (mFlags & PROPERTY_DYNAMIC) != 0;
+    }
+
+    public boolean isOverlay() {
+        return mIsOverlay;
+    }
+
     @Override
     public String toString() {
         return "ApkAssets{path=" + getDebugName() + "}";
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index e6b9342..bcaceb2 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -203,9 +203,25 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
             @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
-        mAssets = assets;
-        mAppliedSharedLibsHash =
-                ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this);
+        // Don't reuse assets by default as we have no control over whether they're already
+        // inside some other ResourcesImpl.
+        this(assets, metrics, config, displayAdjustments, false);
+    }
+
+    public ResourcesImpl(@NonNull ResourcesImpl orig) {
+        // We know for sure that the other assets are in use, so can't reuse the object here.
+        this(orig.getAssets(), orig.getMetrics(), orig.getConfiguration(),
+                orig.getDisplayAdjustments(), false);
+    }
+
+    public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
+            @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments,
+            boolean reuseAssets) {
+        final var assetsAndHash =
+                ResourcesManager.getInstance().updateResourceImplAssetsWithRegisteredLibs(assets,
+                        reuseAssets);
+        mAssets = assetsAndHash.first;
+        mAppliedSharedLibsHash = assetsAndHash.second;
         mMetrics.setToDefaults();
         mDisplayAdjustments = displayAdjustments;
         mConfiguration.setToDefaults();
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index d243575..6c35d10 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -3,6 +3,16 @@
 
 flag {
     namespace: "credential_manager"
+    name: "ttl_fix_enabled"
+    description: "Enable fix for transaction too large issue"
+    bug: "371052524"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    namespace: "credential_manager"
     name: "settings_activity_enabled"
     is_exported: true
     description: "Enable the Credential Manager Settings Activity APIs"
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 6f3e3d8..9fe0dda 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -16,20 +16,30 @@
 
 package android.security.advancedprotection;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.security.Flags;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 
@@ -45,6 +55,139 @@
 public final class AdvancedProtectionManager {
     private static final String TAG = "AdvancedProtectionMgr";
 
+    /**
+     * Advanced Protection's identifier for setting policies or restrictions in DevicePolicyManager.
+     *
+     * @hide */
+    public static final String ADVANCED_PROTECTION_SYSTEM_ENTITY =
+            "android.security.advancedprotection";
+
+    /**
+     * Feature identifier for disallowing 2G.
+     *
+     * @hide */
+    @SystemApi
+    public static final String FEATURE_ID_DISALLOW_CELLULAR_2G =
+            "android.security.advancedprotection.feature_disallow_2g";
+
+    /**
+     * Feature identifier for disallowing install of unknown sources.
+     *
+     * @hide */
+    @SystemApi
+    public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES =
+            "android.security.advancedprotection.feature_disallow_install_unknown_sources";
+
+    /**
+     * Feature identifier for disallowing USB.
+     *
+     * @hide */
+    @SystemApi
+    public static final String FEATURE_ID_DISALLOW_USB =
+            "android.security.advancedprotection.feature_disallow_usb";
+
+    /**
+     * Feature identifier for disallowing WEP.
+     *
+     * @hide */
+    @SystemApi
+    public static final String FEATURE_ID_DISALLOW_WEP =
+            "android.security.advancedprotection.feature_disallow_wep";
+
+    /**
+     * Feature identifier for enabling MTE.
+     *
+     * @hide */
+    @SystemApi
+    public static final String FEATURE_ID_ENABLE_MTE =
+            "android.security.advancedprotection.feature_enable_mte";
+
+    /** @hide */
+    @StringDef(prefix = { "FEATURE_ID_" }, value = {
+            FEATURE_ID_DISALLOW_CELLULAR_2G,
+            FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
+            FEATURE_ID_DISALLOW_USB,
+            FEATURE_ID_DISALLOW_WEP,
+            FEATURE_ID_ENABLE_MTE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeatureId {}
+
+    private static final Set<String> ALL_FEATURE_IDS = Set.of(
+            FEATURE_ID_DISALLOW_CELLULAR_2G,
+            FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
+            FEATURE_ID_DISALLOW_USB,
+            FEATURE_ID_DISALLOW_WEP,
+            FEATURE_ID_ENABLE_MTE);
+
+    /**
+     * Activity Action: Show a dialog with disabled by advanced protection message.
+     * <p> If a user action or a setting toggle is disabled by advanced protection, this dialog can
+     * be triggered to let the user know about this.
+     * <p>
+     * Input:
+     * <p>{@link #EXTRA_SUPPORT_DIALOG_FEATURE}: The feature identifier.
+     * <p>{@link #EXTRA_SUPPORT_DIALOG_TYPE}: The type of the action.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @FlaggedApi(android.security.Flags.FLAG_AAPM_API)
+    public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG =
+            "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
+
+    /**
+     * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+     * show a support dialog explaining it was disabled by advanced protection.
+     *
+     * @hide */
+    @FeatureId
+    @SystemApi
+    public static final String EXTRA_SUPPORT_DIALOG_FEATURE =
+            "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
+
+    /**
+     * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+     * needs to be explained in the support dialog.
+     *
+     * @hide */
+    @SupportDialogType
+    @SystemApi
+    public static final String EXTRA_SUPPORT_DIALOG_TYPE =
+            "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
+
+    /**
+     * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user performed an action that was
+     * blocked by advanced protection.
+     *
+     * @hide */
+    @SystemApi
+    public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION =
+            "android.security.advancedprotection.type_blocked_interaction";
+
+    /**
+     * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user pressed on a setting toggle
+     * that was disabled by advanced protection.
+     *
+     * @hide */
+    @SystemApi
+    public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING =
+            "android.security.advancedprotection.type_disabled_setting";
+
+    /** @hide */
+    @StringDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = {
+            SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
+            SUPPORT_DIALOG_TYPE_DISABLED_SETTING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SupportDialogType {}
+
+    private static final Set<String> ALL_SUPPORT_DIALOG_TYPES = Set.of(
+            SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
+            SUPPORT_DIALOG_TYPE_DISABLED_SETTING);
+
     private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback>
             mCallbackMap = new ConcurrentHashMap<>();
 
@@ -164,6 +307,43 @@
     }
 
     /**
+     * Called by a feature to display a support dialog when a feature was disabled by advanced
+     * protection. This returns an intent that can be used with
+     * {@link Context#startActivity(Intent)} to display the dialog.
+     *
+     * <p>Note that this method doesn't check if the feature is actually disabled, i.e. this method
+     * will always return an intent.
+     *
+     * @param featureId The feature identifier.
+     * @param type The type of the feature describing the action that needs to be explained
+     *                 in the dialog or null for default explanation.
+     * @return Intent An intent to be used to start the dialog-activity that explains a feature was
+     *                disabled by advanced protection.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull Intent createSupportIntent(@NonNull @FeatureId String featureId,
+            @Nullable @SupportDialogType String type) {
+        Objects.requireNonNull(featureId);
+        if (!ALL_FEATURE_IDS.contains(featureId)) {
+            throw new IllegalArgumentException(featureId + " is not a valid feature ID. See"
+                    + " FEATURE_ID_* APIs.");
+        }
+        if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
+            throw new IllegalArgumentException(type + " is not a valid type. See"
+                    + " SUPPORT_DIALOG_TYPE_* APIs.");
+        }
+
+        Intent intent = new Intent(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG);
+        intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(EXTRA_SUPPORT_DIALOG_FEATURE, featureId);
+        if (type != null) {
+            intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type);
+        }
+        return intent;
+    }
+
+    /**
      * A callback class for monitoring changes to Advanced Protection state
      *
      * <p>To register a callback, implement this interface, and register it with
diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig
new file mode 100644
index 0000000..07311d5
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.service.quickaccesswallet"
+container: "system"
+
+flag {
+    name: "launch_wallet_option_on_power_double_tap"
+    namespace: "wallet_integrations"
+    description: "Option to launch the Wallet app on double-tap of the power button"
+    bug: "378469025"
+}
\ No newline at end of file
diff --git a/core/java/android/view/HapticScrollFeedbackProvider.java b/core/java/android/view/HapticScrollFeedbackProvider.java
index 0001176..c3fb855 100644
--- a/core/java/android/view/HapticScrollFeedbackProvider.java
+++ b/core/java/android/view/HapticScrollFeedbackProvider.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.flags.Flags.dynamicViewRotaryHapticsConfiguration;
+
 import android.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -41,13 +43,8 @@
 
     private final View mView;
     private final ViewConfiguration mViewConfig;
-    /**
-     * Flag to disable the logic in this class if the View-based scroll haptics implementation is
-     * enabled. If {@code false}, this class will continue to run despite the View's scroll
-     * haptics implementation being enabled. This value should be set to {@code true} when this
-     * class is directly used by the View class.
-     */
-    private final boolean mDisabledIfViewPlaysScrollHaptics;
+    /** Whether or not this provider is being used directly by the View class. */
+    private final boolean mIsFromView;
 
 
     // Info about the cause of the latest scroll event.
@@ -65,17 +62,23 @@
     private boolean mHapticScrollFeedbackEnabled = false;
 
     public HapticScrollFeedbackProvider(@NonNull View view) {
-        this(view, ViewConfiguration.get(view.getContext()),
-                /* disabledIfViewPlaysScrollHaptics= */ true);
+        this(view, ViewConfiguration.get(view.getContext()), /* isFromView= */ false);
     }
 
     /** @hide */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public HapticScrollFeedbackProvider(
-            View view, ViewConfiguration viewConfig, boolean disabledIfViewPlaysScrollHaptics) {
+            View view, ViewConfiguration viewConfig, boolean isFromView) {
         mView = view;
         mViewConfig = viewConfig;
-        mDisabledIfViewPlaysScrollHaptics = disabledIfViewPlaysScrollHaptics;
+        mIsFromView = isFromView;
+        if (dynamicViewRotaryHapticsConfiguration() && !isFromView) {
+            // Disable the View class's rotary scroll feedback logic if this provider is not being
+            // directly used by the View class. This is to avoid double rotary scroll feedback:
+            // one from the View class, and one from this provider instance (i.e. mute the View
+            // class's rotary feedback and enable this provider).
+            view.disableRotaryScrollFeedback();
+        }
     }
 
     @Override
@@ -151,7 +154,8 @@
             mAxis = axis;
             mDeviceId = deviceId;
 
-            if (mDisabledIfViewPlaysScrollHaptics
+            if (!dynamicViewRotaryHapticsConfiguration()
+                    && !mIsFromView
                     && (source == InputDevice.SOURCE_ROTARY_ENCODER)
                     && mViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) {
                 mHapticScrollFeedbackEnabled = false;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c71bf4b..049189f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16754,9 +16754,7 @@
                 mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_DETERMINED;
             }
         }
-        final boolean processForRotaryScrollHaptics =
-                isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0);
-        if (processForRotaryScrollHaptics) {
+        if (isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0)) {
             mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT;
             mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT;
         }
@@ -16773,7 +16771,10 @@
         // Process scroll haptics after `onGenericMotionEvent`, since that's where scrolling usually
         // happens. Some views may return false from `onGenericMotionEvent` even if they have done
         // scrolling, so disregard the return value when processing for scroll haptics.
-        if (processForRotaryScrollHaptics) {
+        // Check for `PFLAG4_ROTARY_HAPTICS_ENABLED` again, because the View implementation may
+        // call `disableRotaryScrollFeedback` in `onGenericMotionEvent`, which could change the
+        // value of `PFLAG4_ROTARY_HAPTICS_ENABLED`.
+        if (isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0)) {
             if ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT) != 0) {
                 doRotaryProgressForScrollHaptics(event);
             } else {
@@ -18716,7 +18717,7 @@
     private HapticScrollFeedbackProvider getScrollFeedbackProvider() {
         if (mScrollFeedbackProvider == null) {
             mScrollFeedbackProvider = new HapticScrollFeedbackProvider(this,
-                    ViewConfiguration.get(mContext), /* disabledIfViewPlaysScrollHaptics= */ false);
+                    ViewConfiguration.get(mContext), /* isFromView= */ true);
         }
         return mScrollFeedbackProvider;
     }
@@ -18746,6 +18747,21 @@
     }
 
     /**
+     * Disables the rotary scroll feedback implementation of the View class.
+     *
+     * <p>Note that this does NOT disable all rotary scroll feedback; it just disables the logic
+     * implemented within the View class. The child implementation of the View may implement its own
+     * rotary scroll feedback logic or use {@link ScrollFeedbackProvider} to generate rotary scroll
+     * feedback.
+     */
+    void disableRotaryScrollFeedback() {
+        // Force set PFLAG4_ROTARY_HAPTICS_DETERMINED to avoid recalculating
+        // PFLAG4_ROTARY_HAPTICS_ENABLED under any circumstance.
+        mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_DETERMINED;
+        mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_ENABLED;
+    }
+
+    /**
      * This is called in response to an internal scroll in this view (i.e., the
      * view scrolled its own contents). This is typically as a result of
      * {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index 658aa29..b180e58 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -23,3 +23,10 @@
     bug: "331830899"
     is_fixed_read_only: true
 }
+
+flag {
+    namespace: "wear_frameworks"
+    name: "dynamic_view_rotary_haptics_configuration"
+    description: "Whether ScrollFeedbackProvider dynamically disables View-based rotary haptics."
+    bug: "377998870 "
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 73f9d9f..6026e60 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2471,6 +2471,11 @@
                 return;
             }
 
+            if (Flags.refactorInsetsController()) {
+                showSoftInput(rootView, statsToken, flags, resultReceiver, reason);
+                return;
+            }
+
             ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
 
             // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread.
diff --git a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
index ac6c19e..66cf9c7 100644
--- a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
+++ b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED;
+import static android.view.flags.Flags.FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION;
 import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS;
 import static android.view.HapticFeedbackConstants.SCROLL_LIMIT;
 import static android.view.HapticFeedbackConstants.SCROLL_TICK;
@@ -74,12 +75,13 @@
 
         mView = new TestView(InstrumentationRegistry.getContext());
         mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
-                /* disabledIfViewPlaysScrollHaptics= */ true);
+                /* isFromView= */ false);
         mSetFlagsRule.disableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
     }
 
     @Test
     public void testRotaryEncoder_noFeedbackWhenViewBasedFeedbackIsEnabled() {
+        mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
         when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
                 .thenReturn(true);
         setHapticScrollTickInterval(5);
@@ -97,7 +99,24 @@
     }
 
     @Test
+    public void testRotaryEncoder_dynamicViewRotaryFeedback_enabledEvenWhenViewFeedbackIsEnabled() {
+        mSetFlagsRule.enableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
+        when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
+                .thenReturn(true);
+        setHapticScrollTickInterval(5);
+        mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
+                /* isFromView= */ false);
+
+        mProvider.onScrollProgress(
+                INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
+                /* deltaInPixels= */ 10);
+
+        assertFeedbackCount(mView, SCROLL_TICK, 1);
+    }
+
+    @Test
     public void testRotaryEncoder_inputDeviceCustomized_noFeedbackWhenViewBasedFeedbackIsEnabled() {
+        mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
         mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
 
         when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
@@ -119,7 +138,7 @@
     @Test
     public void testRotaryEncoder_feedbackWhenDisregardingViewBasedScrollHaptics() {
         mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
-                /* disabledIfViewPlaysScrollHaptics= */ false);
+                /* isFromView= */ true);
         when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
                 .thenReturn(true);
         setHapticScrollTickInterval(5);
@@ -144,7 +163,7 @@
         List<HapticFeedbackRequest> requests = new ArrayList<>();
 
         mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
-                /* disabledIfViewPlaysScrollHaptics= */ false);
+                /* isFromView= */ true);
         when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
                 .thenReturn(true);
         setHapticScrollTickInterval(5);
@@ -917,19 +936,20 @@
 
     @Test
     public void testNonRotaryInputFeedbackNotBlockedByRotaryUnavailability() {
+        mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
         when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
                 .thenReturn(true);
         setHapticScrollFeedbackEnabled(true);
         setHapticScrollTickInterval(5);
         mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
-                /* disabledIfViewPlaysScrollHaptics= */ true);
+                /* isFromView= */ false);
 
         // Expect one feedback here. Touch input should provide feedback since scroll feedback has
         // been enabled via `setHapticScrollFeedbackEnabled(true)`.
         mProvider.onScrollProgress(
                 INPUT_DEVICE_1, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.AXIS_Y,
                 /* deltaInPixels= */ 10);
-        // Because `isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()` is false and
+        // Because `isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()` is true and
         // `disabledIfViewPlaysScrollHaptics` is true, the scroll progress from rotary encoders will
         // produce no feedback.
         mProvider.onScrollProgress(
diff --git a/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
index 9a5c1c5..b1a5637 100644
--- a/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
+++ b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
@@ -30,8 +30,12 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.flags.Flags;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -39,6 +43,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -48,6 +53,8 @@
 @RunWith(AndroidJUnit4.class)
 @Presubmit
 public final class RotaryScrollHapticsTest {
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final int TEST_ROTARY_DEVICE_ID = 1;
     private static final int TEST_RANDOM_DEVICE_ID = 2;
 
@@ -167,6 +174,26 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION)
+    public void testChildViewImplementationUsesScrollFeedbackProvider_doesNoScrollFeedback() {
+        mView.configureGenericMotion(/* result= */ false, /* scroll= */ true);
+        mView.mUsesCustomScrollFeedbackProvider = true;
+
+        // Send multiple generic motion events, to catch bugs where the behavior is WAI only for the
+        // first dispatch, but buggy for future calls.
+        mView.dispatchGenericMotionEvent(createRotaryEvent(20));
+        mView.dispatchGenericMotionEvent(createRotaryEvent(10));
+        mView.dispatchGenericMotionEvent(createRotaryEvent(30));
+
+        // Verify that the base View class's ScrollFeedbackProvider produces no scroll progress
+        // or limit events, because there's a custom ScrollFeedbackProvider used by the child
+        // View class implementation, which should hint the base View class to disable its own
+        // ScrollFeedbackProvider usage.
+        verifyNoScrollProgress();
+        verifyNoScrollLimit();
+    }
+
+    @Test
     public void testScrollProgress_genericMotionEventCallbackReturningTrue_doesScrollProgress() {
         mView.configureGenericMotion(/* result= */ true, /* scroll= */ true);
 
@@ -208,6 +235,9 @@
     private static final class TestGenericMotionEventControllingView extends View {
         private boolean mGenericMotionResult;
         private boolean mScrollOnGenericMotion;
+        private boolean mUsesCustomScrollFeedbackProvider = false;
+
+        @Nullable private ScrollFeedbackProvider mCustomScrollFeedbackProvider;
 
         TestGenericMotionEventControllingView(Context context) {
             super(context);
@@ -222,6 +252,19 @@
         public boolean onGenericMotionEvent(MotionEvent event) {
             if (mScrollOnGenericMotion) {
                 scrollTo(100, 200); // scroll values random (not relevant for tests).
+                if (mUsesCustomScrollFeedbackProvider) {
+                    // Mimic how a real child class of View would instantiate and use the
+                    // ScrollFeedbackProvider API.
+                    if (mCustomScrollFeedbackProvider == null) {
+                        mCustomScrollFeedbackProvider = ScrollFeedbackProvider.createProvider(this);
+                    }
+                    float axisScrollValue = event.getAxisValue(AXIS_SCROLL);
+                    mCustomScrollFeedbackProvider.onScrollProgress(
+                            event.getDeviceId(),
+                            event.getSource(),
+                            MotionEvent.AXIS_SCROLL,
+                            (int) (axisScrollValue * TEST_SCALED_VERTICAL_SCROLL_FACTOR));
+                }
             }
             return mGenericMotionResult;
         }
diff --git a/data/sounds/Android.bp b/data/sounds/Android.bp
new file mode 100644
index 0000000..65d4872
--- /dev/null
+++ b/data/sounds/Android.bp
@@ -0,0 +1,304 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+phony {
+    name: "frameworks_sounds",
+    required: [
+        "frameworks_alarm_sounds",
+        "frameworks_notifications_sounds",
+        "frameworks_ringtones_sounds",
+        "frameworks_ui_sounds",
+        "frameworks_ui_48k_sounds",
+    ],
+}
+
+prebuilt_media {
+    name: "frameworks_alarm_sounds",
+    srcs: [
+        "Alarm_Beep_01.ogg",
+        "Alarm_Beep_02.ogg",
+        "Alarm_Beep_03.ogg",
+        "Alarm_Buzzer.ogg",
+        "Alarm_Classic.ogg",
+        "Alarm_Rooster_02.ogg",
+        "alarms/ogg/Argon.ogg",
+        "alarms/ogg/Barium.ogg",
+        "alarms/ogg/Carbon.ogg",
+        "alarms/ogg/Helium.ogg",
+        "alarms/ogg/Krypton.ogg",
+        "alarms/ogg/Neon.ogg",
+        "alarms/ogg/Neptunium.ogg",
+        "alarms/ogg/Osmium.ogg",
+        "alarms/ogg/Oxygen.ogg",
+        "alarms/ogg/Platinum.ogg",
+        "alarms/ogg/Promethium.ogg",
+        "alarms/ogg/Scandium.ogg",
+    ],
+    relative_install_path: "audio/alarms",
+    product_specific: true,
+    no_full_install: true,
+}
+
+prebuilt_media {
+    name: "frameworks_notifications_sounds",
+    srcs: [
+        "notifications/ogg/Adara.ogg",
+        "notifications/Aldebaran.ogg",
+        "notifications/Altair.ogg",
+        "notifications/ogg/Alya.ogg",
+        "notifications/Antares.ogg",
+        "notifications/ogg/Antimony.ogg",
+        "notifications/ogg/Arcturus.ogg",
+        "notifications/ogg/Argon.ogg",
+        "notifications/Beat_Box_Android.ogg",
+        "notifications/ogg/Bellatrix.ogg",
+        "notifications/ogg/Beryllium.ogg",
+        "notifications/Betelgeuse.ogg",
+        "newwavelabs/CaffeineSnake.ogg",
+        "notifications/Canopus.ogg",
+        "notifications/ogg/Capella.ogg",
+        "notifications/Castor.ogg",
+        "notifications/ogg/CetiAlpha.ogg",
+        "notifications/ogg/Cobalt.ogg",
+        "notifications/Cricket.ogg",
+        "newwavelabs/DearDeer.ogg",
+        "notifications/Deneb.ogg",
+        "notifications/Doink.ogg",
+        "newwavelabs/DontPanic.ogg",
+        "notifications/Drip.ogg",
+        "notifications/Electra.ogg",
+        "F1_MissedCall.ogg",
+        "F1_New_MMS.ogg",
+        "F1_New_SMS.ogg",
+        "notifications/ogg/Fluorine.ogg",
+        "notifications/Fomalhaut.ogg",
+        "notifications/ogg/Gallium.ogg",
+        "notifications/Heaven.ogg",
+        "notifications/ogg/Helium.ogg",
+        "newwavelabs/Highwire.ogg",
+        "notifications/ogg/Hojus.ogg",
+        "notifications/ogg/Iridium.ogg",
+        "notifications/ogg/Krypton.ogg",
+        "newwavelabs/KzurbSonar.ogg",
+        "notifications/ogg/Lalande.ogg",
+        "notifications/Merope.ogg",
+        "notifications/ogg/Mira.ogg",
+        "newwavelabs/OnTheHunt.ogg",
+        "notifications/ogg/Palladium.ogg",
+        "notifications/Plastic_Pipe.ogg",
+        "notifications/ogg/Polaris.ogg",
+        "notifications/ogg/Pollux.ogg",
+        "notifications/ogg/Procyon.ogg",
+        "notifications/ogg/Proxima.ogg",
+        "notifications/ogg/Radon.ogg",
+        "notifications/ogg/Rubidium.ogg",
+        "notifications/ogg/Selenium.ogg",
+        "notifications/ogg/Shaula.ogg",
+        "notifications/Sirrah.ogg",
+        "notifications/SpaceSeed.ogg",
+        "notifications/ogg/Spica.ogg",
+        "notifications/ogg/Strontium.ogg",
+        "notifications/ogg/Syrma.ogg",
+        "notifications/TaDa.ogg",
+        "notifications/ogg/Talitha.ogg",
+        "notifications/ogg/Tejat.ogg",
+        "notifications/ogg/Thallium.ogg",
+        "notifications/Tinkerbell.ogg",
+        "notifications/ogg/Upsilon.ogg",
+        "notifications/ogg/Vega.ogg",
+        "newwavelabs/Voila.ogg",
+        "notifications/ogg/Xenon.ogg",
+        "notifications/ogg/Zirconium.ogg",
+        "notifications/arcturus.ogg",
+        "notifications/moonbeam.ogg",
+        "notifications/pixiedust.ogg",
+        "notifications/pizzicato.ogg",
+        "notifications/regulus.ogg",
+        "notifications/sirius.ogg",
+        "notifications/tweeters.ogg",
+        "notifications/vega.ogg",
+    ],
+    relative_install_path: "audio/notifications",
+    product_specific: true,
+    no_full_install: true,
+}
+
+prebuilt_media {
+    name: "frameworks_ringtones_sounds",
+    srcs: [
+        "ringtones/ANDROMEDA.ogg",
+        "ringtones/ogg/Andromeda.ogg",
+        "ringtones/ogg/Aquila.ogg",
+        "ringtones/ogg/ArgoNavis.ogg",
+        "ringtones/ogg/Atria.ogg",
+        "ringtones/BOOTES.ogg",
+        "newwavelabs/Backroad.ogg",
+        "newwavelabs/BeatPlucker.ogg",
+        "newwavelabs/BentleyDubs.ogg",
+        "newwavelabs/Big_Easy.ogg",
+        "newwavelabs/BirdLoop.ogg",
+        "newwavelabs/Bollywood.ogg",
+        "newwavelabs/BussaMove.ogg",
+        "ringtones/CANISMAJOR.ogg",
+        "ringtones/CASSIOPEIA.ogg",
+        "newwavelabs/Cairo.ogg",
+        "newwavelabs/Calypso_Steel.ogg",
+        "ringtones/ogg/CanisMajor.ogg",
+        "newwavelabs/CaribbeanIce.ogg",
+        "ringtones/ogg/Carina.ogg",
+        "ringtones/ogg/Centaurus.ogg",
+        "newwavelabs/Champagne_Edition.ogg",
+        "newwavelabs/Club_Cubano.ogg",
+        "newwavelabs/CrayonRock.ogg",
+        "newwavelabs/CrazyDream.ogg",
+        "newwavelabs/CurveBall.ogg",
+        "ringtones/ogg/Cygnus.ogg",
+        "newwavelabs/DancinFool.ogg",
+        "newwavelabs/Ding.ogg",
+        "newwavelabs/DonMessWivIt.ogg",
+        "ringtones/ogg/Draco.ogg",
+        "newwavelabs/DreamTheme.ogg",
+        "newwavelabs/Eastern_Sky.ogg",
+        "newwavelabs/Enter_the_Nexus.ogg",
+        "ringtones/Eridani.ogg",
+        "newwavelabs/EtherShake.ogg",
+        "ringtones/FreeFlight.ogg",
+        "newwavelabs/FriendlyGhost.ogg",
+        "newwavelabs/Funk_Yall.ogg",
+        "newwavelabs/GameOverGuitar.ogg",
+        "newwavelabs/Gimme_Mo_Town.ogg",
+        "ringtones/ogg/Girtab.ogg",
+        "newwavelabs/Glacial_Groove.ogg",
+        "newwavelabs/Growl.ogg",
+        "newwavelabs/HalfwayHome.ogg",
+        "ringtones/ogg/Hydra.ogg",
+        "newwavelabs/InsertCoin.ogg",
+        "ringtones/ogg/Kuma.ogg",
+        "newwavelabs/LoopyLounge.ogg",
+        "newwavelabs/LoveFlute.ogg",
+        "ringtones/Lyra.ogg",
+        "ringtones/ogg/Machina.ogg",
+        "newwavelabs/MidEvilJaunt.ogg",
+        "newwavelabs/MildlyAlarming.ogg",
+        "newwavelabs/Nairobi.ogg",
+        "newwavelabs/Nassau.ogg",
+        "newwavelabs/NewPlayer.ogg",
+        "newwavelabs/No_Limits.ogg",
+        "newwavelabs/Noises1.ogg",
+        "newwavelabs/Noises2.ogg",
+        "newwavelabs/Noises3.ogg",
+        "newwavelabs/OrganDub.ogg",
+        "ringtones/ogg/Orion.ogg",
+        "ringtones/PERSEUS.ogg",
+        "newwavelabs/Paradise_Island.ogg",
+        "ringtones/ogg/Pegasus.ogg",
+        "ringtones/ogg/Perseus.ogg",
+        "newwavelabs/Playa.ogg",
+        "ringtones/ogg/Pyxis.ogg",
+        "ringtones/ogg/Rasalas.ogg",
+        "newwavelabs/Revelation.ogg",
+        "ringtones/ogg/Rigel.ogg",
+        "Ring_Classic_02.ogg",
+        "Ring_Digital_02.ogg",
+        "Ring_Synth_02.ogg",
+        "Ring_Synth_04.ogg",
+        "newwavelabs/Road_Trip.ogg",
+        "newwavelabs/RomancingTheTone.ogg",
+        "newwavelabs/Safari.ogg",
+        "newwavelabs/Savannah.ogg",
+        "ringtones/ogg/Scarabaeus.ogg",
+        "ringtones/ogg/Sceptrum.ogg",
+        "newwavelabs/Seville.ogg",
+        "newwavelabs/Shes_All_That.ogg",
+        "newwavelabs/SilkyWay.ogg",
+        "newwavelabs/SitarVsSitar.ogg",
+        "ringtones/ogg/Solarium.ogg",
+        "newwavelabs/SpringyJalopy.ogg",
+        "newwavelabs/Steppin_Out.ogg",
+        "newwavelabs/Terminated.ogg",
+        "ringtones/Testudo.ogg",
+        "ringtones/ogg/Themos.ogg",
+        "newwavelabs/Third_Eye.ogg",
+        "newwavelabs/Thunderfoot.ogg",
+        "newwavelabs/TwirlAway.ogg",
+        "ringtones/URSAMINOR.ogg",
+        "ringtones/ogg/UrsaMinor.ogg",
+        "newwavelabs/VeryAlarmed.ogg",
+        "ringtones/Vespa.ogg",
+        "newwavelabs/World.ogg",
+        "ringtones/ogg/Zeta.ogg",
+        "ringtones/hydra.ogg",
+    ],
+    relative_install_path: "audio/ringtones",
+    product_specific: true,
+    no_full_install: true,
+}
+
+prebuilt_media {
+    name: "frameworks_ui_48k_sounds",
+    srcs: [
+        "effects/ogg/Effect_Tick_48k.ogg",
+        "effects/ogg/KeypressDelete_120_48k.ogg",
+        "effects/ogg/KeypressReturn_120_48k.ogg",
+        "effects/ogg/KeypressSpacebar_120_48k.ogg",
+        "effects/ogg/KeypressStandard_120_48k.ogg",
+        "effects/ogg/KeypressInvalid_120_48k.ogg",
+        "effects/ogg/Trusted_48k.ogg",
+        "effects/ogg/VideoRecord_48k.ogg",
+        "effects/ogg/VideoStop_48k.ogg",
+        "effects/ogg/camera_click_48k.ogg",
+    ],
+    dsts: [
+        "Effect_Tick.ogg",
+        "KeypressDelete.ogg",
+        "KeypressReturn.ogg",
+        "KeypressSpacebar.ogg",
+        "KeypressStandard.ogg",
+        "KeypressInvalid.ogg",
+        "Trusted.ogg",
+        "VideoRecord.ogg",
+        "VideoStop.ogg",
+        "camera_click.ogg",
+    ],
+    relative_install_path: "audio/ui",
+    product_specific: true,
+    no_full_install: true,
+}
+
+prebuilt_media {
+    name: "frameworks_ui_sounds",
+    srcs: [
+        "effects/ogg/Dock.ogg",
+        "effects/ogg/Lock.ogg",
+        "effects/ogg/LowBattery.ogg",
+        "effects/ogg/Undock.ogg",
+        "effects/ogg/Unlock.ogg",
+        "effects/ogg/WirelessChargingStarted.ogg",
+        "effects/ogg/camera_focus.ogg",
+        "effects/ogg/ChargingStarted.ogg",
+        "effects/ogg/InCallNotification.ogg",
+        "effects/ogg/NFCFailure.ogg",
+        "effects/ogg/NFCInitiated.ogg",
+        "effects/ogg/NFCSuccess.ogg",
+        "effects/ogg/NFCTransferComplete.ogg",
+        "effects/ogg/NFCTransferInitiated.ogg",
+    ],
+    relative_install_path: "audio/ui",
+    product_specific: true,
+    no_full_install: true,
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
index cae5d8e..35b2375 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
@@ -96,7 +96,7 @@
             }
             if (WRITE_PARCELABLE.matches(tree, state)) {
                 return buildDescription(tree)
-                        .setMessage("Recommended to use 'item.writeToParcel()' to improve "
+                        .setMessage("Recommended to use 'writeTypedObject()' to improve "
                                 + "efficiency; saves overhead of Parcelable class name")
                         .build();
             }
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index a23845f..c25f77b 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -107,6 +107,7 @@
     method public void onRfDiscoveryStarted(boolean);
     method public void onRfFieldActivated(boolean);
     method public void onRoutingChanged();
+    method public void onRoutingTableFull();
     method public void onStateUpdated(int);
     method public void onTagConnected(boolean);
     method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>);
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index b102e87..fb793b0 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -52,5 +52,6 @@
    void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent);
    void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category);
    void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category);
+   void onRoutingTableFull();
    void onLogEventNotified(in OemLogItems item);
 }
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index abd99bc..57ee981 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -394,6 +394,13 @@
         void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category);
 
         /**
+         * Callback to indicate that routing table is full and the OEM can optionally launch a
+         * dialog to request the user to remove some Card Emulation apps from the device to free
+         * routing table space.
+         */
+        void onRoutingTableFull();
+
+        /**
          * Callback when OEM specified log event are notified.
          * @param item the log items that contains log information of NFC event.
          */
@@ -853,6 +860,12 @@
                     handleVoidCallback(enabled, cb::onReaderOptionChanged, ex));
         }
 
+        public void onRoutingTableFull() throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(null,
+                            (Object input) -> cb.onRoutingTableFull(), ex));
+        }
+
         @Override
         public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer)
                 throws RemoteException {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index c710aa3..f03014c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -821,9 +821,9 @@
 
     <!-- Title of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=32] -->
     <string name="enable_linux_terminal_title">Linux development environment</string>
-    <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=64] -->
-    <string name="enable_linux_terminal_summary">Run Linux terminal on Android</string>
-    <!-- Disclaimer below the checkbox that disabling the Linux terminal app would clear its data. [CHAR LIMIT=64] -->
+    <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=none] -->
+    <string name="enable_linux_terminal_summary">(Experimental) Run Linux terminal on Android</string>
+    <!-- Disclaimer below the checkbox that disabling the Linux terminal app would clear its data. [CHAR LIMIT=none] -->
     <string name="disable_linux_terminal_disclaimer">If you disable, Linux terminal data will be cleared</string>
 
     <!-- HDCP checking title, used for debug purposes only. [CHAR LIMIT=25] -->
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index fbce6ca..aca2c4e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.settings;
 
+import static android.provider.DeviceConfig.DUMP_ARG_NAMESPACE;
 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
@@ -42,6 +43,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Config.SyncDisabledMode;
 import android.provider.UpdatableDeviceConfigServiceReadiness;
+import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.util.FastPrintWriter;
@@ -55,11 +57,13 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * Receives shell commands from the command line related to device config flags, and dispatches them
@@ -80,6 +84,7 @@
     final SettingsProvider mProvider;
 
     private static final String TAG = "DeviceConfigService";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     public DeviceConfigService(SettingsProvider provider) {
         mProvider = provider;
@@ -97,14 +102,35 @@
         }
     }
 
+    // TODO(b/364399200): add unit test
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        String filter = null;
         if (android.provider.flags.Flags.dumpImprovements()) {
-            pw.print("SyncDisabledForTests: ");
-            MyShellCommand.getSyncDisabledForTests(pw, pw);
+            if (args.length > 0) {
+                switch (args[0]) {
+                    case DUMP_ARG_NAMESPACE:
+                        if (args.length < 2) {
+                            throw new IllegalArgumentException("argument " + DUMP_ARG_NAMESPACE
+                                    + " requires an extra argument");
+                        }
+                        filter = args[1];
+                        if (DEBUG) {
+                            Slog.d(TAG, "dump(): setting filter as " + filter);
+                        }
+                        break;
+                    default:
+                        Slog.w(TAG, "dump(): ignoring invalid arguments: " + Arrays.toString(args));
+                        break;
+                }
+            }
+            if (filter == null) {
+                pw.print("SyncDisabledForTests: ");
+                MyShellCommand.getSyncDisabledForTests(pw, pw);
 
-            pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): ");
-            pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+                pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): ");
+                pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+            }
 
             pw.println("DeviceConfig provider: ");
             try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd)) {
@@ -117,8 +143,16 @@
 
         IContentProvider iprovider = mProvider.getIContentProvider();
         pw.println("DeviceConfig flags:");
+        Pattern lineFilter = filter == null ? null : Pattern.compile("^.*" + filter + ".*\\/.*$");
         for (String line : MyShellCommand.listAll(iprovider)) {
-            pw.println(line);
+            if (lineFilter == null || lineFilter.matcher(line).matches()) {
+                pw.println(line);
+            }
+        }
+
+        if (filter != null) {
+            // TODO(b/364399200): use filter to skip instead?
+            return;
         }
 
         ArrayList<String> missingFiles = new ArrayList<String>();
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 9392b1a..96e99b1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -66,13 +66,13 @@
                             interactionHandler = interactionHandler,
                             dialogFactory = dialogFactory,
                             widgetSection = widgetSection,
-                            modifier = Modifier.element(Communal.Elements.Grid)
+                            modifier = Modifier.element(Communal.Elements.Grid),
                         )
                     }
                     with(lockSection) {
                         LockIcon(
                             overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
-                            modifier = Modifier.element(Communal.Elements.LockIcon)
+                            modifier = Modifier.element(Communal.Elements.LockIcon),
                         )
                     }
                     with(bottomAreaSection) {
@@ -80,17 +80,13 @@
                             Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
                         )
                     }
-                }
+                },
             ) { measurables, constraints ->
                 val communalGridMeasurable = measurables[0]
                 val lockIconMeasurable = measurables[1]
                 val bottomAreaMeasurable = measurables[2]
 
-                val noMinConstraints =
-                    constraints.copy(
-                        minWidth = 0,
-                        minHeight = 0,
-                    )
+                val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
 
                 val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
                 val lockIconBounds =
@@ -109,14 +105,8 @@
                     )
 
                 layout(constraints.maxWidth, constraints.maxHeight) {
-                    communalGridPlaceable.place(
-                        x = 0,
-                        y = 0,
-                    )
-                    lockIconPlaceable.place(
-                        x = lockIconBounds.left,
-                        y = lockIconBounds.top,
-                    )
+                    communalGridPlaceable.place(x = 0, y = 0)
+                    lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top)
                     bottomAreaPlaceable.place(
                         x = 0,
                         y = constraints.maxHeight - bottomAreaPlaceable.height,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 5e1ac1f..df1185c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -807,7 +807,6 @@
                 ) {
                     ResizeableItemFrameViewModel()
                 }
-
             if (viewModel.isEditMode && dragDropState != null) {
                 val isItemDragging = dragDropState.draggingItemKey == item.key
                 val outlineAlpha by
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 6e30575..16002bc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -59,7 +59,14 @@
     private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
     private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
     private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
-    private val onResizeWidget: (id: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) -> Unit,
+    private val onResizeWidget:
+        (
+            id: Int,
+            spanY: Int,
+            widgetIdToRankMap: Map<Int, Int>,
+            componentName: ComponentName,
+            rank: Int,
+        ) -> Unit,
 ) {
     var list = communalContent.toMutableStateList()
         private set
@@ -105,7 +112,9 @@
             } else {
                 emptyMap()
             }
-        onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap)
+        val componentName = item.componentName
+        val rank = item.rank
+        onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap, componentName, rank)
     }
 
     /**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
index 82918a5..af2d7e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
@@ -47,20 +47,14 @@
 
     @Test
     fun logAddWidget_componentNotLoggable_doNotLog() {
-        underTest.logAddWidget(
-            componentName = "com.green.package/my_test_widget",
-            rank = 1,
-        )
+        underTest.logAddWidget(componentName = "com.green.package/my_test_widget", rank = 1)
         verify(statsLogProxy, never())
-            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
     }
 
     @Test
     fun logAddWidget_componentLoggable_logAddEvent() {
-        underTest.logAddWidget(
-            componentName = "com.blue.package/my_test_widget",
-            rank = 1,
-        )
+        underTest.logAddWidget(componentName = "com.blue.package/my_test_widget", rank = 1)
         verify(statsLogProxy)
             .writeCommunalHubWidgetEventReported(
                 SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
@@ -71,20 +65,14 @@
 
     @Test
     fun logRemoveWidget_componentNotLoggable_doNotLog() {
-        underTest.logRemoveWidget(
-            componentName = "com.yellow.package/my_test_widget",
-            rank = 2,
-        )
+        underTest.logRemoveWidget(componentName = "com.yellow.package/my_test_widget", rank = 2)
         verify(statsLogProxy, never())
-            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
     }
 
     @Test
     fun logRemoveWidget_componentLoggable_logRemoveEvent() {
-        underTest.logRemoveWidget(
-            componentName = "com.red.package/my_test_widget",
-            rank = 2,
-        )
+        underTest.logRemoveWidget(componentName = "com.red.package/my_test_widget", rank = 2)
         verify(statsLogProxy)
             .writeCommunalHubWidgetEventReported(
                 SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE,
@@ -95,20 +83,14 @@
 
     @Test
     fun logTapWidget_componentNotLoggable_doNotLog() {
-        underTest.logTapWidget(
-            componentName = "com.yellow.package/my_test_widget",
-            rank = 2,
-        )
+        underTest.logTapWidget(componentName = "com.yellow.package/my_test_widget", rank = 2)
         verify(statsLogProxy, never())
-            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
     }
 
     @Test
     fun logTapWidget_componentLoggable_logRemoveEvent() {
-        underTest.logTapWidget(
-            componentName = "com.red.package/my_test_widget",
-            rank = 2,
-        )
+        underTest.logTapWidget(componentName = "com.red.package/my_test_widget", rank = 2)
         verify(statsLogProxy)
             .writeCommunalHubWidgetEventReported(
                 SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP,
@@ -140,4 +122,43 @@
             )
         assertThat(statsEvents).hasSize(1)
     }
+
+    @Test
+    fun logResizeWidget_componentNotLoggable_doNotLog() {
+        underTest.logResizeWidget(
+            componentName = "com.green.package/my_test_widget",
+            rank = 1,
+            spanY = 2,
+        )
+        verify(statsLogProxy, never())
+            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
+    }
+
+    @Test
+    fun logResizeWidget_componentLoggable_logResizeEvent() {
+        underTest.logResizeWidget(
+            componentName = "com.blue.package/my_test_widget",
+            rank = 1,
+            spanY = 2,
+        )
+        verify(statsLogProxy)
+            .writeCommunalHubWidgetEventReported(
+                SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+                "com.blue.package/my_test_widget",
+                rank = 1,
+                spanY = 2,
+            )
+    }
+
+    @Test
+    fun logResizeWidget_defaultSpanY_usesDefaultValue() {
+        underTest.logResizeWidget(componentName = "com.blue.package/my_test_widget", rank = 1)
+        verify(statsLogProxy)
+            .writeCommunalHubWidgetEventReported(
+                SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+                "com.blue.package/my_test_widget",
+                rank = 1,
+                spanY = 0,
+            )
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index cecc11e..6b851cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -353,6 +353,32 @@
         assertThat(event.contentDescription).isEqualTo("Test Clock widget added to lock screen")
     }
 
+    @Test
+    fun onResizeWidget_logsMetrics() =
+        testScope.runTest {
+            val appWidgetId = 123
+            val spanY = 2
+            val widgetIdToRankMap = mapOf(appWidgetId to 1)
+            val componentName = ComponentName("test.package", "TestWidget")
+            val rank = 1
+
+            underTest.onResizeWidget(
+                appWidgetId = appWidgetId,
+                spanY = spanY,
+                widgetIdToRankMap = widgetIdToRankMap,
+                componentName = componentName,
+                rank = rank,
+            )
+
+            verify(communalInteractor).resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+            verify(metricsLogger)
+                .logResizeWidget(
+                    componentName = componentName.flattenToString(),
+                    rank = rank,
+                    spanY = spanY,
+                )
+        }
+
     private companion object {
         val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
         const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 03feceb..b72668b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.StatusBarManager
 import android.content.testableContext
+import android.graphics.Rect
 import android.testing.TestableLooper.RunWithLooper
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -43,6 +44,7 @@
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
 import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
 import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.util.animation.DisappearParameters
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -375,6 +377,92 @@
             }
         }
 
+    @Test
+    fun applyQsScrollPositionForClipping() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                val left = 1f
+                val top = 3f
+                val right = 5f
+                val bottom = 7f
+
+                underTest.applyNewQsScrollerBounds(left, top, right, bottom)
+
+                assertThat(qsMediaHost.currentClipping)
+                    .isEqualTo(Rect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt()))
+            }
+        }
+
+    @Test
+    fun shouldUpdateMediaSquishiness_inSplitShadeFalse_mediaSquishinessSet() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                underTest.isInSplitShade = false
+                underTest.squishinessFraction = 0.3f
+
+                underTest.shouldUpdateSquishinessOnMedia = true
+                Snapshot.sendApplyNotifications()
+                runCurrent()
+
+                assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(0.3f)
+
+                underTest.shouldUpdateSquishinessOnMedia = false
+                Snapshot.sendApplyNotifications()
+                runCurrent()
+                assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(1f)
+            }
+        }
+
+    @Test
+    fun inSplitShade_differentStatusBarState_mediaSquishinessSet() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                underTest.isInSplitShade = true
+                underTest.squishinessFraction = 0.3f
+
+                sysuiStatusBarStateController.setState(StatusBarState.SHADE)
+                Snapshot.sendApplyNotifications()
+                runCurrent()
+                assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(0.3f)
+
+                sysuiStatusBarStateController.setState(StatusBarState.KEYGUARD)
+                runCurrent()
+                Snapshot.sendApplyNotifications()
+                runCurrent()
+                assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f)
+
+                sysuiStatusBarStateController.setState(StatusBarState.SHADE_LOCKED)
+                runCurrent()
+                Snapshot.sendApplyNotifications()
+                runCurrent()
+                assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f)
+            }
+        }
+
+    @Test
+    fun disappearParams() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                setMediaState(ACTIVE_MEDIA)
+
+                setConfigurationForMediaInRow(false)
+                Snapshot.sendApplyNotifications()
+                runCurrent()
+
+                assertThat(underTest.qqsMediaHost.disappearParameters)
+                    .isEqualTo(disappearParamsColumn)
+                assertThat(underTest.qsMediaHost.disappearParameters)
+                    .isEqualTo(disappearParamsColumn)
+
+                setConfigurationForMediaInRow(true)
+                Snapshot.sendApplyNotifications()
+                runCurrent()
+
+                assertThat(underTest.qqsMediaHost.disappearParameters).isEqualTo(disappearParamsRow)
+                assertThat(underTest.qsMediaHost.disappearParameters).isEqualTo(disappearParamsRow)
+            }
+        }
+
     private fun TestScope.setMediaState(state: MediaState) {
         with(kosmos) {
             val activeMedia = state == ACTIVE_MEDIA
@@ -404,6 +492,26 @@
         }
 
         private const val epsilon = 0.001f
+
+        private val disappearParamsColumn =
+            DisappearParameters().apply {
+                fadeStartPosition = 0.95f
+                disappearStart = 0f
+                disappearEnd = 0.95f
+                disappearSize.set(1f, 0f)
+                gonePivot.set(0f, 0f)
+                contentTranslationFraction.set(0f, 1f)
+            }
+
+        private val disappearParamsRow =
+            DisappearParameters().apply {
+                fadeStartPosition = 0.95f
+                disappearStart = 0f
+                disappearEnd = 0.6f
+                disappearSize.set(0f, 0.4f)
+                gonePivot.set(1f, 0f)
+                contentTranslationFraction.set(0.25f, 1f)
+            }
     }
 }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 152911a..af0274b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
 import com.android.systemui.classifier.FalsingCollector
@@ -54,6 +55,7 @@
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
 import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.flags.EnableSceneContainer
@@ -114,6 +116,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
@@ -2356,6 +2359,58 @@
             assertThat(isLockscreenEnabled).isTrue()
         }
 
+    @Test
+    fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() =
+        testScope.runTest {
+            val transitionState =
+                prepareState(
+                    isDeviceUnlocked = false,
+                    initialSceneKey = Scenes.Lockscreen,
+                    authenticationMethod = AuthenticationMethodModel.Pin,
+                )
+            underTest.start()
+
+            val isUnlocked by
+                collectLastValue(
+                    kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
+                )
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val backStack by collectLastValue(sceneBackInteractor.backStack)
+            val isAlternateBouncerVisible by
+                collectLastValue(kosmos.alternateBouncerInteractor.isVisible)
+            assertThat(isUnlocked).isFalse()
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(isAlternateBouncerVisible).isFalse()
+
+            // Change to shade.
+            sceneInteractor.changeScene(Scenes.Shade, "")
+            transitionState.value = ObservableTransitionState.Idle(Scenes.Shade)
+            runCurrent()
+            assertThat(isUnlocked).isFalse()
+            assertThat(currentScene).isEqualTo(Scenes.Shade)
+            assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+            assertThat(isAlternateBouncerVisible).isFalse()
+
+            // Show the alternate bouncer.
+            kosmos.alternateBouncerInteractor.forceShow()
+            kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
+            runCurrent()
+            assertThat(isUnlocked).isFalse()
+            assertThat(currentScene).isEqualTo(Scenes.Shade)
+            assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+            assertThat(isAlternateBouncerVisible).isTrue()
+
+            // Trigger a fingerprint unlock.
+            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+            assertThat(isUnlocked).isTrue()
+            assertThat(currentScene).isEqualTo(Scenes.Shade)
+            assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone)
+            assertThat(isAlternateBouncerVisible).isFalse()
+        }
+
     private fun TestScope.emulateSceneTransition(
         transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
         toScene: SceneKey,
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
deleted file mode 100644
index 8198f03..0000000
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<!-- Extends LinearLayout -->
-<com.android.systemui.statusbar.policy.SplitClockView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    >
-    <TextClock
-        android:id="@+id/time_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:textSize="@dimen/qs_time_collapsed_size"
-        android:textColor="?android:attr/textColorPrimary"
-        />
-    <TextClock
-        android:id="@+id/am_pm_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:textSize="@dimen/qs_time_collapsed_size"
-        android:textColor="?android:attr/textColorPrimary"
-        android:importantForAccessibility="no"
-        />
-
-    <!-- Empty text view so we have the same height when expanded/collapsed-->
-    <TextView
-        android:id="@+id/empty_time_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:textColor="?android:attr/textColorPrimary"
-        />
-</com.android.systemui.statusbar.policy.SplitClockView>
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
index 7cfad60..6423f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
@@ -30,6 +30,7 @@
     @Named(LOGGABLE_PREFIXES) private val loggablePrefixes: List<String>,
     private val statsLogProxy: StatsLogProxy,
 ) {
+
     /** Logs an add widget event for metrics. No-op if widget is not loggable. */
     fun logAddWidget(componentName: String, rank: Int?) {
         if (!componentName.isLoggable()) {
@@ -69,11 +70,21 @@
         )
     }
 
+    fun logResizeWidget(componentName: String, rank: Int, spanY: Int = 0) {
+        if (!componentName.isLoggable()) {
+            return
+        }
+
+        statsLogProxy.writeCommunalHubWidgetEventReported(
+            SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+            componentName,
+            rank = rank,
+            spanY = spanY,
+        )
+    }
+
     /** Logs loggable widgets and the total widget count as a [StatsEvent]. */
-    fun logWidgetsSnapshot(
-        statsEvents: MutableList<StatsEvent>,
-        componentNames: List<String>,
-    ) {
+    fun logWidgetsSnapshot(statsEvents: MutableList<StatsEvent>, componentNames: List<String>) {
         val loggableComponentNames = componentNames.filter { it.isLoggable() }.toTypedArray()
         statsEvents.add(
             statsLogProxy.buildCommunalHubSnapshotStatsEvent(
@@ -95,6 +106,7 @@
             action: Int,
             componentName: String,
             rank: Int,
+            spanY: Int = 0,
         )
 
         /** Builds a [SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT] stats event. */
@@ -112,12 +124,14 @@
         action: Int,
         componentName: String,
         rank: Int,
+        spanY: Int,
     ) {
         SysUiStatsLog.write(
             SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED,
             action,
             componentName,
             rank,
+            spanY,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index e25ea4c..4e0e112 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -178,7 +178,13 @@
      *   alongside the resize, in case resizing also requires re-ordering. This ensures the
      *   re-ordering is done in the same database transaction as the resize.
      */
-    open fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {}
+    open fun onResizeWidget(
+        appWidgetId: Int,
+        spanY: Int,
+        widgetIdToRankMap: Map<Int, Int>,
+        componentName: ComponentName,
+        rank: Int,
+    ) {}
 
     /** Called as the UI requests opening the widget editor with an optional preselected widget. */
     open fun onOpenWidgetEditor(shouldOpenWidgetPickerOnStart: Boolean = false) {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 6508e4b5..ccff230 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -141,8 +141,19 @@
     override fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) =
         communalInteractor.updateWidgetOrder(widgetIdToRankMap)
 
-    override fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+    override fun onResizeWidget(
+        appWidgetId: Int,
+        spanY: Int,
+        widgetIdToRankMap: Map<Int, Int>,
+        componentName: ComponentName,
+        rank: Int,
+    ) {
         communalInteractor.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+        metricsLogger.logResizeWidget(
+            componentName = componentName.flattenToString(),
+            rank = rank,
+            spanY = spanY,
+        )
     }
 
     override fun onReorderWidgetStart() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 51ff598..ec6a17b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -70,6 +70,7 @@
 import androidx.compose.ui.layout.onPlaced
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.layout.positionOnScreen
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
@@ -124,6 +125,7 @@
 import com.android.systemui.qs.ui.composable.QuickSettingsTheme
 import com.android.systemui.res.R
 import com.android.systemui.util.LifecycleFragment
+import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.printSection
 import com.android.systemui.util.println
@@ -447,8 +449,7 @@
     }
 
     override fun setShouldUpdateSquishinessOnMedia(shouldUpdate: Boolean) {
-        super.setShouldUpdateSquishinessOnMedia(shouldUpdate)
-        // TODO (b/353253280)
+        viewModel.shouldUpdateSquishinessOnMedia = shouldUpdate
     }
 
     override fun setInSplitShade(isInSplitShade: Boolean) {
@@ -660,7 +661,20 @@
 
                     Column(
                         modifier =
-                            Modifier.offset {
+                            Modifier.onPlaced { coordinates ->
+                                    val positionOnScreen = coordinates.positionOnScreen()
+                                    val left = positionOnScreen.x
+                                    val right = left + coordinates.size.width
+                                    val top = positionOnScreen.y
+                                    val bottom = top + coordinates.size.height
+                                    viewModel.applyNewQsScrollerBounds(
+                                        left = left,
+                                        top = top,
+                                        right = right,
+                                        bottom = bottom,
+                                    )
+                                }
+                                .offset {
                                     IntOffset(
                                         x = 0,
                                         y = viewModel.qsScrollTranslationY.fastRoundToInt(),
@@ -704,7 +718,10 @@
                         val Media =
                             @Composable {
                                 if (viewModel.qsMediaVisible) {
-                                    MediaObject(mediaHost = viewModel.qsMediaHost)
+                                    MediaObject(
+                                        mediaHost = viewModel.qsMediaHost,
+                                        update = { translationY = viewModel.qsMediaTranslationY },
+                                    )
                                 }
                             }
                         Box(
@@ -987,7 +1004,11 @@
     }
 
 @Composable
-private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) {
+private fun MediaObject(
+    mediaHost: MediaHost,
+    modifier: Modifier = Modifier,
+    update: UniqueObjectHostView.() -> Unit = {},
+) {
     Box {
         AndroidView(
             modifier = modifier,
@@ -1000,6 +1021,7 @@
                         )
                 }
             },
+            update = { view -> view.update() },
             onReset = {},
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index e912a0c..9029563 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -26,6 +26,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.BouncerPanelExpansionCalculator
 import com.android.systemui.Dumpable
@@ -270,6 +271,23 @@
     val qsMediaInRow: Boolean
         get() = qsMediaInRowViewModel.shouldMediaShowInRow
 
+    var shouldUpdateSquishinessOnMedia by mutableStateOf(false)
+
+    val qsMediaTranslationY by derivedStateOf {
+        if (
+            qsExpansion > 0f &&
+                !isKeyguardState &&
+                !qqsMediaVisible &&
+                !qsMediaInRow &&
+                !isInSplitShade
+        ) {
+            val interpolation = Interpolators.ACCELERATE.getInterpolation(1f - qsExpansion)
+            -qsMediaHost.hostView.height * 1.3f * interpolation
+        } else {
+            0f
+        }
+    }
+
     val animateTilesExpansion: Boolean
         get() = inFirstPage && !mediaSuddenlyAppearingInLandscape
 
@@ -297,6 +315,18 @@
                 MediaHostState.EXPANDED
             }
 
+    private val shouldApplySquishinessToMedia by derivedStateOf {
+        shouldUpdateSquishinessOnMedia || (isInSplitShade && statusBarState == StatusBarState.SHADE)
+    }
+
+    private val mediaSquishiness by derivedStateOf {
+        if (shouldApplySquishinessToMedia) {
+            squishinessFraction
+        } else {
+            1f
+        }
+    }
+
     private var qsBounds by mutableStateOf(Rect())
 
     private val constrainedSquishinessFraction: Float
@@ -355,8 +385,6 @@
     private val isOverscrolling: Boolean
         get() = overScrollAmount != 0
 
-    private var shouldUpdateMediaSquishiness by mutableStateOf(false)
-
     private val forceQs by derivedStateOf {
         (isQsExpanded || isStackScrollerOverscrolling) &&
             (isKeyguardState && !showCollapsedOnKeyguard)
@@ -394,11 +422,26 @@
                 ),
         )
 
+    fun applyNewQsScrollerBounds(left: Float, top: Float, right: Float, bottom: Float) {
+        if (usingMedia) {
+            qsMediaHost.currentClipping.set(
+                left.toInt(),
+                top.toInt(),
+                right.toInt(),
+                bottom.toInt(),
+            )
+        }
+    }
+
     override suspend fun onActivated(): Nothing {
         initMediaHosts() // init regardless of using media (same as current QS).
         coroutineScope {
             launch { hydrateSquishinessInteractor() }
-            launch { hydrateQqsMediaExpansion() }
+            if (usingMedia) {
+                launch { hydrateQqsMediaExpansion() }
+                launch { hydrateMediaSquishiness() }
+                launch { hydrateMediaDisappearParameters() }
+            }
             launch { hydrator.activate() }
             launch { containerViewModel.activate() }
             launch { qqsMediaInRowViewModel.activate() }
@@ -429,6 +472,21 @@
         snapshotFlow { qqsMediaExpansion }.collect { qqsMediaHost.expansion = it }
     }
 
+    private suspend fun hydrateMediaSquishiness() {
+        snapshotFlow { mediaSquishiness }.collect { qsMediaHost.squishFraction = it }
+    }
+
+    private suspend fun hydrateMediaDisappearParameters() {
+        coroutineScope {
+            launch {
+                snapshotFlow { qqsMediaInRow }.collect { qqsMediaHost.applyDisappearParameters(it) }
+            }
+            launch {
+                snapshotFlow { qsMediaInRow }.collect { qsMediaHost.applyDisappearParameters(it) }
+            }
+        }
+    }
+
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.asIndenting().run {
             printSection("Quick Settings state") {
@@ -474,6 +532,9 @@
                 println("qsMediaInRow", qsMediaInRow)
                 println("collapsedLandscapeMedia", collapsedLandscapeMedia)
                 println("qqsMediaExpansion", qqsMediaExpansion)
+                println("shouldUpdateSquishinessOnMedia", shouldUpdateSquishinessOnMedia)
+                println("mediaSquishiness", mediaSquishiness)
+                println("qsMediaTranslationY", qsMediaTranslationY)
             }
         }
     }
@@ -510,3 +571,22 @@
         // lazily.
         .onStart { emit(mediaHost.visible) }
 }
+
+// Taken from QSPanelControllerBase
+private fun MediaHost.applyDisappearParameters(inRow: Boolean) {
+    disappearParameters.apply {
+        fadeStartPosition = 0.95f
+        disappearStart = 0f
+        if (inRow) {
+            disappearSize.set(0f, 0.4f)
+            gonePivot.set(1f, 0f)
+            contentTranslationFraction.set(0.25f, 1f)
+            disappearEnd = 0.6f
+        } else {
+            disappearSize.set(1f, 0f)
+            gonePivot.set(0f, 0f)
+            contentTranslationFraction.set(0f, 1f)
+            disappearEnd = 0.95f
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 7ff43c6..fe59c4d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -130,15 +130,7 @@
 
     // TODO(b/361789146): Draw the shapes instead of clipping
     val tileShape = TileDefaults.animateTileShape(uiState.state)
-    val animatedColor by
-        animateColorAsState(
-            if (iconOnly || !uiState.handlesSecondaryClick) {
-                colors.iconBackground
-            } else {
-                colors.background
-            },
-            label = "QSTileBackgroundColor",
-        )
+    val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
 
     TileExpandable(
         color = { animatedColor },
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 580a51a..daeaaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -373,6 +373,7 @@
                                     "device was unlocked with alternate bouncer showing" +
                                         " and shade didn't need to be left open"
                             } else {
+                                replaceLockscreenSceneOnBackStack()
                                 null
                             }
                         }
@@ -391,16 +392,7 @@
                                 val prevScene = previousScene.value
                                 val targetScene = prevScene ?: Scenes.Gone
                                 if (targetScene != Scenes.Gone) {
-                                    sceneBackInteractor.updateBackStack { stack ->
-                                        val list = stack.asIterable().toMutableList()
-                                        check(list.last() == Scenes.Lockscreen) {
-                                            "The bottommost/last SceneKey of the back stack isn't" +
-                                                " the Lockscreen scene like expected. The back" +
-                                                " stack is $stack."
-                                        }
-                                        list[list.size - 1] = Scenes.Gone
-                                        sceneStackOf(*list.toTypedArray())
-                                    }
+                                    replaceLockscreenSceneOnBackStack()
                                 }
                                 targetScene to
                                     "device was unlocked with primary bouncer showing," +
@@ -435,6 +427,20 @@
         }
     }
 
+    /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */
+    private fun replaceLockscreenSceneOnBackStack() {
+        sceneBackInteractor.updateBackStack { stack ->
+            val list = stack.asIterable().toMutableList()
+            check(list.last() == Scenes.Lockscreen) {
+                "The bottommost/last SceneKey of the back stack isn't" +
+                    " the Lockscreen scene like expected. The back" +
+                    " stack is $stack."
+            }
+            list[list.size - 1] = Scenes.Gone
+            sceneStackOf(*list.toTypedArray())
+        }
+    }
+
     private fun handlePowerState() {
         applicationScope.launch {
             powerInteractor.detailedWakefulness.collect { wakefulness ->
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 24dba59..4d77e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -17,8 +17,6 @@
 package com.android.systemui.shade;
 
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
 
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -27,16 +25,13 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
 import android.view.Display;
-import android.view.Gravity;
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.View;
@@ -271,33 +266,7 @@
         // Now that the notification shade encompasses the sliding panel and its
         // translucent backdrop, the entire thing is made TRANSLUCENT and is
         // hardware-accelerated.
-        mLp = new LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                LayoutParams.TYPE_NOTIFICATION_SHADE,
-                LayoutParams.FLAG_NOT_FOCUSABLE
-                        | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
-                        | LayoutParams.FLAG_SPLIT_TOUCH
-                        | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                PixelFormat.TRANSLUCENT);
-        mLp.token = new Binder();
-        mLp.gravity = Gravity.TOP;
-        mLp.setFitInsetsTypes(0 /* types */);
-        mLp.setTitle("NotificationShade");
-        mLp.packageName = mContext.getPackageName();
-        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;
-
-        if (SceneContainerFlag.isEnabled()) {
-            // This prevents the appearance and disappearance of the software keyboard (also known
-            // as the "IME") from scrolling/panning the window to make room for the keyboard.
-            //
-            // The scene container logic does its own adjustment and animation when the IME appears
-            // or disappears.
-            mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-        }
-
+        mLp = ShadeWindowLayoutParams.INSTANCE.create(mContext);
         mWindowManager.addView(mWindowRootView, mLp);
 
         // We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt
new file mode 100644
index 0000000..6bb50f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.shade
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Binder
+import android.view.Gravity
+import android.view.ViewGroup
+import android.view.WindowManager.LayoutParams
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+
+object ShadeWindowLayoutParams {
+    /**
+     * Creates [LayoutParams] for the shade window.
+     *
+     * This is extracted to a single place as those layout params will be used by several places:
+     * - When sysui starts, and the shade is added the first time
+     * - When the shade moves to a different window (e.g. while an external display is connected)
+     */
+    fun create(context: Context): LayoutParams {
+        return LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                LayoutParams.TYPE_NOTIFICATION_SHADE,
+                LayoutParams.FLAG_NOT_FOCUSABLE or
+                    LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING or
+                    LayoutParams.FLAG_SPLIT_TOUCH or
+                    LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+                    LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+                // Now that the notification shade encompasses the sliding panel and its
+                // translucent backdrop, the entire thing is made TRANSLUCENT and is
+                // hardware-accelerated.
+                PixelFormat.TRANSLUCENT,
+            )
+            .apply {
+                token = Binder()
+                gravity = Gravity.TOP
+                fitInsetsTypes = 0
+                title = "NotificationShade"
+                packageName = context.packageName
+                layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+                privateFlags = privateFlags or LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE
+                if (SceneContainerFlag.isEnabled) {
+                    // This prevents the appearance and disappearance of the software keyboard (also
+                    // known as the "IME") from scrolling/panning the window to make room for the
+                    // keyboard.
+                    //
+                    // The scene container logic does its own adjustment and animation when the IME
+                    // appears or disappears.
+                    softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING
+                }
+            }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index e74ed8d..c487ff5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -49,6 +49,7 @@
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
+import android.util.Log;
 import android.view.ContentInfo;
 
 import androidx.annotation.NonNull;
@@ -65,6 +66,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.icon.IconPack;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -194,6 +196,10 @@
      */
     private boolean mIsDemoted = false;
 
+    // TODO(b/377565433): Move into NotificationContentModel during/after
+    //  NotificationRowContentBinderRefactor.
+    private PromotedNotificationContentModel mPromotedNotificationContentModel;
+
     /**
      * True if both
      *  1) app provided full screen intent but does not have the permission to send it
@@ -1061,6 +1067,32 @@
         this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
     }
 
+    /**
+     * Gets the content needed to render this notification as a promoted notification on various
+     * surfaces (like status bar chips and AOD).
+     */
+    public PromotedNotificationContentModel getPromotedNotificationContentModel() {
+        if (PromotedNotificationContentModel.featureFlagEnabled()) {
+            return mPromotedNotificationContentModel;
+        } else {
+            Log.wtf(TAG, "getting promoted content without feature flag enabled");
+            return null;
+        }
+    }
+
+    /**
+     * Sets the content needed to render this notification as a promoted notification on various
+     * surfaces (like status bar chips and AOD).
+     */
+    public void setPromotedNotificationContentModel(
+            @Nullable PromotedNotificationContentModel promotedNotificationContentModel) {
+        if (PromotedNotificationContentModel.featureFlagEnabled()) {
+            this.mPromotedNotificationContentModel = promotedNotificationContentModel;
+        } else {
+            Log.wtf(TAG, "setting promoted content without feature flag enabled");
+        }
+    }
+
     /** Information about a suggestion that is being edited. */
     public static class EditedSuggestionInfo {
 
@@ -1101,4 +1133,6 @@
     private static final long INITIALIZATION_DELAY = 400;
     private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
     private static final int COLOR_INVALID = 1;
+
+    private static final String TAG = "NotificationEntry";
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 23da90d..8edbc5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -173,6 +174,7 @@
             isGroupSummary = sbn.notification.isGroupSummary,
             bucket = bucket,
             callType = sbn.toCallType(),
+            promotedContent = promotedNotificationContentModel,
         )
     }
 }
@@ -199,6 +201,7 @@
     isGroupSummary: Boolean,
     bucket: Int,
     callType: CallType,
+    promotedContent: PromotedNotificationContentModel?,
 ): ActiveNotificationModel {
     return individuals[key]?.takeIf {
         it.isCurrent(
@@ -223,6 +226,7 @@
             contentIntent = contentIntent,
             bucket = bucket,
             callType = callType,
+            promotedContent = promotedContent,
         )
     }
         ?: ActiveNotificationModel(
@@ -247,6 +251,7 @@
             contentIntent = contentIntent,
             bucket = bucket,
             callType = callType,
+            promotedContent = promotedContent,
         )
 }
 
@@ -272,6 +277,7 @@
     isGroupSummary: Boolean,
     bucket: Int,
     callType: CallType,
+    promotedContent: PromotedNotificationContentModel?,
 ): Boolean {
     return when {
         key != this.key -> false
@@ -295,6 +301,9 @@
         contentIntent != this.contentIntent -> false
         bucket != this.bucket -> false
         callType != this.callType -> false
+        // QQQ: Do we need to do the same `isCurrent` thing within the content model to avoid
+        // recreating the active notification model constantly?
+        promotedContent != this.promotedContent -> false
         else -> true
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
new file mode 100644
index 0000000..41ee3b9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.shared.model
+
+import android.annotation.DrawableRes
+import android.graphics.drawable.Icon
+import com.android.internal.widget.NotificationProgressModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+
+/**
+ * The content needed to render a promoted notification to surfaces besides the notification stack,
+ * like the skeleton view on AOD or the status bar chip.
+ */
+data class PromotedNotificationContentModel(
+    val key: String,
+
+    // for all styles:
+    val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+    val appName: CharSequence?,
+    val subText: CharSequence?,
+    val time: When?,
+    val lastAudiblyAlertedMs: Long,
+    @DrawableRes val profileBadgeResId: Int?,
+    val title: CharSequence?,
+    val text: CharSequence?,
+    val skeletonLargeIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+    val style: Style,
+
+    // for CallStyle:
+    val personIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+    val personName: CharSequence?,
+    val verificationIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+    val verificationText: CharSequence?,
+
+    // for ProgressStyle:
+    val progress: NotificationProgressModel?,
+) {
+    class Builder(val key: String) {
+        var skeletonSmallIcon: Icon? = null
+        var appName: CharSequence? = null
+        var subText: CharSequence? = null
+        var time: When? = null
+        var lastAudiblyAlertedMs: Long = 0L
+        @DrawableRes var profileBadgeResId: Int? = null
+        var title: CharSequence? = null
+        var text: CharSequence? = null
+        var skeletonLargeIcon: Icon? = null
+        var style: Style = Style.Ineligible
+
+        // for CallStyle:
+        var personIcon: Icon? = null
+        var personName: CharSequence? = null
+        var verificationIcon: Icon? = null
+        var verificationText: CharSequence? = null
+
+        // for ProgressStyle:
+        var progress: NotificationProgressModel? = null
+
+        fun build() =
+            PromotedNotificationContentModel(
+                key = key,
+                skeletonSmallIcon = skeletonSmallIcon,
+                appName = appName,
+                subText = subText,
+                time = time,
+                lastAudiblyAlertedMs = lastAudiblyAlertedMs,
+                profileBadgeResId = profileBadgeResId,
+                title = title,
+                text = text,
+                skeletonLargeIcon = skeletonLargeIcon,
+                style = style,
+                personIcon = personIcon,
+                personName = personName,
+                verificationIcon = verificationIcon,
+                verificationText = verificationText,
+                progress = progress,
+            )
+    }
+
+    /** The timestamp associated with a notification, along with the mode used to display it. */
+    data class When(val time: Long, val mode: Mode) {
+        /** The mode used to display a notification's `when` value. */
+        enum class Mode {
+            Absolute,
+            CountDown,
+            CountUp,
+        }
+    }
+
+    /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */
+    enum class Style {
+        BigPicture,
+        BigText,
+        Call,
+        Progress,
+        Ineligible,
+    }
+
+    companion object {
+        @JvmStatic
+        fun featureFlagEnabled(): Boolean =
+            PromotedNotificationUi.isEnabled || StatusBarNotifChips.isEnabled
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index 19a92a2..a2b7155 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -17,7 +17,9 @@
 
 import android.app.PendingIntent
 import android.graphics.drawable.Icon
+import android.util.Log
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.stack.PriorityBucket
 
 /**
@@ -36,6 +38,7 @@
     val groupKey: String?,
     /** When this notification was posted. */
     val whenTime: Long,
+    // TODO(b/377566661): Make isPromoted just check if promotedContent != null.
     /** True if this notification should be promoted and false otherwise. */
     val isPromoted: Boolean,
     /** Is this entry in the ambient / minimized section (lowest priority)? */
@@ -78,7 +81,24 @@
     @PriorityBucket val bucket: Int,
     /** The call type set on the notification. */
     val callType: CallType,
-) : ActiveNotificationEntryModel()
+    /**
+     * The content needed to render this as a promoted notification on various surfaces, or null if
+     * this notification cannot be rendered as a promoted notification.
+     */
+    val promotedContent: PromotedNotificationContentModel?,
+) : ActiveNotificationEntryModel() {
+    init {
+        if (!PromotedNotificationContentModel.featureFlagEnabled()) {
+            if (promotedContent != null) {
+                Log.wtf(TAG, "passing non-null promoted content without feature flag enabled")
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "ActiveNotificationEntryModel"
+    }
+}
 
 /** Model for a group of notifications. */
 data class ActiveNotificationGroupModel(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0c511aea..aef26de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -520,6 +520,7 @@
             mListenForCanShowAlternateBouncer.cancel(null);
         }
         mListenForCanShowAlternateBouncer = null;
+
         // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
         mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
                 mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
@@ -568,6 +569,12 @@
     }
 
     private void consumeCanShowAlternateBouncer(boolean canShow) {
+        if (SceneContainerFlag.isEnabled()) {
+            // When the scene framework is enabled, the alternative bouncer is hidden from the scene
+            // framework logic so there's no need for this logic here.
+            return;
+        }
+
         // Hack: this is required to fix issues where
         // KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never
         // reset. This is caused by usages of show()/forceShow() that only read this flow to set the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
deleted file mode 100644
index 0d36b48..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserHandle;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-import android.widget.TextClock;
-
-import com.android.systemui.res.R;
-
-/**
- * Container for a clock which has two separate views for the clock itself and AM/PM indicator. This
- * is used to scale the clock independently of AM/PM.
- */
-public class SplitClockView extends LinearLayout {
-
-    private TextClock mTimeView;
-    private TextClock mAmPmView;
-
-    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_TIME_CHANGED.equals(action)
-                    || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
-                    || Intent.ACTION_LOCALE_CHANGED.equals(action)
-                    || Intent.ACTION_CONFIGURATION_CHANGED.equals(action)
-                    || Intent.ACTION_USER_SWITCHED.equals(action)) {
-                updatePatterns();
-            }
-        }
-    };
-
-    public SplitClockView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mTimeView = findViewById(R.id.time_view);
-        mAmPmView = findViewById(R.id.am_pm_view);
-        mTimeView.setShowCurrentUserTime(true);
-        mAmPmView.setShowCurrentUserTime(true);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_TIME_CHANGED);
-        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
-
-        updatePatterns();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        getContext().unregisterReceiver(mIntentReceiver);
-    }
-
-    private void updatePatterns() {
-        String formatString = DateFormat.getTimeFormatString(getContext(),
-                ActivityManager.getCurrentUser());
-        int index = getAmPmPartEndIndex(formatString);
-        String timeString;
-        String amPmString;
-        if (index == -1) {
-            timeString = formatString;
-            amPmString = "";
-        } else {
-            timeString = formatString.substring(0, index);
-            amPmString = formatString.substring(index);
-        }
-        mTimeView.setFormat12Hour(timeString);
-        mTimeView.setFormat24Hour(timeString);
-        mTimeView.setContentDescriptionFormat12Hour(formatString);
-        mTimeView.setContentDescriptionFormat24Hour(formatString);
-        mAmPmView.setFormat12Hour(amPmString);
-        mAmPmView.setFormat24Hour(amPmString);
-    }
-
-    /**
-     * @return the index where the AM/PM part starts at the end in {@code formatString} including
-     *         leading white spaces or {@code -1} if no AM/PM part is found or {@code formatString}
-     *         doesn't end with AM/PM part
-     */
-    private static int getAmPmPartEndIndex(String formatString) {
-        boolean hasAmPm = false;
-        int length = formatString.length();
-        for (int i = length - 1; i >= 0; i--) {
-            char c = formatString.charAt(i);
-            boolean isAmPm = c == 'a';
-            boolean isWhitespace = Character.isWhitespace(c);
-            if (isAmPm) {
-                hasAmPm = true;
-            }
-            if (isAmPm || isWhitespace) {
-                continue;
-            }
-            if (i == length - 1) {
-
-                // First character was not AM/PM and not whitespace, so it's not ending with AM/PM.
-                return -1;
-            } else {
-
-                // If we have AM/PM at all, return last index, or -1 to indicate that it's not
-                // ending with AM/PM.
-                return hasAmPm ? i + 1 : -1;
-            }
-        }
-
-        // Only AM/PM and whitespaces? The whole string is AM/PM. Else: Only whitespaces in the
-        // string.
-        return hasAmPm ? 0 : -1;
-    }
-
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 32c582f..2ec8016 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -19,6 +19,7 @@
 import android.app.PendingIntent
 import android.graphics.drawable.Icon
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.notification.shared.CallType
 import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
@@ -46,6 +47,7 @@
     contentIntent: PendingIntent? = null,
     bucket: Int = BUCKET_UNKNOWN,
     callType: CallType = CallType.None,
+    promotedContent: PromotedNotificationContentModel? = null,
 ) =
     ActiveNotificationModel(
         key = key,
@@ -69,4 +71,5 @@
         isGroupSummary = isGroupSummary,
         bucket = bucket,
         callType = callType,
+        promotedContent = promotedContent,
     )
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
index 44ae1d1..81e83b5 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
@@ -16,7 +16,6 @@
 
 package com.android.server.appfunctions;
 
-import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -24,15 +23,21 @@
 /** Executors for App function operations. */
 public final class AppFunctionExecutors {
 
+    static final int sConcurrency = Runtime.getRuntime().availableProcessors();
+
     /** Executor for operations that do not need to block. */
-    public static final Executor THREAD_POOL_EXECUTOR =
+    public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR =
             new ThreadPoolExecutor(
-                    /* corePoolSize= */ Runtime.getRuntime().availableProcessors(),
-                    /* maxConcurrency= */ Runtime.getRuntime().availableProcessors(),
-                    /* keepAliveTime= */ 0L,
+                    /* corePoolSize= */ sConcurrency,
+                    /* maxConcurrency= */ sConcurrency,
+                    /* keepAliveTime= */ 1L,
                     /* unit= */ TimeUnit.SECONDS,
                     /* workQueue= */ new LinkedBlockingQueue<>(),
                     new NamedThreadFactory("AppFunctionExecutors"));
 
+    static {
+        THREAD_POOL_EXECUTOR.allowCoreThreadTimeOut(true);
+    }
+
     private AppFunctionExecutors() {}
 }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8567ccb..b221d74 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -187,11 +187,6 @@
     // Simple flag to enable/disable debug logging.
     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
-    // String constants for XML schema migration related to changes in keyguard package.
-    private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
-    private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
-    private static final int KEYGUARD_HOST_ID = 0x4b455947;
-
     // Filename for app widgets state persisted on disk.
     private static final String STATE_FILENAME = "appwidgets.xml";
 
@@ -211,6 +206,7 @@
     private static final int UNKNOWN_USER_ID = -10;
 
     // Version of XML schema for app widgets. Bump if the stored widgets need to be upgraded.
+    // Version 1 introduced in 2014 - Android 5.0
     private static final int CURRENT_VERSION = 1;
 
     // Every widget update request is associated which an increasing sequence number. This is
@@ -4428,19 +4424,11 @@
 
         int version = fromVersion;
 
-        // Update 1: keyguard moved from package "android" to "com.android.keyguard"
+        // Update 1: From version 0 to 1, was used from Android 4 to Android 5. It updated the
+        // location of the keyguard widget database. No modern device will have db version 0.
         if (version == 0) {
-            HostId oldHostId = new HostId(Process.myUid(),
-                    KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE);
-
-            Host host = lookupHostLocked(oldHostId);
-            if (host != null) {
-                final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
-                        UserHandle.USER_SYSTEM);
-                if (uid >= 0) {
-                    host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
-                }
-            }
+            Slog.e(TAG, "Found widget database with version 0, this should not be possible,"
+                    + " forcing upgrade to version 1");
 
             version = 1;
         }
@@ -4450,24 +4438,8 @@
         }
     }
 
-    private static File getStateFile(int userId) {
-        return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME);
-    }
-
     private static AtomicFile getSavedStateFile(int userId) {
-        File dir = Environment.getUserSystemDirectory(userId);
-        File settingsFile = getStateFile(userId);
-        if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
-            if (!dir.exists()) {
-                dir.mkdirs();
-            }
-            // Migrate old data
-            File oldFile = new File("/data/system/" + STATE_FILENAME);
-            // Method doesn't throw an exception on failure. Ignore any errors
-            // in moving the file (like non-existence)
-            oldFile.renameTo(settingsFile);
-        }
-        return new AtomicFile(settingsFile);
+        return new AtomicFile(new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME));
     }
 
     void onUserStopped(int userId) {
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index bb4ae96..a132876b 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -46,7 +46,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
-import com.android.server.integrity.model.RuleMetadata;
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
deleted file mode 100644
index b0647fc..0000000
--- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2019 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.server.integrity.model;
-
-import android.annotation.Nullable;
-import android.content.integrity.Rule;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A class encapsulating the result from the evaluation engine after evaluating rules against app
- * install metadata.
- *
- * <p>It contains the outcome effect (whether to allow or block the install), and the rule causing
- * that effect.
- */
-public final class IntegrityCheckResult {
-
-    public enum Effect {
-        ALLOW,
-        DENY
-    }
-
-    private final Effect mEffect;
-    private final List<Rule> mRuleList;
-
-    private IntegrityCheckResult(Effect effect, @Nullable List<Rule> ruleList) {
-        this.mEffect = effect;
-        this.mRuleList = ruleList;
-    }
-
-    public Effect getEffect() {
-        return mEffect;
-    }
-
-    public List<Rule> getMatchedRules() {
-        return mRuleList;
-    }
-
-    /**
-     * Create an ALLOW evaluation outcome.
-     *
-     * @return An evaluation outcome with ALLOW effect and no rule.
-     */
-    public static IntegrityCheckResult allow() {
-        return new IntegrityCheckResult(Effect.ALLOW, Collections.emptyList());
-    }
-
-    /**
-     * Create an ALLOW evaluation outcome.
-     *
-     * @return An evaluation outcome with ALLOW effect and rule causing that effect.
-     */
-    public static IntegrityCheckResult allow(List<Rule> ruleList) {
-        return new IntegrityCheckResult(Effect.ALLOW, ruleList);
-    }
-
-    /**
-     * Create a DENY evaluation outcome.
-     *
-     * @param ruleList All valid rules that cause the DENY effect.
-     * @return An evaluation outcome with DENY effect and rule causing that effect.
-     */
-    public static IntegrityCheckResult deny(List<Rule> ruleList) {
-        return new IntegrityCheckResult(Effect.DENY, ruleList);
-    }
-
-    /** Returns true when the {@code mEffect} is caused by an app certificate mismatch. */
-    public boolean isCausedByAppCertRule() {
-        return mRuleList.stream().anyMatch(rule -> rule.getFormula().isAppCertificateFormula());
-    }
-
-    /** Returns true when the {@code mEffect} is caused by an installer rule. */
-    public boolean isCausedByInstallerRule() {
-        return mRuleList.stream().anyMatch(rule -> rule.getFormula().isInstallerFormula());
-    }
-
-}
diff --git a/services/core/java/com/android/server/integrity/model/RuleMetadata.java b/services/core/java/com/android/server/integrity/model/RuleMetadata.java
deleted file mode 100644
index 6b582ae..0000000
--- a/services/core/java/com/android/server/integrity/model/RuleMetadata.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 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.server.integrity.model;
-
-import android.annotation.Nullable;
-
-/** Data class containing relevant metadata associated with a rule set. */
-public class RuleMetadata {
-
-    private final String mRuleProvider;
-    private final String mVersion;
-
-    public RuleMetadata(String ruleProvider, String version) {
-        mRuleProvider = ruleProvider;
-        mVersion = version;
-    }
-
-    @Nullable
-    public String getRuleProvider() {
-        return mRuleProvider;
-    }
-
-    @Nullable
-    public String getVersion() {
-        return mVersion;
-    }
-}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 7469c92..09feb18 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -36,6 +36,7 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
+import android.health.connect.HealthPermissions;
 import android.media.RingtoneManager;
 import android.media.midi.MidiManager;
 import android.net.Uri;
@@ -48,6 +49,7 @@
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.permission.PermissionManager;
+import android.permission.flags.Flags;
 import android.print.PrintManager;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
@@ -214,8 +216,13 @@
 
     private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
     static {
-        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
-        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+        if (Flags.replaceBodySensorPermissionEnabled()) {
+            SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEART_RATE);
+            SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
+        } else {
+            SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+            SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+        }
     }
 
     private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 93aa10b..fd22118 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -68,7 +68,6 @@
 
 import com.android.internal.R;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.integrity.model.IntegrityCheckResult;
 import com.android.server.testutils.TestUtils;
 
 import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
deleted file mode 100644
index d31ed68..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 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.server.integrity.model;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.Rule;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-@RunWith(JUnit4.class)
-public class IntegrityCheckResultTest {
-
-    @Test
-    public void createAllowResult() {
-        IntegrityCheckResult allowResult = IntegrityCheckResult.allow();
-
-        assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-        assertThat(allowResult.getMatchedRules()).isEmpty();
-    }
-
-    @Test
-    public void createAllowResultWithRule() {
-        String packageName = "com.test.deny";
-        Rule forceAllowRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
-                                packageName),
-                        Rule.FORCE_ALLOW);
-
-        IntegrityCheckResult allowResult =
-                IntegrityCheckResult.allow(Collections.singletonList(forceAllowRule));
-
-        assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-        assertThat(allowResult.getMatchedRules()).containsExactly(forceAllowRule);
-    }
-
-    @Test
-    public void createDenyResultWithRule() {
-        String packageName = "com.test.deny";
-        Rule failedRule =
-                new Rule(
-                        new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
-                                packageName),
-                        Rule.DENY);
-
-        IntegrityCheckResult denyResult =
-                IntegrityCheckResult.deny(Collections.singletonList(failedRule));
-
-        assertThat(denyResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.DENY);
-        assertThat(denyResult.getMatchedRules()).containsExactly(failedRule);
-    }
-
-    @Test
-    public void isDenyCausedByAppCertificate() {
-        String packageName = "com.test.deny";
-        String appCert = "app-cert";
-        Rule failedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.AND,
-                                Arrays.asList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME, packageName),
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.APP_CERTIFICATE, appCert))),
-                        Rule.DENY);
-        Rule otherFailedRule =
-                new Rule(
-                        new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
-                                AtomicFormula.EQ, 12),
-                        Rule.DENY);
-
-        IntegrityCheckResult denyResult =
-                IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule));
-
-        assertThat(denyResult.isCausedByAppCertRule()).isTrue();
-        assertThat(denyResult.isCausedByInstallerRule()).isFalse();
-    }
-
-    @Test
-    public void isDenyCausedByInstaller() {
-        String packageName = "com.test.deny";
-        String appCert = "app-cert";
-        Rule failedRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.AND,
-                                Arrays.asList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.PACKAGE_NAME, packageName),
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.INSTALLER_CERTIFICATE, appCert))),
-                        Rule.DENY);
-        Rule otherFailedRule =
-                new Rule(
-                        new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
-                                AtomicFormula.EQ, 12),
-                        Rule.DENY);
-
-        IntegrityCheckResult denyResult =
-                IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule));
-
-        assertThat(denyResult.isCausedByAppCertRule()).isFalse();
-        assertThat(denyResult.isCausedByInstallerRule()).isTrue();
-    }
-}