Merge "Added remoteauth enrollment/settings feature flag." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 16170d9..29349cb 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -25,6 +25,8 @@
         ":com.android.text.flags-aconfig-java{.generated_srcjars}",
         ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
     ],
+    // Add aconfig-annotations-lib as a dependency for the optimization
+    libs: ["aconfig-annotations-lib"],
 }
 
 // Default flags for java_aconfig_libraries that go into framework-minus-apex
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7f1e289..e4846b8 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -529,6 +529,7 @@
   public static final class Settings.Config extends android.provider.Settings.NameValueTable {
     method @RequiresPermission(android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS) public static void clearMonitorCallback(@NonNull android.content.ContentResolver);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteString(@NonNull String, @NonNull String);
+    method @NonNull public static java.util.Map<java.lang.String,java.lang.String> getAllStrings();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static java.util.Map<java.lang.String,java.lang.String> getStrings(@NonNull String, @NonNull java.util.List<java.lang.String>);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static int getSyncDisabledMode();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cbcc2bc..29b5213 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2712,7 +2712,7 @@
 package android.os.vibrator.persistence {
 
   public class ParsedVibration {
-    method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffectListForTesting();
+    method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects();
     method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator);
   }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 56fef1a..dab4110 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1486,13 +1486,13 @@
             AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
 
     /**
-     * Allows the assistant app to get the negative trigger data from the PCC sandbox to improve the
+     * Allows the assistant app to get the training data from the PCC sandbox to improve the
      * hotword training model.
      *
      * @hide
      */
-    public static final int OP_RECEIVE_SANDBOX_NEGATIVE_DATA_AUDIO =
-            AppProtoEnums.APP_OP_RECEIVE_SANDBOX_NEGATIVE_DATA_AUDIO;
+    public static final int OP_RECEIVE_SANDBOX_TRAINING_DATA =
+            AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRAINING_DATA;
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1640,7 +1640,7 @@
             OPSTR_CAMERA_SANDBOXED,
             OPSTR_RECORD_AUDIO_SANDBOXED,
             OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
-            OPSTR_RECEIVE_SANDBOX_NEGATIVE_DATA_AUDIO
+            OPSTR_RECEIVE_SANDBOX_TRAINING_DATA
     })
     public @interface AppOpString {}
 
@@ -2261,13 +2261,13 @@
             "android:receive_sandbox_trigger_audio";
 
     /**
-     * Allows the assistant app to get the negative trigger data from the PCC sandbox to improve
+     * Allows the assistant app to get the training data from the PCC sandbox to improve
      * the hotword training model.
      *
      * @hide
      */
-    public static final String OPSTR_RECEIVE_SANDBOX_NEGATIVE_DATA_AUDIO =
-            "android:receive_sandbox_negative_data_audio";
+    public static final String OPSTR_RECEIVE_SANDBOX_TRAINING_DATA =
+            "android:receive_sandbox_training_data";
 
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -2811,9 +2811,9 @@
                 OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
                 "RECEIVE_SANDBOX_TRIGGER_AUDIO")
                 .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
-        new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_NEGATIVE_DATA_AUDIO,
-                OPSTR_RECEIVE_SANDBOX_NEGATIVE_DATA_AUDIO,
-                "RECEIVE_SANDBOX_NEGATIVE_DATA_AUDIO").build()
+        new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRAINING_DATA,
+                OPSTR_RECEIVE_SANDBOX_TRAINING_DATA,
+                "RECEIVE_SANDBOX_TRAINING_DATA").build()
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index d66fca8..ed0f872 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2495,7 +2495,8 @@
                     + ", hints=" + Arrays.toString(node.getAutofillHints())
                     + ", value=" + node.getAutofillValue()
                     + ", sanitized=" + node.isSanitized()
-                    + ", important=" + node.getImportantForAutofill());
+                    + ", important=" + node.getImportantForAutofill()
+                    + ", visibility=" + node.getVisibility());
         }
 
         final int NCHILDREN = node.getChildCount();
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index ea21d51..ab669cc 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -48,9 +48,6 @@
             "name":"CarrierAppIntegrationTestCases"
         },
         {
-            "name":"ApkVerityTest"
-        },
-        {
             "name":"CtsSilentUpdateHostTestCases"
         },
         {
diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java
index a76f597..ded74ea 100644
--- a/core/java/android/os/vibrator/persistence/ParsedVibration.java
+++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java
@@ -70,7 +70,7 @@
     @TestApi
     @VisibleForTesting
     @NonNull
-    public List<VibrationEffect> getVibrationEffectListForTesting() {
+    public List<VibrationEffect> getVibrationEffects() {
         return Collections.unmodifiableList(mEffects);
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index abc0c7a..82756af 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2831,7 +2831,7 @@
     /** @hide - Private call() method to query the 'global' table */
     public static final String CALL_METHOD_LIST_GLOBAL = "LIST_global";
 
-    /** @hide - Private call() method to reset to defaults the 'configuration' table */
+    /** @hide - Private call() method to query the 'configuration' table */
     public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
 
     /** @hide - Private call() method to disable / re-enable syncs to the 'configuration' table */
@@ -10664,20 +10664,6 @@
                 "search_press_hold_nav_handle_enabled";
 
         /**
-         * Control whether Trust Agents are in active unlock or extend unlock mode.
-         * @hide
-         */
-        @Readable
-        public static final String TRUST_AGENTS_EXTEND_UNLOCK = "trust_agents_extend_unlock";
-
-        /**
-         * Control whether the screen locks when trust is lost.
-         * @hide
-         */
-        @Readable
-        public static final String LOCK_SCREEN_WHEN_TRUST_LOST = "lock_screen_when_trust_lost";
-
-        /**
          * Control whether Night display is currently activated.
          * @hide
          */
@@ -19423,6 +19409,36 @@
         }
 
         /**
+         * Return all stored flags.
+         *
+         * The keys take the form {@code namespace/flag}, and the values are the flag values.
+         *
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @NonNull
+        public static Map<String, String> getAllStrings() {
+            HashMap<String, String> allFlags = new HashMap<String, String>();
+            try {
+                ContentResolver resolver = getContentResolver();
+                Bundle arg = new Bundle();
+                arg.putInt(Settings.CALL_METHOD_USER_KEY, resolver.getUserId());
+                IContentProvider cp = sProviderHolder.getProvider(resolver);
+                Bundle b = cp.call(resolver.getAttributionSource(),
+                        sProviderHolder.mUri.getAuthority(), CALL_METHOD_LIST_CONFIG, null, arg);
+                if (b != null) {
+                    Map<String, String> flagsToValues =
+                            (HashMap) b.getSerializable(Settings.NameValueTable.VALUE,
+                                            java.util.HashMap.class);
+                    allFlags.putAll(flagsToValues);
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Can't query configuration table for " + CONTENT_URI, e);
+            }
+            return allFlags;
+        }
+
+        /**
          * Look up a list of names in the database, within the specified namespace.
          *
          * @param resolver to access the database with
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index c7e5453..637770c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1307,9 +1307,9 @@
                     visibleFrame.intersect(mInsetsState.getDisplayFrame());
                     WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame,
                             null /* ignoringVisibilityState */, config.isScreenRound(),
-                            false /* alwaysConsumeSystemBars */, mLayout.softInputMode,
-                            mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
-                            config.windowConfiguration.getWindowingMode(), null /* idSideMap */);
+                            mLayout.softInputMode, mLayout.flags, SYSTEM_UI_FLAG_VISIBLE,
+                            mLayout.type, config.windowConfiguration.getActivityType(),
+                            null /* idSideMap */);
 
                     if (!fixedSize) {
                         final Rect padding = mIWallpaperEngine.mDisplayPadding;
diff --git a/core/java/android/text/flags/deprecate_fonts_xml.aconfig b/core/java/android/text/flags/deprecate_fonts_xml.aconfig
new file mode 100644
index 0000000..58dc210
--- /dev/null
+++ b/core/java/android/text/flags/deprecate_fonts_xml.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+  name: "deprecate_fonts_xml"
+  namespace: "text"
+  description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
+  bug: "281769620"
+}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index fabfed3..1ec7c41 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.EventLogTags.IMF_IME_ANIM_CANCEL;
 import static android.view.EventLogTags.IMF_IME_ANIM_FINISH;
 import static android.view.EventLogTags.IMF_IME_ANIM_START;
@@ -39,6 +39,7 @@
 import static android.view.InsetsState.ISIDE_RIGHT;
 import static android.view.InsetsState.ISIDE_TOP;
 import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
 import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
@@ -62,7 +63,6 @@
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation.Bounds;
-import android.view.WindowManager.LayoutParams;
 import android.view.animation.Interpolator;
 import android.view.inputmethod.ImeTracker;
 
@@ -396,10 +396,9 @@
     private Insets getInsetsFromState(InsetsState state, Rect frame,
             @Nullable @InternalInsetsSide SparseIntArray idSideMap) {
         return state.calculateInsets(frame, null /* ignoringVisibilityState */,
-                false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
-                LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
+                false /* isScreenRound */, SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode */,
                 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, idSideMap).getInsets(mTypes);
+                ACTIVITY_TYPE_UNDEFINED, idSideMap).getInsets(mTypes);
     }
 
     /** Computes the insets relative to the given frame. */
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8ec7d67..fb24211 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -652,7 +652,7 @@
     private int mLastLegacySoftInputMode;
     private int mLastLegacyWindowFlags;
     private int mLastLegacySystemUiFlags;
-    private int mLastWindowingMode;
+    private int mLastActivityType;
     private boolean mStartingAnimation;
     private int mCaptionInsetsHeight = 0;
     private int mImeCaptionBarInsetsHeight = 0;
@@ -800,10 +800,10 @@
                 }
             }
 
-            WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
-                    mLastInsets.isRound(), false /* alwaysConsumeSystemBars */,
+            WindowInsets insets = state.calculateInsets(mFrame,
+                    mState /* ignoringVisibilityState */, mLastInsets.isRound(),
                     mLastLegacySoftInputMode, mLastLegacyWindowFlags, mLastLegacySystemUiFlags,
-                    mWindowType, mLastWindowingMode, null /* idSideMap */);
+                    mWindowType, mLastActivityType, null /* idSideMap */);
             mHost.dispatchWindowInsetsAnimationProgress(insets,
                     Collections.unmodifiableList(runningAnimations));
             if (DEBUG) {
@@ -939,30 +939,29 @@
     }
 
     /**
-     * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, boolean, int, int, int, int,
-     *      int, android.util.SparseIntArray)
+     * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, int, int, int, int, int,
+     *      android.util.SparseIntArray)
      */
     @VisibleForTesting
-    public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars,
-            int windowType, int windowingMode, int legacySoftInputMode, int legacyWindowFlags,
-            int legacySystemUiFlags) {
+    public WindowInsets calculateInsets(boolean isScreenRound, int windowType, int activityType,
+            int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) {
         mWindowType = windowType;
-        mLastWindowingMode = windowingMode;
+        mLastActivityType = activityType;
         mLastLegacySoftInputMode = legacySoftInputMode;
         mLastLegacyWindowFlags = legacyWindowFlags;
         mLastLegacySystemUiFlags = legacySystemUiFlags;
-        mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
-                isScreenRound, alwaysConsumeSystemBars, legacySoftInputMode, legacyWindowFlags,
-                legacySystemUiFlags, windowType, windowingMode, null /* idSideMap */);
+        mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState */,
+                isScreenRound, legacySoftInputMode, legacyWindowFlags,
+                legacySystemUiFlags, windowType, activityType, null /* idSideMap */);
         return mLastInsets;
     }
 
     /**
      * @see InsetsState#calculateVisibleInsets(Rect, int, int, int, int)
      */
-    public Insets calculateVisibleInsets(int windowType, int windowingMode,
+    public Insets calculateVisibleInsets(int windowType, int activityType,
             @SoftInputModeFlags int softInputMode, int windowFlags) {
-        return mState.calculateVisibleInsets(mFrame, windowType, windowingMode, softInputMode,
+        return mState.calculateVisibleInsets(mFrame, windowType, activityType, softInputMode,
                 windowFlags);
     }
 
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index af24140..59e0932 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
 import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
@@ -39,7 +40,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.ActivityType;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Parcel;
@@ -136,9 +137,8 @@
      * @return The calculated insets.
      */
     public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
-            boolean isScreenRound, boolean alwaysConsumeSystemBars,
-            int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
-            int windowType, @WindowConfiguration.WindowingMode int windowingMode,
+            boolean isScreenRound, int legacySoftInputMode, int legacyWindowFlags,
+            int legacySystemUiFlags, int windowType, @ActivityType int activityType,
             @Nullable @InternalInsetsSide SparseIntArray idSideMap) {
         Insets[] typeInsetsMap = new Insets[Type.SIZE];
         Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
@@ -185,9 +185,8 @@
         if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
             compatInsetsTypes &= ~statusBars();
         }
-        if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
-            // Clear all types but forceConsumingTypes.
-            compatInsetsTypes &= forceConsumingTypes;
+        if (clearsCompatInsets(windowType, legacyWindowFlags, activityType, forceConsumingTypes)) {
+            compatInsetsTypes = 0;
         }
 
         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
@@ -295,26 +294,27 @@
         return insets;
     }
 
-    public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode,
+    public Insets calculateVisibleInsets(Rect frame, int windowType, @ActivityType int activityType,
             @SoftInputModeFlags int softInputMode, int windowFlags) {
-        final boolean clearsCompatInsets = clearsCompatInsets(
-                windowType, windowFlags, windowingMode);
         final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST;
         final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING
                 ? systemBars() | ime()
                 : systemBars();
+        @InsetsType int forceConsumingTypes = 0;
         Insets insets = Insets.NONE;
         for (int i = mSources.size() - 1; i >= 0; i--) {
             final InsetsSource source = mSources.valueAt(i);
             if ((source.getType() & visibleInsetsTypes) == 0) {
                 continue;
             }
-            if (clearsCompatInsets && !source.hasFlags(FLAG_FORCE_CONSUMING)) {
-                continue;
+            if (source.hasFlags(FLAG_FORCE_CONSUMING)) {
+                forceConsumingTypes |= source.getType();
             }
             insets = Insets.max(source.calculateVisibleInsets(frame), insets);
         }
-        return insets;
+        return clearsCompatInsets(windowType, windowFlags, activityType, forceConsumingTypes)
+                ? Insets.NONE
+                : insets;
     }
 
     /**
@@ -662,10 +662,15 @@
         mSources.put(source.getId(), source);
     }
 
-    public static boolean clearsCompatInsets(int windowType, int windowFlags, int windowingMode) {
+    public static boolean clearsCompatInsets(int windowType, int windowFlags,
+            @ActivityType int activityType, @InsetsType int forceConsumingTypes) {
         return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0
+                // For compatibility reasons, this excludes the wallpaper, the system error windows,
+                // and the app windows while any system bar is forcibly consumed.
                 && windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR
-                && !WindowConfiguration.inMultiWindowMode(windowingMode);
+                // This ensures the app content won't be obscured by compat insets even if the app
+                // has FLAG_LAYOUT_NO_LIMITS.
+                && (forceConsumingTypes == 0 || activityType != ACTIVITY_TYPE_STANDARD);
     }
 
     public void dump(String prefix, PrintWriter pw) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e0fda7e..60d964e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2848,16 +2848,15 @@
         if (mLastWindowInsets == null || forceConstruct) {
             final Configuration config = getConfiguration();
             mLastWindowInsets = mInsetsController.calculateInsets(
-                    config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
-                    mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
-                    mWindowAttributes.softInputMode, mWindowAttributes.flags,
-                    (mWindowAttributes.systemUiVisibility
+                    config.isScreenRound(), mWindowAttributes.type,
+                    config.windowConfiguration.getActivityType(), mWindowAttributes.softInputMode,
+                    mWindowAttributes.flags, (mWindowAttributes.systemUiVisibility
                             | mWindowAttributes.subtreeSystemUiVisibility));
 
             mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
             mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
             mAttachInfo.mVisibleInsets.set(mInsetsController.calculateVisibleInsets(
-                    mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
+                    mWindowAttributes.type, config.windowConfiguration.getActivityType(),
                     mWindowAttributes.softInputMode, mWindowAttributes.flags).toRect());
         }
         return mLastWindowInsets;
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 2858f0a..e32c8e5 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -16,7 +16,7 @@
 
 package android.window;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 
@@ -80,7 +80,7 @@
         final Rect bounds;
         final float density;
         final boolean isScreenRound;
-        final int windowingMode;
+        final int activityType;
         synchronized (ResourcesManager.getInstance()) {
             final Configuration config = mContext.getResources().getConfiguration();
             final WindowConfiguration winConfig = config.windowConfiguration;
@@ -90,11 +90,11 @@
             // as DisplayMetrics#density
             density = config.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
             isScreenRound = config.isScreenRound();
-            windowingMode = winConfig.getWindowingMode();
+            activityType = winConfig.getActivityType();
         }
         final IBinder token = Context.getToken(mContext);
         final Supplier<WindowInsets> insetsSupplier = () -> getWindowInsetsFromServerForDisplay(
-                mContext.getDisplayId(), token, bounds, isScreenRound, windowingMode);
+                mContext.getDisplayId(), token, bounds, isScreenRound, activityType);
         return new WindowMetrics(new Rect(bounds), insetsSupplier, density);
     }
 
@@ -105,23 +105,22 @@
      * @param token the token of Activity or WindowContext
      * @param bounds the window bounds to calculate insets for
      * @param isScreenRound if the display identified by displayId is round
-     * @param windowingMode the windowing mode of the window to calculate insets for
+     * @param activityType the activity type of the window to calculate insets for
      * @return WindowInsets calculated for the given window bounds, on the given display
      */
     private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId, IBinder token,
-            Rect bounds, boolean isScreenRound, int windowingMode) {
+            Rect bounds, boolean isScreenRound, int activityType) {
         try {
             final InsetsState insetsState = new InsetsState();
-            final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
-                    .getWindowInsets(displayId, token, insetsState);
+            WindowManagerGlobal.getWindowManagerService().getWindowInsets(
+                    displayId, token, insetsState);
             final float overrideInvScale = CompatibilityInfo.getOverrideInvertedScale();
             if (overrideInvScale != 1f) {
                 insetsState.scale(overrideInvScale);
             }
             return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState */,
-                    isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING,
-                    0 /* flags */, SYSTEM_UI_FLAG_VISIBLE,
-                    WindowManager.LayoutParams.INVALID_WINDOW_TYPE, windowingMode,
+                    isScreenRound, SOFT_INPUT_ADJUST_NOTHING, 0 /* flags */, SYSTEM_UI_FLAG_VISIBLE,
+                    WindowManager.LayoutParams.INVALID_WINDOW_TYPE, activityType,
                     null /* idSideMap */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -157,7 +156,7 @@
                     currentDisplayInfo.displayId, null /* token */,
                     new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
                             currentDisplayInfo.getNaturalHeight()), isScreenRound,
-                    WINDOWING_MODE_FULLSCREEN);
+                    ACTIVITY_TYPE_UNDEFINED);
             // Set the hardware-provided insets.
             windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
                             currentDisplayInfo.roundedCorners)
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index bb86801..86ca077 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1113,15 +1113,14 @@
             if (insets != null) {
                 mLastForceConsumingTypes = insets.getForceConsumingTypes();
 
-                @InsetsType int compatInsetsTypes =
+                final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags,
+                        getResources().getConfiguration().windowConfiguration.getActivityType(),
+                        mLastForceConsumingTypes);
+                final @InsetsType int compatInsetsTypes =
                         WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout();
-                if (clearsCompatInsets(attrs.type, attrs.flags,
-                        getResources().getConfiguration().windowConfiguration.getWindowingMode())) {
-                    compatInsetsTypes &= mLastForceConsumingTypes;
-                }
                 final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
                         WindowInsets.Type.systemBars());
-                final Insets systemInsets = compatInsetsTypes == 0
+                final Insets systemInsets = clearsCompatInsets
                         ? Insets.NONE
                         : Insets.min(insets.getInsets(compatInsetsTypes), stableBarInsets);
                 mLastTopInset = systemInsets.top;
diff --git a/core/java/com/android/internal/security/TEST_MAPPING b/core/java/com/android/internal/security/TEST_MAPPING
index 2d598dc..0af3b03 100644
--- a/core/java/com/android/internal/security/TEST_MAPPING
+++ b/core/java/com/android/internal/security/TEST_MAPPING
@@ -12,10 +12,6 @@
       ]
     },
     {
-      "name": "ApkVerityTest",
-      "file_patterns": ["VerityUtils\\.java"]
-    },
-    {
       "name": "UpdatableSystemFontTest",
       "file_patterns": ["VerityUtils\\.java"]
     },
@@ -23,5 +19,11 @@
       "name": "CtsApkVerityInstallHostTestCases",
       "file_patterns": ["VerityUtils\\.java"]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "FsVerityTest",
+      "file_patterns": ["VerityUtils\\.java"]
+    }
   ]
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a72f779..7bfc7de 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1185,6 +1185,9 @@
     -->
     <integer name="config_shortPressOnSleepBehavior">0</integer>
 
+    <!-- Whether to silence telephony ringer on sleep key event -->
+    <bool name="config_silenceRingerOnSleepKey">false</bool>
+
     <!-- Control the behavior when the user long presses the stem primary button.
          Stem primary button is only used on watch form factor. If a device is not
          a watch, setting this config is no-op.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ee0563b..c903e33 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -464,6 +464,7 @@
   <java-symbol type="integer" name="config_toastDefaultGravity" />
   <java-symbol type="integer" name="config_triplePressOnPowerBehavior" />
   <java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
+  <java-symbol type="bool" name="config_silenceRingerOnSleepKey" />
   <java-symbol type="integer" name="config_longPressOnStemPrimaryBehavior" />
   <java-symbol type="integer" name="config_shortPressOnStemPrimaryBehavior" />
   <java-symbol type="string" name="config_primaryShortPressTargetActivity" />
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 1ea20f1..349fe7b 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -53,6 +53,7 @@
     private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
     private static final String DEFAULT_VALUE = "test_default_value";
     private static final String NAMESPACE = "namespace1";
+    private static final String NAMESPACE2 = "namespace2";
     private static final String KEY = "key1";
     private static final String KEY2 = "key2";
     private static final String KEY3 = "key3";
@@ -67,6 +68,89 @@
         deleteViaContentProvider(NAMESPACE, KEY);
         deleteViaContentProvider(NAMESPACE, KEY2);
         deleteViaContentProvider(NAMESPACE, KEY3);
+        DeviceConfig.clearAllLocalOverrides();
+    }
+
+    /**
+     * Test that creating a sticky local override for a flag prevents further writes to that flag.
+     */
+    @Test
+    public void testAddStickyLocalOverridePreventsWrites() {
+        DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE);
+
+        String key1Value = DeviceConfig.getProperty(NAMESPACE, KEY);
+        assertThat(key1Value).isEqualTo(VALUE);
+
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE2, /* makeDefault= */ false);
+        key1Value = DeviceConfig.getProperty(NAMESPACE, KEY);
+        assertThat(key1Value).isEqualTo(VALUE);
+    }
+
+    /**
+     * Test that when we locally override a flag, we can still write other flags.
+     */
+    @Test
+    public void testAddStickyLocalOverrideDoesNotAffectOtherFlags() {
+        DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, /* makeDefault= */ false);
+        String key2Value = DeviceConfig.getProperty(NAMESPACE, KEY2);
+        assertThat(key2Value).isEqualTo(VALUE2);
+    }
+
+    /**
+     * Test that when we apply some overrides, they show up in the override list.
+     */
+    @Test
+    public void testGetStickyLocalOverrides() {
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE2, false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE, false);
+        DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE);
+        DeviceConfig.setLocalOverride(NAMESPACE, KEY2, VALUE2);
+
+        Map<String, Map<String, String>> expectedOverrides = new HashMap<>();
+        Map<String, String> expectedInnerMap = new HashMap<>();
+        expectedInnerMap.put(KEY, VALUE2);
+        expectedInnerMap.put(KEY2, VALUE);
+        expectedOverrides.put(NAMESPACE, expectedInnerMap);
+
+        assertThat(DeviceConfig.getUnderlyingValuesForOverriddenFlags())
+                .isEqualTo(expectedOverrides);
+    }
+
+    /**
+     * Test that when we clear all overrides, the override list is empty.
+     */
+    @Test
+    public void testClearStickyLocalOverrides() {
+        DeviceConfig.setLocalOverride(NAMESPACE2, KEY, VALUE);
+        DeviceConfig.setLocalOverride(NAMESPACE2, KEY2, VALUE2);
+
+        DeviceConfig.clearAllLocalOverrides();
+
+        Map<String, Map<String, String>> overrides =
+                DeviceConfig.getUnderlyingValuesForOverriddenFlags();
+        assertThat(overrides).isEmpty();
+    }
+
+    /**
+     * Test that when we clear a single override, it doesn't appear in the list.
+     */
+    @Test
+    public void testClearStickyLocalOverride() {
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE2, false);
+        DeviceConfig.setProperty(NAMESPACE2, KEY2, VALUE, false);
+        DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE);
+        DeviceConfig.setLocalOverride(NAMESPACE2, KEY2, VALUE2);
+
+        DeviceConfig.clearLocalOverride(NAMESPACE, KEY);
+
+        Map<String, Map<String, String>> expectedOverrides = new HashMap<>();
+        Map<String, String> expectedInnerMap = new HashMap<>();
+        expectedInnerMap.put(KEY2, VALUE);
+        expectedOverrides.put(NAMESPACE2, expectedInnerMap);
+
+        assertThat(DeviceConfig.getUnderlyingValuesForOverriddenFlags())
+                .isEqualTo(expectedOverrides);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index f45db23..8c93fbb 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -81,8 +81,7 @@
                     Insets.of(10, 10, 10, 10), rect, rect, rect, rect));
             mController.calculateInsets(
                     false,
-                    false,
-                    TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                    TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                     SOFT_INPUT_ADJUST_RESIZE, 0, 0);
             mImeConsumer = mController.getImeSourceConsumer();
         });
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index b8f0d5c..1568174 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
 import static android.view.InsetsController.ANIMATION_TYPE_NONE;
 import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
@@ -171,8 +171,7 @@
             mController.onStateChanged(state);
             mController.calculateInsets(
                     false,
-                    false,
-                    TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                    TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                     SOFT_INPUT_ADJUST_RESIZE, 0, 0);
             mController.onFrameChanged(new Rect(0, 0, 100, 100));
         });
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index b06cd39..906d84e 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -16,8 +16,9 @@
 
 package android.view;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.InsetsState.ISIDE_BOTTOM;
 import static android.view.InsetsState.ISIDE_TOP;
@@ -101,7 +102,7 @@
                 .setVisible(true);
         SparseIntArray typeSideMap = new SparseIntArray();
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 typeSideMap);
         assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
@@ -120,7 +121,7 @@
                 .setFrame(new Rect(0, 100, 100, 300))
                 .setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 null);
         assertEquals(100, insets.getStableInsetBottom());
         assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(systemBars()));
@@ -139,7 +140,7 @@
                 .setFrame(new Rect(80, 0, 100, 300))
                 .setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -154,7 +155,7 @@
                 .setFrame(new Rect(80, 0, 100, 300))
                 .setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -169,7 +170,7 @@
                 .setFrame(new Rect(0, 200, 100, 300))
                 .setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
         assertEquals(100, insets.getInsets(ime()).bottom);
@@ -185,12 +186,12 @@
                 .setFrame(new Rect(0, 200, 100, 300))
                 .setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION,
+                ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
-        insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+        insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
                 SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
     }
 
@@ -200,12 +201,12 @@
                 .setFrame(new Rect(0, 0, 100, 100))
                 .setVisible(false);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE,
-                TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE,
+                TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
-        insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+        insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
                 SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
     }
 
@@ -213,22 +214,23 @@
     public void testCalculateInsets_flagLayoutNoLimits() {
         mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
                 .setFrame(new Rect(0, 0, 100, 100))
-                .setVisible(true);
+                .setVisible(true)
+                .setFlags(FLAG_FORCE_CONSUMING);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
-                0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
-                0 /* legacySystemUiFlags */, TYPE_SYSTEM_ERROR, WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_SYSTEM_ERROR, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
-                0 /* legacySystemUiFlags */, TYPE_WALLPAPER, WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_WALLPAPER, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
-                0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_FREEFORM, null);
+                SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_APPLICATION, ACTIVITY_TYPE_STANDARD, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
     }
 
@@ -243,7 +245,7 @@
                 .setVisible(true);
 
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 100, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                new Rect(0, 0, 100, 400), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
     }
@@ -255,7 +257,7 @@
                 .setVisible(true);
 
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                new Rect(0, 0, 150, 400), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
     }
@@ -269,7 +271,7 @@
                 .setFrame(new Rect(80, 0, 100, 300))
                 .setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -284,7 +286,7 @@
                 .setFrame(new Rect(80, 0, 100, 300))
                 .setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -292,11 +294,11 @@
 
     @Test
     public void testCalculateInsets_emptyIme() {
-        WindowInsets insets1 = mState.calculateInsets(new Rect(), null, false, false,
-                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        WindowInsets insets1 = mState.calculateInsets(new Rect(), null, false,
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         mState.getOrCreateSource(ID_IME, ime());
-        WindowInsets insets2 = mState.calculateInsets(new Rect(), null, false, false,
-                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        WindowInsets insets2 = mState.calculateInsets(new Rect(), null, false,
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(Insets.NONE, insets1.getInsets(ime()));
         assertEquals(Insets.NONE, insets2.getInsets(ime()));
         assertEquals(insets1, insets2);
@@ -311,8 +313,8 @@
                 .setFrame(new Rect(0, 200, 100, 300))
                 .setVisible(true);
         mState.removeSource(ID_IME);
-        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
-                SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+                SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
     }
 
@@ -527,7 +529,7 @@
                 .setFrame(new Rect(0, 100, 100, 300))
                 .setVisible(true);
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                new Rect(0, 0, 100, 300), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 SOFT_INPUT_ADJUST_PAN, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 100, 0, 100), visibleInsets);
     }
@@ -546,7 +548,7 @@
                 .setFrame(new Rect(0, 100, 100, 300))
                 .setVisible(true);
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                new Rect(0, 0, 100, 300), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 100, 0, 0), visibleInsets);
     }
@@ -565,7 +567,7 @@
                 .setFrame(new Rect(0, 100, 100, 300))
                 .setVisible(true);
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                new Rect(0, 0, 100, 300), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 SOFT_INPUT_ADJUST_PAN, FLAG_LAYOUT_NO_LIMITS);
         assertEquals(Insets.NONE, visibleInsets);
     }
@@ -599,8 +601,8 @@
                 new Rect(0, 0, 1, 2),
                 new Rect(197, 296, 200, 300),
                 new Rect(197, 296, 200, 300)));
-        DisplayCutout cutout = mState.calculateInsets(new Rect(1, 1, 199, 300), null, false, false,
-                SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+        DisplayCutout cutout = mState.calculateInsets(new Rect(1, 1, 199, 300), null, false,
+                SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
                 new SparseIntArray()).getDisplayCutout();
         assertEquals(0, cutout.getSafeInsetLeft());
         assertEquals(1, cutout.getSafeInsetTop());
@@ -625,8 +627,8 @@
                 new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
                 new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
         WindowInsets windowInsets = mState.calculateInsets(new Rect(1, 2, 197, 396), null, false,
-                false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, new SparseIntArray());
+                SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+                ACTIVITY_TYPE_UNDEFINED, new SparseIntArray());
         assertEquals(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8),
                 windowInsets.getRoundedCorner(POSITION_TOP_LEFT));
         assertEquals(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8),
@@ -642,8 +644,8 @@
         mState.setDisplayFrame(new Rect(0, 0, 200, 400));
         mState.setDisplayShape(DisplayShape.createDefaultDisplayShape(200, 400, false));
         WindowInsets windowInsets = mState.calculateInsets(new Rect(10, 20, 200, 400), null, false,
-                false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, new SparseIntArray());
+                SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+                ACTIVITY_TYPE_UNDEFINED, new SparseIntArray());
 
         final DisplayShape expect =
                 DisplayShape.createDefaultDisplayShape(200, 400, false).setOffset(-10, -20);
diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java
index 274c25a..94298dc 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java
@@ -92,18 +92,18 @@
     }
 
     @Test
-    public void testGetVibrationEffectListForTesting() {
+    public void testGetVibrationEffects() {
         ParsedVibration parsedVibration =
                 new ParsedVibration(List.of(mEffect1, mEffect2, mEffect3));
-        assertThat(parsedVibration.getVibrationEffectListForTesting())
+        assertThat(parsedVibration.getVibrationEffects())
                 .containsExactly(mEffect1, mEffect2, mEffect3)
                 .inOrder();
 
         parsedVibration = new ParsedVibration(List.of(mEffect1));
-        assertThat(parsedVibration.getVibrationEffectListForTesting()).containsExactly(mEffect1);
+        assertThat(parsedVibration.getVibrationEffects()).containsExactly(mEffect1);
 
         parsedVibration = new ParsedVibration(List.of());
-        assertThat(parsedVibration.getVibrationEffectListForTesting()).isEmpty();
+        assertThat(parsedVibration.getVibrationEffects()).isEmpty();
     }
 
     private Subject assertThatResolution(
diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index d73b5cb..2814a5f 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -363,8 +363,7 @@
 
     private void assertParseDocumentSucceeds(String xml, int flags, VibrationEffect... effects)
             throws Exception {
-        assertThat(parseDocument(xml, flags).getVibrationEffectListForTesting())
-                .containsExactly(effects);
+        assertThat(parseDocument(xml, flags).getVibrationEffects()).containsExactly(effects);
     }
 
     /**
@@ -381,8 +380,7 @@
         String tagName = parser.getName();
         assertThat(Set.of("vibration", "vibration-select")).contains(tagName);
 
-        assertThat(parseElement(parser, flags).getVibrationEffectListForTesting())
-                .containsExactly(effects);
+        assertThat(parseElement(parser, flags).getVibrationEffects()).containsExactly(effects);
         assertThat(parser.getEventType()).isEqualTo(XmlPullParser.END_TAG);
         assertThat(parser.getName()).isEqualTo(tagName);
     }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 9fb627f..4c4e8fa 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1475,7 +1475,10 @@
         String locale = SystemProperties.get("persist.sys.locale", "en-US");
         String script = ULocale.addLikelySubtags(ULocale.forLanguageTag(locale)).getScript();
 
-        FontConfig config = SystemFonts.getSystemPreinstalledFontConfig();
+        // The feature flag cannot be referred from Zygote. Use legacy fonts.xml for preloading font
+        // files.
+        // TODO(nona): Use new XML file once the feature is fully launched.
+        FontConfig config = SystemFonts.getSystemPreinstalledFontConfigFromLegacyXml();
         for (int i = 0; i < config.getFontFamilies().size(); ++i) {
             FontConfig.FontFamily family = config.getFontFamilies().get(i);
             if (!family.getLocaleList().isEmpty()) {
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 36bfb98..9810022 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -48,6 +48,8 @@
     private static final String TAG = "SystemFonts";
 
     private static final String FONTS_XML = "/system/etc/font_fallback.xml";
+    private static final String LEGACY_FONTS_XML = "/system/etc/fonts.xml";
+
     /** @hide */
     public static final String SYSTEM_FONT_DIR = "/system/fonts/";
     private static final String OEM_XML = "/product/etc/fonts_customization.xml";
@@ -230,7 +232,13 @@
             long lastModifiedDate,
             int configVersion
     ) {
-        return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
+        final String fontsXml;
+        if (com.android.text.flags.Flags.deprecateFontsXml()) {
+            fontsXml = FONTS_XML;
+        } else {
+            fontsXml = LEGACY_FONTS_XML;
+        }
+        return getSystemFontConfigInternal(fontsXml, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
                 updatableFontMap, lastModifiedDate, configVersion);
     }
 
@@ -255,10 +263,24 @@
      * @hide
      */
     public static @NonNull FontConfig getSystemPreinstalledFontConfig() {
-        return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
+        final String fontsXml;
+        if (com.android.text.flags.Flags.deprecateFontsXml()) {
+            fontsXml = FONTS_XML;
+        } else {
+            fontsXml = LEGACY_FONTS_XML;
+        }
+        return getSystemFontConfigInternal(fontsXml, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
                 0, 0);
     }
 
+    /**
+     * @hide
+     */
+    public static @NonNull FontConfig getSystemPreinstalledFontConfigFromLegacyXml() {
+        return getSystemFontConfigInternal(LEGACY_FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
+                null, 0, 0);
+    }
+
     /* package */ static @NonNull FontConfig getSystemFontConfigInternal(
             @NonNull String fontsXml,
             @NonNull String systemFontDir,
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 7edf2fc..cba86c8 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -428,9 +428,12 @@
     <!-- The height of the handle menu's "Windowing" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
 
-    <!-- The height of the handle menu's "More Actions" pill in desktop mode. -->
+    <!-- The height of the handle menu's "More Actions" pill in desktop mode, but not freeform. -->
     <dimen name="desktop_mode_handle_menu_more_actions_pill_height">156dp</dimen>
 
+    <!-- The height of the handle menu's "More Actions" pill in freeform desktop windowing mode. -->
+    <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">104dp</dimen>
+
     <!-- The top margin of the handle menu in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index ca7cbfd..42b1240 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -186,7 +186,12 @@
         // More Actions pill setup.
         final View moreActionsPillView = mMoreActionsPill.mWindowViewHost.getView();
         final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button);
-        closeBtn.setOnClickListener(mOnClickListener);
+        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+            closeBtn.setVisibility(View.GONE);
+        } else {
+            closeBtn.setVisibility(View.VISIBLE);
+            closeBtn.setOnClickListener(mOnClickListener);
+        }
         final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button);
         selectBtn.setOnClickListener(mOnClickListener);
     }
@@ -228,7 +233,6 @@
 
     /**
      * Update pill layout, in case task changes have caused positioning to change.
-     * @param t
      */
     void relayout(SurfaceControl.Transaction t) {
         if (mAppInfoPill != null) {
@@ -245,10 +249,12 @@
                     mMoreActionsPillPosition.x, mMoreActionsPillPosition.y);
         }
     }
+
     /**
      * Check a passed MotionEvent if a click has occurred on any button on this caption
      * Note this should only be called when a regular onClick is not possible
      * (i.e. the button was clicked through status bar layer)
+     *
      * @param ev the MotionEvent to compare against.
      */
     void checkClickEvent(MotionEvent ev) {
@@ -267,6 +273,7 @@
      * A valid menu input is one of the following:
      * An input that happens in the menu views.
      * Any input before the views have been laid out.
+     *
      * @param inputPoint the input to compare against.
      */
     boolean isValidMenuInput(PointF inputPoint) {
@@ -297,7 +304,6 @@
 
     /**
      * Check if the views for handle menu can be seen.
-     * @return
      */
     private boolean viewsLaidOut() {
         return mAppInfoPill.mWindowViewHost.getView().isLaidOut();
@@ -318,8 +324,7 @@
                 R.dimen.desktop_mode_handle_menu_app_info_pill_height);
         mWindowingPillHeight = loadDimensionPixelSize(resources,
                 R.dimen.desktop_mode_handle_menu_windowing_pill_height);
-        mMoreActionsPillHeight = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
+        mMoreActionsPillHeight = shouldShowCloseButton(resources);
         mShadowRadius = loadDimensionPixelSize(resources,
                 R.dimen.desktop_mode_handle_menu_shadow_radius);
         mCornerRadius = loadDimensionPixelSize(resources,
@@ -333,6 +338,14 @@
         return resources.getDimensionPixelSize(resourceId);
     }
 
+    private int shouldShowCloseButton(Resources resources) {
+        return (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM)
+                ? loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_more_actions_pill_freeform_height)
+                : loadDimensionPixelSize(resources,
+                        R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
+    }
+
     void close() {
         mAppInfoPill.releaseView();
         mAppInfoPill = null;
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
index 87b20ed..5e6a6c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
@@ -73,7 +73,9 @@
         <option name="shell-timeout" value="6600s"/>
         <option name="test-timeout" value="6000s"/>
         <option name="hidden-api-checks" value="false"/>
+        <!-- TODO(b/288396763): re-enable when PerfettoListener is fixed
         <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        -->
         <!-- PerfettoListener related arguments -->
         <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
         <option name="instrumentation-arg"
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index ea26185..1de0881 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -1155,7 +1155,7 @@
     public static final int OPTION_CLOSEST          = 0x03;
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "OPTION_" }, value = {
+    @IntDef(flag = false, prefix = { "OPTION_" }, value = {
             OPTION_PREVIOUS_SYNC,
             OPTION_NEXT_SYNC,
             OPTION_CLOSEST_SYNC,
diff --git a/packages/Keyguard/proguard.flags b/packages/Keyguard/proguard.flags
deleted file mode 100644
index fb74b64..0000000
--- a/packages/Keyguard/proguard.flags
+++ /dev/null
@@ -1,27 +0,0 @@
--keep public class * {
-  public void setBackgroundAlpha(float);
-  public float getBackgroundAlpha();
-  public void setContentAlpha(float);
-  public float getContentAlpha();
-  public void setAlpha(float);
-  public float getAlpha();
-  public void setAlpha(int);
-  public int getAlpha();
-  public void setRotationX(float);
-  public float getRotationX();
-  public void setRotationY(float);
-  public float getRotationY();
-  public void setPivotX(float);
-  public float getPivotX();
-  public void setPivotY(float);
-  public float getPivotY();
-  public void setScaleX(float);
-  public float getScaleX();
-  public void setScaleY(float);
-  public float getScaleY();
-  public void setTranslationX(float);
-  public float getTranslationX();
-  public void setTranslationY(float);
-  public float getTranslationY();
-}
-
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8787c25..94a3173 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -165,12 +165,10 @@
         Settings.Secure.CHARGING_VIBRATION_ENABLED,
         Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
         Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
-        Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
         Settings.Secure.UI_NIGHT_MODE,
         Settings.Secure.UI_NIGHT_MODE_CUSTOM_TYPE,
         Settings.Secure.DARK_THEME_CUSTOM_START_TIME,
         Settings.Secure.DARK_THEME_CUSTOM_END_TIME,
-        Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
         Settings.Secure.SKIP_DIRECTION,
         Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
         Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index dfc3cef..8179998 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -245,9 +245,7 @@
                 Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.USER_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.TRUST_AGENTS_EXTEND_UNLOCK, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, JSON_OBJECT_VALIDATOR);
-        VALIDATORS.put(Secure.LOCK_SCREEN_WHEN_TRUST_LOST, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SKIP_GESTURE, BOOLEAN_VALIDATOR);
         /*
          * Only used if FeatureFlag "settings_skip_direction_mutable" is enabled.
diff --git a/packages/SharedStorageBackup/Android.bp b/packages/SharedStorageBackup/Android.bp
index 21516fa..225b5b4 100644
--- a/packages/SharedStorageBackup/Android.bp
+++ b/packages/SharedStorageBackup/Android.bp
@@ -27,9 +27,6 @@
     name: "SharedStorageBackup",
     defaults: ["platform_app_defaults"],
     srcs: ["src/**/*.java"],
-    optimize: {
-        proguard_flags_files: ["proguard.flags"],
-    },
     platform_apis: true,
     certificate: "platform",
     privileged: true,
diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags
deleted file mode 100644
index 6a66a47..0000000
--- a/packages/SharedStorageBackup/proguard.flags
+++ /dev/null
@@ -1,2 +0,0 @@
--keep class com.android.sharedstoragebackup.SharedStorageAgent
--keep class com.android.sharedstoragebackup.ObbBackupService
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index e7bbf97..f68078a 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -22,6 +22,7 @@
 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_REMOVED
 import android.os.Handler
+import android.os.Trace
 import android.util.Log
 import android.view.Display
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -34,9 +35,14 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /** Provides a [Flow] of [Display] as returned by [DisplayManager]. */
@@ -49,7 +55,25 @@
      *
      * When `null`, it means there is no pending display waiting to be enabled.
      */
-    val pendingDisplayId: Flow<Int?>
+    val pendingDisplay: Flow<PendingDisplay?>
+
+    /** Represents a connected display that has not been enabled yet. */
+    interface PendingDisplay {
+        /** Id of the pending display. */
+        val id: Int
+
+        /** Enables the display, making it available to the system. */
+        suspend fun enable()
+
+        /**
+         * Ignores the pending display. When called, this specific display id doesn't appear as
+         * pending anymore until the display is disconnected and reconnected again.
+         */
+        suspend fun ignore()
+
+        /** Disables the display, making it unavailable to the system. */
+        suspend fun disable()
+    }
 }
 
 @SysUISingleton
@@ -62,7 +86,8 @@
     @Background backgroundCoroutineDispatcher: CoroutineDispatcher
 ) : DisplayRepository {
 
-    override val displays: Flow<Set<Display>> =
+    // Displays are enabled only after receiving them in [onDisplayAdded]
+    private val enabledDisplays: StateFlow<Set<Display>> =
         conflatedCallbackFlow {
                 val callback =
                     object : DisplayListener {
@@ -99,27 +124,38 @@
             displayManager.displays?.toSet() ?: emptySet()
         }
 
-    override val pendingDisplayId: Flow<Int?> =
+    /** Propagate to the listeners only enabled displays */
+    override val displays: Flow<Set<Display>> = enabledDisplays
+
+    private val enabledDisplayIds: Flow<Set<Int>> =
+        enabledDisplays
+            .map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() }
+            .debugLog("enabledDisplayIds")
+
+    private val ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
+
+    /* keeps connected displays until they are disconnected. */
+    private val connectedDisplayIds: StateFlow<Set<Int>> =
         conflatedCallbackFlow {
                 val callback =
                     object : DisplayConnectionListener {
-                        private val pendingIds = mutableSetOf<Int>()
+                        private val connectedIds = mutableSetOf<Int>()
                         override fun onDisplayConnected(id: Int) {
-                            pendingIds += id
-                            trySend(id)
+                            if (DEBUG) {
+                                Log.d(TAG, "$id connected")
+                            }
+                            connectedIds += id
+                            ignoredDisplayIds.value -= id
+                            trySend(connectedIds.toSet())
                         }
 
                         override fun onDisplayDisconnected(id: Int) {
-                            if (id in pendingIds) {
-                                pendingIds -= id
-                                trySend(null)
-                            } else {
-                                Log.e(
-                                    TAG,
-                                    "onDisplayDisconnected received for unknown display. " +
-                                        "id=$id, knownIds=$pendingIds"
-                                )
+                            connectedIds -= id
+                            if (DEBUG) {
+                                Log.d(TAG, "$id disconnected. Connected ids: $connectedIds")
                             }
+                            ignoredDisplayIds.value -= id
+                            trySend(connectedIds.toSet())
                         }
                     }
                 displayManager.registerDisplayListener(
@@ -130,15 +166,80 @@
                 awaitClose { displayManager.unregisterDisplayListener(callback) }
             }
             .distinctUntilChanged()
+            .debugLog("connectedDisplayIds")
             .flowOn(backgroundCoroutineDispatcher)
             .stateIn(
                 applicationScope,
                 started = SharingStarted.WhileSubscribed(),
-                initialValue = null
+                initialValue = emptySet()
             )
 
+    /**
+     * Pending displays are the ones connected, but not enabled and not ignored. A connected display
+     * is ignored after the user makes the decision to use it or not. For now, the initial decision
+     * from the user is final and not reversible.
+     */
+    private val pendingDisplayIds: Flow<Set<Int>> =
+        combine(enabledDisplayIds, connectedDisplayIds, ignoredDisplayIds) {
+                enabledDisplaysIds,
+                connectedDisplayIds,
+                ignoredDisplayIds ->
+                if (DEBUG) {
+                    Log.d(
+                        TAG,
+                        "combining enabled: $enabledDisplaysIds, " +
+                            "connected: $connectedDisplayIds, ignored: $ignoredDisplayIds"
+                    )
+                }
+                connectedDisplayIds - enabledDisplaysIds - ignoredDisplayIds
+            }
+            .debugLog("pendingDisplayIds")
+
+    override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
+        pendingDisplayIds
+            .map { pendingDisplayIds ->
+                val id = pendingDisplayIds.maxOrNull() ?: return@map null
+                object : DisplayRepository.PendingDisplay {
+                    override val id = id
+                    override suspend fun enable() {
+                        traceSection("DisplayRepository#enable($id)") {
+                            displayManager.enableConnectedDisplay(id)
+                        }
+                        // After the display has been enabled, it is automatically ignored.
+                        ignore()
+                    }
+
+                    override suspend fun ignore() {
+                        traceSection("DisplayRepository#ignore($id)") {
+                            ignoredDisplayIds.value += id
+                        }
+                    }
+
+                    override suspend fun disable() {
+                        ignore()
+                        traceSection("DisplayRepository#disable($id)") {
+                            displayManager.disableConnectedDisplay(id)
+                        }
+                    }
+                }
+            }
+            .debugLog("pendingDisplay")
+
+    private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
+        return if (DEBUG) {
+            this.onEach {
+                Log.d(TAG, "$flowName: $it")
+                Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "$TAG#$flowName", 0)
+                Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "$TAG#$flowName", "$it", 0)
+            }
+        } else {
+            this
+        }
+    }
+
     private companion object {
         const val TAG = "DisplayRepository"
+        val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index ef6fa26..11ed96d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -16,14 +16,12 @@
 
 package com.android.systemui.display.domain.interactor
 
-import android.hardware.display.DisplayManager
 import android.view.Display
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.display.data.repository.DisplayRepository
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.util.traceSection
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -52,13 +50,18 @@
         CONNECTED_SECURE,
     }
 
-    /** Represents a connected display that has not been enabled yet. */
+    /** Represents a connected display that has not been enabled yet for the UI layer. */
     interface PendingDisplay {
         /** Enables the display, making it available to the system. */
-        fun enable()
+        suspend fun enable()
 
-        /** Disables the display, making it unavailable to the system. */
-        fun disable()
+        /**
+         * Ignores the pending display.
+         *
+         * When called, this specific display id doesn't appear as pending anymore until the display
+         * is disconnected and reconnected again.
+         */
+        suspend fun ignore()
     }
 }
 
@@ -66,7 +69,6 @@
 class ConnectedDisplayInteractorImpl
 @Inject
 constructor(
-    private val displayManager: DisplayManager,
     keyguardRepository: KeyguardRepository,
     displayRepository: DisplayRepository,
 ) : ConnectedDisplayInteractor {
@@ -92,28 +94,19 @@
 
     // Provides the pending display only if the lockscreen is unlocked
     override val pendingDisplay: Flow<PendingDisplay?> =
-        displayRepository.pendingDisplayId.combine(keyguardRepository.isKeyguardUnlocked) {
-            pendingDisplayId,
-            keyguardUnlocked ->
-            if (pendingDisplayId != null && keyguardUnlocked) {
-                pendingDisplayId.toPendingDisplay()
+        displayRepository.pendingDisplay.combine(keyguardRepository.isKeyguardShowing) {
+            repositoryPendingDisplay,
+            keyguardShowing ->
+            if (repositoryPendingDisplay != null && !keyguardShowing) {
+                repositoryPendingDisplay.toInteractorPendingDisplay()
             } else {
                 null
             }
         }
 
-    private fun Int.toPendingDisplay() =
+    private fun DisplayRepository.PendingDisplay.toInteractorPendingDisplay(): PendingDisplay =
         object : PendingDisplay {
-            val id = this@toPendingDisplay
-            override fun enable() {
-                traceSection("DisplayRepository#enable($id)") {
-                    displayManager.enableConnectedDisplay(id)
-                }
-            }
-            override fun disable() {
-                traceSection("DisplayRepository#enable($id)") {
-                    displayManager.disableConnectedDisplay(id)
-                }
-            }
+            override suspend fun enable() = this@toInteractorPendingDisplay.enable()
+            override suspend fun ignore() = this@toInteractorPendingDisplay.ignore()
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
index 174c6ff..ecc9d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
@@ -24,15 +24,22 @@
 import android.widget.TextView
 import com.android.systemui.R
 
-/** Dialog used to decide what to do with a connected display. */
+/**
+ * Dialog used to decide what to do with a connected display.
+ *
+ * [onCancelMirroring] is called **only** if mirroring didn't start, or when the dismiss button is
+ * pressed.
+ */
 class MirroringConfirmationDialog(
     context: Context,
     private val onStartMirroringClickListener: View.OnClickListener,
-    private val onDismissClickListener: View.OnClickListener,
+    private val onCancelMirroring: View.OnClickListener,
 ) : Dialog(context, R.style.Theme_SystemUI_Dialog) {
 
     private lateinit var mirrorButton: TextView
     private lateinit var dismissButton: TextView
+    private var enabledPressed = false
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         window?.apply {
@@ -45,10 +52,15 @@
         mirrorButton =
             requireViewById<TextView>(R.id.enable_display).apply {
                 setOnClickListener(onStartMirroringClickListener)
+                enabledPressed = true
             }
         dismissButton =
-            requireViewById<TextView>(R.id.cancel).apply {
-                setOnClickListener(onDismissClickListener)
+            requireViewById<TextView>(R.id.cancel).apply { setOnClickListener(onCancelMirroring) }
+
+        setOnDismissListener {
+            if (!enabledPressed) {
+                onCancelMirroring.onClick(null)
             }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
index ece33b7..86ef439 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -19,13 +19,16 @@
 import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
 import com.android.systemui.display.ui.view.MirroringConfirmationDialog
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
 
 /**
  * Shows/hides a dialog to allow the user to decide whether to use the external display for
@@ -38,6 +41,7 @@
     private val context: Context,
     private val connectedDisplayInteractor: ConnectedDisplayInteractor,
     @Application private val scope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher
 ) {
 
     private var dialog: Dialog? = null
@@ -61,10 +65,13 @@
             MirroringConfirmationDialog(
                     context,
                     onStartMirroringClickListener = {
-                        pendingDisplay.enable()
+                        scope.launch(bgDispatcher) { pendingDisplay.enable() }
                         hideDialog()
                     },
-                    onDismissClickListener = { hideDialog() }
+                    onCancelMirroring = {
+                        scope.launch(bgDispatcher) { pendingDisplay.ignore() }
+                        hideDialog()
+                    }
                 )
                 .apply { show() }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 6bc9abf..257006e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -126,11 +126,11 @@
         }
 
     override fun start() {
+        bindKeyguardRootView()
         if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
             keyguardRootView.removeAllViews()
             initializeViews()
         } else {
-            bindKeyguardRootView()
             val notificationPanel =
                 notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
             unbindKeyguardBottomArea(notificationPanel)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index db7c003..4bd380e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -52,6 +52,7 @@
 
     private val displayManager = mock<DisplayManager>()
     private val displayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
+    private val connectedDisplayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
 
     private val testHandler = FakeHandler(Looper.getMainLooper())
     private val testScope = TestScope(UnconfinedTestDispatcher())
@@ -114,7 +115,7 @@
 
             // Let's make sure it has *NOT* been unregistered, as there is still a subscriber.
             setDisplays(1)
-            displayListener.value.onDisplayAdded(1)
+            sendOnDisplayAdded(1)
             assertThat(firstSubscriber?.ids()).containsExactly(1)
         }
 
@@ -127,7 +128,7 @@
             val value by latestDisplayFlowValue()
 
             setDisplays(1)
-            displayListener.value.onDisplayAdded(1)
+            sendOnDisplayAdded(1)
 
             assertThat(value?.ids()).containsExactly(1)
         }
@@ -138,13 +139,13 @@
             val value by latestDisplayFlowValue()
 
             setDisplays(1, 2, 3, 4)
-            displayListener.value.onDisplayAdded(1)
-            displayListener.value.onDisplayAdded(2)
-            displayListener.value.onDisplayAdded(3)
-            displayListener.value.onDisplayAdded(4)
+            sendOnDisplayAdded(1)
+            sendOnDisplayAdded(2)
+            sendOnDisplayAdded(3)
+            sendOnDisplayAdded(4)
 
             setDisplays(1, 2, 3)
-            displayListener.value.onDisplayRemoved(4)
+            sendOnDisplayRemoved(4)
 
             assertThat(value?.ids()).containsExactly(1, 2, 3)
         }
@@ -155,10 +156,10 @@
             val value by latestDisplayFlowValue()
 
             setDisplays(1, 2, 3, 4)
-            displayListener.value.onDisplayAdded(1)
-            displayListener.value.onDisplayAdded(2)
-            displayListener.value.onDisplayAdded(3)
-            displayListener.value.onDisplayAdded(4)
+            sendOnDisplayAdded(1)
+            sendOnDisplayAdded(2)
+            sendOnDisplayAdded(3)
+            sendOnDisplayAdded(4)
 
             displayListener.value.onDisplayChanged(4)
 
@@ -168,22 +169,22 @@
     @Test
     fun onDisplayConnected_pendingDisplayReceived() =
         testScope.runTest {
-            val pendingDisplay by latestPendingDisplayFlowValue()
+            val pendingDisplay by lastPendingDisplay()
 
-            displayListener.value.onDisplayConnected(1)
+            sendOnDisplayConnected(1)
 
-            assertThat(pendingDisplay).isEqualTo(1)
+            assertThat(pendingDisplay!!.id).isEqualTo(1)
         }
 
     @Test
     fun onDisplayDisconnected_pendingDisplayNull() =
         testScope.runTest {
-            val pendingDisplay by latestPendingDisplayFlowValue()
-            displayListener.value.onDisplayConnected(1)
+            val pendingDisplay by lastPendingDisplay()
+            sendOnDisplayConnected(1)
 
             assertThat(pendingDisplay).isNotNull()
 
-            displayListener.value.onDisplayDisconnected(1)
+            sendOnDisplayDisconnected(1)
 
             assertThat(pendingDisplay).isNull()
         }
@@ -191,24 +192,162 @@
     @Test
     fun onDisplayDisconnected_unknownDisplay_doesNotSendNull() =
         testScope.runTest {
-            val pendingDisplay by latestPendingDisplayFlowValue()
-            displayListener.value.onDisplayConnected(1)
+            val pendingDisplay by lastPendingDisplay()
+            sendOnDisplayConnected(1)
 
             assertThat(pendingDisplay).isNotNull()
 
-            displayListener.value.onDisplayDisconnected(2)
+            sendOnDisplayDisconnected(2)
 
             assertThat(pendingDisplay).isNotNull()
         }
 
     @Test
-    fun onDisplayConnected_multipleTimes_sendsOnlyTheLastOne() =
+    fun onDisplayConnected_multipleTimes_sendsOnlyTheMaximum() =
         testScope.runTest {
-            val pendingDisplay by latestPendingDisplayFlowValue()
-            displayListener.value.onDisplayConnected(1)
-            displayListener.value.onDisplayConnected(2)
+            val pendingDisplay by lastPendingDisplay()
 
-            assertThat(pendingDisplay).isEqualTo(2)
+            sendOnDisplayConnected(1)
+            sendOnDisplayConnected(2)
+
+            assertThat(pendingDisplay!!.id).isEqualTo(2)
+        }
+
+    @Test
+    fun onPendingDisplay_enable_displayEnabled() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1)
+            pendingDisplay!!.enable()
+
+            verify(displayManager).enableConnectedDisplay(eq(1))
+        }
+
+    @Test
+    fun onPendingDisplay_enableBySysui_disabledBySomeoneElse_pendingDisplayStillIgnored() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1)
+            pendingDisplay!!.enable()
+            // to mock the display being really enabled:
+            sendOnDisplayAdded(1)
+
+            // Simulate the display being disabled by someone else. Now, sysui will have it in the
+            // "pending displays" list again, but it should be ignored.
+            sendOnDisplayRemoved(1)
+
+            assertThat(pendingDisplay).isNull()
+        }
+
+    @Test
+    fun onPendingDisplay_ignoredBySysui_enabledDisabledBySomeoneElse_pendingDisplayStillIgnored() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1)
+            pendingDisplay!!.ignore()
+
+            // to mock the display being enabled and disabled by someone else:
+            sendOnDisplayAdded(1)
+            sendOnDisplayRemoved(1)
+
+            // Sysui already decided to ignore it, so the pending display should be null.
+            assertThat(pendingDisplay).isNull()
+        }
+
+    @Test
+    fun onPendingDisplay_disable_displayDisabled() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1)
+            pendingDisplay!!.disable()
+
+            verify(displayManager).disableConnectedDisplay(eq(1))
+        }
+
+    @Test
+    fun onPendingDisplay_ignore_pendingDisplayNull() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+            sendOnDisplayConnected(1)
+
+            pendingDisplay!!.ignore()
+
+            assertThat(pendingDisplay).isNull()
+            verify(displayManager, never()).disableConnectedDisplay(eq(1))
+            verify(displayManager, never()).enableConnectedDisplay(eq(1))
+        }
+
+    @Test
+    fun onPendingDisplay_enabled_pendingDisplayNull() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1)
+            assertThat(pendingDisplay).isNotNull()
+
+            setDisplays(1)
+            sendOnDisplayAdded(1)
+
+            assertThat(pendingDisplay).isNull()
+        }
+
+    @Test
+    fun onPendingDisplay_multipleConnected_oneEnabled_pendingDisplayNotNull() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1)
+            sendOnDisplayConnected(2)
+
+            assertThat(pendingDisplay).isNotNull()
+
+            setDisplays(1)
+            sendOnDisplayAdded(1)
+
+            assertThat(pendingDisplay).isNotNull()
+            assertThat(pendingDisplay!!.id).isEqualTo(2)
+
+            setDisplays(1, 2)
+            sendOnDisplayAdded(2)
+
+            assertThat(pendingDisplay).isNull()
+        }
+
+    @Test
+    fun pendingDisplay_connectedDisconnectedAndReconnected_expectedPendingDisplayState() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            // Plug the cable
+            sendOnDisplayConnected(1)
+
+            // Enable it
+            assertThat(pendingDisplay).isNotNull()
+            pendingDisplay!!.enable()
+
+            // Enabled
+            verify(displayManager).enableConnectedDisplay(1)
+            setDisplays(1)
+            sendOnDisplayAdded(1)
+
+            // No more pending displays
+            assertThat(pendingDisplay).isNull()
+
+            // Let's disconnect the cable
+            setDisplays()
+            sendOnDisplayRemoved(1)
+            sendOnDisplayDisconnected(1)
+
+            assertThat(pendingDisplay).isNull()
+
+            // Let's reconnect it
+            sendOnDisplayConnected(1)
+
+            assertThat(pendingDisplay).isNotNull()
         }
 
     private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
@@ -216,6 +355,23 @@
     // Wrapper to capture the displayListener.
     private fun TestScope.latestDisplayFlowValue(): FlowValue<Set<Display>?> {
         val flowValue = collectLastValue(displayRepository.displays)
+        captureAddedRemovedListener()
+        return flowValue
+    }
+
+    private fun TestScope.lastPendingDisplay(): FlowValue<DisplayRepository.PendingDisplay?> {
+        val flowValue = collectLastValue(displayRepository.pendingDisplay)
+        captureAddedRemovedListener()
+        verify(displayManager)
+            .registerDisplayListener(
+                connectedDisplayListener.capture(),
+                eq(testHandler),
+                eq(DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
+            )
+        return flowValue
+    }
+
+    private fun captureAddedRemovedListener() {
         verify(displayManager)
             .registerDisplayListener(
                 displayListener.capture(),
@@ -226,18 +382,20 @@
                         DisplayManager.EVENT_FLAG_DISPLAY_REMOVED
                 )
             )
-        return flowValue
+    }
+    private fun sendOnDisplayAdded(id: Int) {
+        displayListener.value.onDisplayAdded(id)
+    }
+    private fun sendOnDisplayRemoved(id: Int) {
+        displayListener.value.onDisplayRemoved(id)
     }
 
-    private fun TestScope.latestPendingDisplayFlowValue(): FlowValue<Int?> {
-        val flowValue = collectLastValue(displayRepository.pendingDisplayId)
-        verify(displayManager)
-            .registerDisplayListener(
-                displayListener.capture(),
-                eq(testHandler),
-                eq(DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
-            )
-        return flowValue
+    private fun sendOnDisplayDisconnected(id: Int) {
+        connectedDisplayListener.value.onDisplayDisconnected(id)
+    }
+
+    private fun sendOnDisplayConnected(id: Int) {
+        connectedDisplayListener.value.onDisplayConnected(id)
     }
 
     private fun setDisplays(displays: List<Display>) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index 50617a1..26ee094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.display.domain.interactor
 
-import android.hardware.display.DisplayManager
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display
@@ -27,12 +26,11 @@
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.display.data.repository.createPendingDisplay
 import com.android.systemui.display.data.repository.display
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -41,7 +39,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito
 
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
@@ -49,20 +46,15 @@
 @SmallTest
 class ConnectedDisplayInteractorTest : SysuiTestCase() {
 
-    private val displayManager = mock<DisplayManager>()
     private val fakeDisplayRepository = FakeDisplayRepository()
     private val fakeKeyguardRepository = FakeKeyguardRepository()
     private val connectedDisplayStateProvider: ConnectedDisplayInteractor =
-        ConnectedDisplayInteractorImpl(
-            displayManager,
-            fakeKeyguardRepository,
-            fakeDisplayRepository
-        )
+        ConnectedDisplayInteractorImpl(fakeKeyguardRepository, fakeDisplayRepository)
     private val testScope = TestScope(UnconfinedTestDispatcher())
 
     @Before
     fun setup() {
-        fakeKeyguardRepository.setKeyguardUnlocked(true)
+        fakeKeyguardRepository.setKeyguardShowing(false)
     }
 
     @Test
@@ -148,7 +140,7 @@
     fun pendingDisplay_propagated() =
         testScope.runTest {
             val value by lastPendingDisplay()
-            val pendingDisplayId = 4
+            val pendingDisplayId = createPendingDisplay()
 
             fakeDisplayRepository.emit(pendingDisplayId)
 
@@ -156,51 +148,29 @@
         }
 
     @Test
-    fun onPendingDisplay_enable_displayEnabled() =
+    fun onPendingDisplay_keyguardShowing_returnsPendingDisplay() =
         testScope.runTest {
+            fakeKeyguardRepository.setKeyguardShowing(true)
             val pendingDisplay by lastPendingDisplay()
 
-            fakeDisplayRepository.emit(1)
-            pendingDisplay!!.enable()
-
-            Mockito.verify(displayManager).enableConnectedDisplay(eq(1))
-        }
-
-    @Test
-    fun onPendingDisplay_disable_displayDisabled() =
-        testScope.runTest {
-            val pendingDisplay by lastPendingDisplay()
-
-            fakeDisplayRepository.emit(1)
-            pendingDisplay!!.disable()
-
-            Mockito.verify(displayManager).disableConnectedDisplay(eq(1))
-        }
-
-    @Test
-    fun onPendingDisplay_keyguardUnlocked_returnsPendingDisplay() =
-        testScope.runTest {
-            fakeKeyguardRepository.setKeyguardUnlocked(false)
-            val pendingDisplay by lastPendingDisplay()
-
-            fakeDisplayRepository.emit(1)
+            fakeDisplayRepository.emit(createPendingDisplay())
             assertThat(pendingDisplay).isNull()
 
-            fakeKeyguardRepository.setKeyguardUnlocked(true)
+            fakeKeyguardRepository.setKeyguardShowing(false)
 
             assertThat(pendingDisplay).isNotNull()
         }
 
     @Test
-    fun onPendingDisplay_keyguardLocked_returnsNull() =
+    fun onPendingDisplay_keyguardShowing_returnsNull() =
         testScope.runTest {
-            fakeKeyguardRepository.setKeyguardUnlocked(true)
+            fakeKeyguardRepository.setKeyguardShowing(false)
             val pendingDisplay by lastPendingDisplay()
 
-            fakeDisplayRepository.emit(1)
+            fakeDisplayRepository.emit(createPendingDisplay())
             assertThat(pendingDisplay).isNotNull()
 
-            fakeKeyguardRepository.setKeyguardUnlocked(false)
+            fakeKeyguardRepository.setKeyguardShowing(true)
 
             assertThat(pendingDisplay).isNull()
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
index 7059647..46f7582 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
@@ -68,6 +68,28 @@
         verify(onStartMirroringCallback, never()).onClick(any())
     }
 
+    @Test
+    fun onCancel_afterEnablingMirroring_cancelCallbackNotCalled() {
+        dialog.show()
+        dialog.requireViewById<View>(R.id.enable_display).callOnClick()
+
+        dialog.cancel()
+
+        verify(onCancelCallback, never()).onClick(any())
+        verify(onStartMirroringCallback).onClick(any())
+    }
+
+    @Test
+    fun onDismiss_afterEnablingMirroring_cancelCallbackNotCalled() {
+        dialog.show()
+        dialog.requireViewById<View>(R.id.enable_display).callOnClick()
+
+        dialog.dismiss()
+
+        verify(onCancelCallback, never()).onClick(any())
+        verify(onStartMirroringCallback).onClick(any())
+    }
+
     @After
     fun teardown() {
         if (::dialog.isInitialized) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 6f51d1b..2ac625d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -30,20 +30,24 @@
     }
 }
 
+/** Creates a mock [DisplayRepository.PendingDisplay]. */
+fun createPendingDisplay(id: Int = 0): DisplayRepository.PendingDisplay =
+    mock<DisplayRepository.PendingDisplay> { whenever(this.id).thenReturn(id) }
+
 /** Fake [DisplayRepository] implementation for testing. */
 class FakeDisplayRepository : DisplayRepository {
     private val flow = MutableSharedFlow<Set<Display>>()
-    private val pendingDisplayFlow = MutableSharedFlow<Int?>()
+    private val pendingDisplayFlow = MutableSharedFlow<DisplayRepository.PendingDisplay?>()
 
     /** Emits [value] as [displays] flow value. */
     suspend fun emit(value: Set<Display>) = flow.emit(value)
 
-    /** Emits [value] as [pendingDisplayId] flow value. */
-    suspend fun emit(value: Int?) = pendingDisplayFlow.emit(value)
+    /** Emits [value] as [pendingDisplay] flow value. */
+    suspend fun emit(value: DisplayRepository.PendingDisplay?) = pendingDisplayFlow.emit(value)
 
     override val displays: Flow<Set<Display>>
         get() = flow
 
-    override val pendingDisplayId: Flow<Int?>
+    override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
         get() = pendingDisplayFlow
 }
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
index 8acc508..155dc1a 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -27,9 +27,6 @@
     name: "WallpaperBackup",
     defaults: ["platform_app_defaults"],
     srcs: ["src/**/*.java"],
-    optimize: {
-        proguard_flags_files: ["proguard.flags"],
-    },
     platform_apis: true,
     certificate: "platform",
     privileged: false,
diff --git a/packages/WallpaperBackup/proguard.flags b/packages/WallpaperBackup/proguard.flags
deleted file mode 100644
index 247e6ef..0000000
--- a/packages/WallpaperBackup/proguard.flags
+++ /dev/null
@@ -1 +0,0 @@
--keep class com.android.wallpaperbackup.WallpaperBackupAgent
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
index 7a7d376..6526c78 100644
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ b/services/companion/java/com/android/server/companion/virtual/Android.bp
@@ -1,9 +1,6 @@
 java_aconfig_library {
     name: "virtualdevice_flags_lib",
     aconfig_declarations: "virtualdevice_flags",
-    static_libs: [
-        "android.companion.virtual.flags-aconfig-java",
-    ],
 }
 
 aconfig_declarations {
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index dcc36bc..0ded75a 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -199,28 +199,18 @@
             @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
             @AppOpsManager.AttributionFlags
                     int attributionFlags, int attributionChainId) throws RemoteException {
-        started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
-                uidState, flags, /*triggerCallbackIfNeeded*/ true, attributionFlags,
-                attributionChainId);
-    }
-
-    private void started(@NonNull IBinder clientId, int proxyUid,
-            @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
-            @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
-            boolean triggerCallbackIfNeeded, @AppOpsManager.AttributionFlags int attributionFlags,
-            int attributionChainId) throws RemoteException {
         startedOrPaused(clientId, proxyUid, proxyPackageName,
-                proxyAttributionTag, uidState, flags, triggerCallbackIfNeeded,
-                /*triggerCallbackIfNeeded*/ true, attributionFlags, attributionChainId);
+                proxyAttributionTag, uidState, flags, /* triggeredByUidStateChange */ false,
+                /* isStarted */ true, attributionFlags, attributionChainId);
     }
 
     @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
     private void startedOrPaused(@NonNull IBinder clientId, int proxyUid,
             @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
             @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
-            boolean triggerCallbackIfNeeded, boolean isStarted, @AppOpsManager.AttributionFlags
+            boolean triggeredByUidStateChange, boolean isStarted, @AppOpsManager.AttributionFlags
             int attributionFlags, int attributionChainId) throws RemoteException {
-        if (triggerCallbackIfNeeded && !parent.isRunning() && isStarted) {
+        if (!triggeredByUidStateChange && !parent.isRunning() && isStarted) {
             mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                     parent.packageName, tag, true, attributionFlags, attributionChainId);
         }
@@ -263,19 +253,27 @@
      * @param clientId Id of the finishOp caller
      */
     public void finished(@NonNull IBinder clientId) {
-        finished(clientId, true);
+        finished(clientId, false);
     }
 
-    private void finished(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded) {
-        finishOrPause(clientId, triggerCallbackIfNeeded, false);
+    private void finished(@NonNull IBinder clientId, boolean triggeredByUidStateChange) {
+        finishOrPause(clientId, triggeredByUidStateChange, false);
     }
 
     /**
      * Update state when paused or finished is called. If pausing, it records the op as
      * stopping in the HistoricalRegistry, but does not delete it.
+     *
+     * @param triggeredByUidStateChange If {@code true}, then this method operates as usual, except
+     * that {@link AppOpsService#mActiveWatchers} will not be notified. This is currently only
+     * used in {@link #onUidStateChanged(int)}, for the purpose of restarting (i.e.,
+     * finishing then immediately starting again in the new uid state) the AttributedOp. In this
+     * case, the caller is responsible for guaranteeing that either the AttributedOp is started
+     * again or all {@link AppOpsService#mActiveWatchers} are notified that the AttributedOp is
+     * finished.
      */
     @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
-    private void finishOrPause(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded,
+    private void finishOrPause(@NonNull IBinder clientId, boolean triggeredByUidStateChange,
             boolean isPausing) {
         int indexOfToken = isRunning() ? mInProgressEvents.indexOfKey(clientId) : -1;
         if (indexOfToken < 0) {
@@ -320,7 +318,7 @@
                     mInProgressEvents = null;
 
                     // TODO ntmyren: Also callback for single attribution tag activity changes
-                    if (triggerCallbackIfNeeded && !parent.isRunning()) {
+                    if (!triggeredByUidStateChange && !parent.isRunning()) {
                         mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op,
                                 parent.uid, parent.packageName, tag, false,
                                 event.getAttributionFlags(), event.getAttributionChainId());
@@ -368,7 +366,7 @@
             @AppOpsManager.AttributionFlags
                     int attributionFlags, int attributionChainId) throws RemoteException {
         startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
-                uidState, flags, true, false, attributionFlags, attributionChainId);
+                uidState, flags, false, false, attributionFlags, attributionChainId);
     }
 
     /**
@@ -386,7 +384,7 @@
         for (int i = 0; i < mInProgressEvents.size(); i++) {
             InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
             mPausedInProgressEvents.put(event.getClientId(), event);
-            finishOrPause(event.getClientId(), true, true);
+            finishOrPause(event.getClientId(), false, true);
 
             mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                     parent.packageName, tag, false,
@@ -475,6 +473,8 @@
             InProgressStartOpEvent event = events.get(binders.get(i));
 
             if (event != null && event.getUidState() != newState) {
+                int eventAttributionFlags = event.getAttributionFlags();
+                int eventAttributionChainId = event.getAttributionChainId();
                 try {
                     // Remove all but one unfinished start count and then call finished() to
                     // remove start event object
@@ -482,18 +482,18 @@
                     event.mNumUnfinishedStarts = 1;
                     AppOpsManager.OpEventProxyInfo proxy = event.getProxy();
 
-                    finished(event.getClientId(), false);
+                    finished(event.getClientId(), true);
 
                     // Call started() to add a new start event object and then add the
                     // previously removed unfinished start counts back
                     if (proxy != null) {
                         startedOrPaused(event.getClientId(), proxy.getUid(),
                                 proxy.getPackageName(), proxy.getAttributionTag(), newState,
-                                event.getFlags(), false, isRunning,
+                                event.getFlags(), true, isRunning,
                                 event.getAttributionFlags(), event.getAttributionChainId());
                     } else {
                         startedOrPaused(event.getClientId(), Process.INVALID_UID, null, null,
-                                newState, event.getFlags(), false, isRunning,
+                                newState, event.getFlags(), true, isRunning,
                                 event.getAttributionFlags(), event.getAttributionChainId());
                     }
 
@@ -507,6 +507,9 @@
                         Slog.e(AppOpsService.TAG,
                                 "Cannot switch to new uidState " + newState);
                     }
+                    mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op,
+                            parent.uid, parent.packageName, tag, false,
+                            eventAttributionFlags, eventAttributionChainId);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7072b85..3d5b7fa 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -166,6 +166,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceDebugInfo;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.SystemClock;
@@ -13078,12 +13079,25 @@
 
     private static final String AUDIO_HAL_SERVICE_PREFIX = "android.hardware.audio";
 
-    private Set<Integer> getAudioHalPids() {
+    private void getAudioAidlHalPids(HashSet<Integer> pids) {
+        try {
+            ServiceDebugInfo[] infos = ServiceManager.getServiceDebugInfo();
+            if (infos == null) return;
+            for (ServiceDebugInfo info : infos) {
+                if (info.debugPid > 0 && info.name.startsWith(AUDIO_HAL_SERVICE_PREFIX)) {
+                    pids.add(info.debugPid);
+                }
+            }
+        } catch (RuntimeException e) {
+            // ignored, pid hashset does not change
+        }
+    }
+
+    private void getAudioHalHidlPids(HashSet<Integer> pids) {
         try {
             IServiceManager serviceManager = IServiceManager.getService();
             ArrayList<IServiceManager.InstanceDebugInfo> dump =
                     serviceManager.debugDump();
-            HashSet<Integer> pids = new HashSet<>();
             for (IServiceManager.InstanceDebugInfo info : dump) {
                 if (info.pid != IServiceManager.PidConstant.NO_PID
                         && info.interfaceName != null
@@ -13091,12 +13105,18 @@
                     pids.add(info.pid);
                 }
             }
-            return pids;
         } catch (RemoteException | RuntimeException e) {
-            return new HashSet<Integer>();
+            // ignored, pid hashset does not change
         }
     }
 
+    private Set<Integer> getAudioHalPids() {
+        HashSet<Integer> pids = new HashSet<>();
+        getAudioAidlHalPids(pids);
+        getAudioHalHidlPids(pids);
+        return pids;
+    }
+
     private void updateAudioHalPids() {
         Set<Integer> pidsSet = getAudioHalPids();
         if (pidsSet.isEmpty()) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b3aa09b..cbac39a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -559,6 +559,7 @@
     boolean mWakeOnDpadKeyPress;
     boolean mWakeOnAssistKeyPress;
     boolean mWakeOnBackKeyPress;
+    boolean mSilenceRingerOnSleepKey;
     long mWakeUpToLastStateTimeout;
     int mSearchKeyBehavior;
     ComponentName mSearchKeyTargetActivity;
@@ -1423,6 +1424,15 @@
     }
 
     private void sleepRelease(long eventTime) {
+        if (mSilenceRingerOnSleepKey) {
+            TelecomManager telecomManager = getTelecommService();
+            if (telecomManager != null && telecomManager.isRinging()) {
+                telecomManager.silenceRinger();
+                Slog.i(TAG, "sleepRelease() silence ringer");
+                return;
+            }
+        }
+
         switch (mShortPressOnSleepBehavior) {
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
@@ -2347,6 +2357,8 @@
                 com.android.internal.R.string.config_primaryShortPressTargetActivity));
         mShortPressOnSleepBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_shortPressOnSleepBehavior);
+        mSilenceRingerOnSleepKey = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_silenceRingerOnSleepKey);
         mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index cc95da5..9905ddf 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -31,7 +31,6 @@
 import android.app.trust.ITrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -42,11 +41,9 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -161,9 +158,6 @@
     private final ActivityManager mActivityManager;
     private VirtualDeviceManagerInternal mVirtualDeviceManager;
 
-    @GuardedBy("mUserIsTrusted")
-    private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
-
     private enum TrustState {
         UNTRUSTED, // the phone is not unlocked by any trustagents
         TRUSTABLE, // the phone is in a semi-locked state that can be unlocked if
@@ -224,7 +218,6 @@
             mIdleTrustableTimeoutAlarmListenerForUser = new SparseArray<>();
     private AlarmManager mAlarmManager;
     private final Object mAlarmLock = new Object();
-    private final SettingsObserver mSettingsObserver;
 
     private final StrongAuthTracker mStrongAuthTracker;
 
@@ -266,7 +259,6 @@
         mLockPatternUtils = injector.getLockPatternUtils();
         mStrongAuthTracker = new StrongAuthTracker(context, injector.getLooper());
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-        mSettingsObserver = new SettingsObserver(mHandler);
     }
 
     @Override
@@ -294,103 +286,10 @@
         }
     }
 
-    // Extend unlock config and logic
-    private final class SettingsObserver extends ContentObserver {
-        private final Uri TRUST_AGENTS_EXTEND_UNLOCK =
-                Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK);
-
-        private final Uri LOCK_SCREEN_WHEN_TRUST_LOST =
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST);
-
-        private final boolean mIsAutomotive;
-        private final ContentResolver mContentResolver;
-        private boolean mTrustAgentsNonrenewableTrust;
-        private boolean mLockWhenTrustLost;
-
-        /**
-         * Creates a settings observer
-         *
-         * @param handler The handler to run {@link #onChange} on, or null if none.
-         */
-        SettingsObserver(Handler handler) {
-            super(handler);
-
-            PackageManager packageManager = getContext().getPackageManager();
-            mIsAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
-
-            mContentResolver = getContext().getContentResolver();
-            updateContentObserver();
-        }
-
-        void updateContentObserver() {
-            mContentResolver.unregisterContentObserver(this);
-            mContentResolver.registerContentObserver(TRUST_AGENTS_EXTEND_UNLOCK,
-                    false /* notifyForDescendents */,
-                    this /* observer */,
-                    mCurrentUser);
-            mContentResolver.registerContentObserver(LOCK_SCREEN_WHEN_TRUST_LOST,
-                    false /* notifyForDescendents */,
-                    this /* observer */,
-                    mCurrentUser);
-
-            // Update the value immediately
-            onChange(true /* selfChange */, TRUST_AGENTS_EXTEND_UNLOCK);
-            onChange(true /* selfChange */, LOCK_SCREEN_WHEN_TRUST_LOST);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) {
-                // Smart lock should only grant non-renewable trust. The only exception is for
-                // automotive, where it can actively unlock the head unit.
-                int defaultValue = mIsAutomotive ? 0 : 1;
-
-                mTrustAgentsNonrenewableTrust =
-                        Settings.Secure.getIntForUser(
-                                mContentResolver,
-                                Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
-                                defaultValue,
-                                mCurrentUser) != 0;
-            } else if (LOCK_SCREEN_WHEN_TRUST_LOST.equals(uri)) {
-                mLockWhenTrustLost =
-                        Settings.Secure.getIntForUser(
-                                mContentResolver,
-                                Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
-                                0 /* default */,
-                                mCurrentUser) != 0;
-            }
-        }
-
-        boolean getTrustAgentsNonrenewableTrust() {
-            return mTrustAgentsNonrenewableTrust;
-        }
-
-        boolean getLockWhenTrustLost() {
-            return mLockWhenTrustLost;
-        }
-    }
-
-    private void maybeLockScreen(int userId) {
-        if (userId != mCurrentUser) {
-            return;
-        }
-
-        if (mSettingsObserver.getLockWhenTrustLost()) {
-            if (DEBUG) Slog.d(TAG, "Locking device because trust was lost");
-            try {
-                WindowManagerGlobal.getWindowManagerService().lockNow(null);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Error locking screen when trust was lost");
-            }
-
-            // If active unlocking is not allowed, cancel any pending trust timeouts because the
-            // screen is already locked.
-            TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
-            if (alarm != null && mSettingsObserver.getTrustAgentsNonrenewableTrust()) {
-                mAlarmManager.cancel(alarm);
-                alarm.setQueued(false /* isQueued */);
-            }
-        }
+    // Automotive head units can be unlocked by a trust agent, even when the agent doesn't use
+    // FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE.
+    private boolean isAutomotive() {
+        return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
     }
 
     private void scheduleTrustTimeout(boolean override, boolean isTrustableTimeout) {
@@ -587,12 +486,10 @@
         synchronized (mUserTrustState) {
             wasTrusted = (mUserTrustState.get(userId) == TrustState.TRUSTED);
             wasTrustable = (mUserTrustState.get(userId) == TrustState.TRUSTABLE);
-            boolean isAutomotive = getContext().getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_AUTOMOTIVE);
             boolean renewingTrust = wasTrustable && (
                     (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0);
             boolean canMoveToTrusted =
-                    alreadyUnlocked || isFromUnlock || renewingTrust || isAutomotive;
+                    alreadyUnlocked || isFromUnlock || renewingTrust || isAutomotive();
             boolean upgradingTrustForCurrentUser = (userId == mCurrentUser);
 
             if (trustedByAtLeastOneAgent && wasTrusted) {
@@ -619,9 +516,7 @@
                 isNowTrusted, newlyUnlocked, userId, flags, getTrustGrantedMessages(userId));
         if (isNowTrusted != wasTrusted) {
             refreshDeviceLockedForUser(userId);
-            if (!isNowTrusted) {
-                maybeLockScreen(userId);
-            } else {
+            if (isNowTrusted) {
                 boolean isTrustableTimeout =
                         (flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0;
                 // Every time we grant renewable trust we should override the idle trustable
@@ -1831,9 +1726,7 @@
             synchronized(mUsersUnlockedByBiometric) {
                 mUsersUnlockedByBiometric.put(userId, true);
             }
-            // In non-renewable trust mode we need to refresh trust state here, which will call
-            // refreshDeviceLockedForUser()
-            int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsNonrenewableTrust() ? 1 : 0;
+            int updateTrustOnUnlock = isAutomotive() ? 0 : 1;
             mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
                     updateTrustOnUnlock).sendToTarget();
             mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget();
@@ -1942,7 +1835,6 @@
                         break;
                     case MSG_SWITCH_USER:
                         mCurrentUser = msg.arg1;
-                        mSettingsObserver.updateContentObserver();
                         refreshDeviceLockedForUser(UserHandle.USER_ALL);
                         break;
                     case MSG_STOP_USER:
@@ -2041,9 +1933,6 @@
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 int userId = getUserId(intent);
                 if (userId > 0) {
-                    synchronized (mUserIsTrusted) {
-                        mUserIsTrusted.delete(userId);
-                    }
                     synchronized (mDeviceLockedForUser) {
                         mDeviceLockedForUser.delete(userId);
                     }
@@ -2175,7 +2064,6 @@
                 mLockPatternUtils.requireStrongAuth(
                         mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId);
             }
-            maybeLockScreen(mUserId);
         }
 
         protected abstract void handleAlarm();
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 0ab6d57..06a8516 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -96,6 +96,8 @@
     /* A map from a HDMI logical address to the matching TV input ID. */
     private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
     private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
+    /* A map from a HDMI input parent ID to the related input IDs. */
+    private final Map<String, List<String>> mHdmiParentInputMap = new ArrayMap<>();
 
     private final AudioManager mAudioManager;
     private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
@@ -293,6 +295,12 @@
         }
     }
 
+    public Map<String, List<String>> getHdmiParentInputMap() {
+        synchronized (mLock) {
+            return Collections.unmodifiableMap(mHdmiParentInputMap);
+        }
+    }
+
     private boolean checkUidChangedLocked(
             Connection connection, int callingUid, int resolvedUserId) {
         Integer connectionCallingUid = connection.getCallingUidLocked();
@@ -379,12 +387,15 @@
             }
             mHdmiInputIdMap.put(id, info.getId());
             mInputMap.put(info.getId(), info);
+            if (!mHdmiParentInputMap.containsKey(parentId)) {
+                mHdmiParentInputMap.put(parentId, new ArrayList<String>());
+            }
+            mHdmiParentInputMap.get(parentId).add(info.getId());
         }
     }
 
     public void removeHardwareInput(String inputId) {
         synchronized (mLock) {
-            mInputMap.remove(inputId);
             int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
             if (hardwareIndex >= 0) {
                 mHardwareInputIdMap.removeAt(hardwareIndex);
@@ -393,6 +404,17 @@
             if (deviceIndex >= 0) {
                 mHdmiInputIdMap.removeAt(deviceIndex);
             }
+            if (mInputMap.containsKey(inputId)) {
+                String parentId = mInputMap.get(inputId).getParentId();
+                if (parentId != null && mHdmiParentInputMap.containsKey(parentId)) {
+                    List<String> parentInputList = mHdmiParentInputMap.get(parentId);
+                    parentInputList.remove(inputId);
+                    if (parentInputList.isEmpty()) {
+                        mHdmiParentInputMap.remove(parentId);
+                    }
+                }
+                mInputMap.remove(inputId);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 2559b84..b12ecc3 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1839,22 +1839,22 @@
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
                                 channelUri, params);
                         UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
-                                userState);
+                        SessionState sessionState =
+                                getSessionStateLocked(sessionToken, callingUid, userState);
                         if (!sessionState.isCurrent
                                 || !Objects.equals(sessionState.currentChannel, channelUri)) {
                             sessionState.isCurrent = true;
                             sessionState.currentChannel = channelUri;
                             notifyCurrentChannelInfosUpdatedLocked(userState);
                             if (!sessionState.isRecordingSession) {
-                                if (mOnScreenInputId == null
-                                    || !TextUtils.equals(mOnScreenInputId, sessionState.inputId)) {
+                                String actualInputId = getActualInputId(sessionState);
+                                if (!TextUtils.equals(mOnScreenInputId, actualInputId)) {
                                     logExternalInputEvent(
-                                        FrameworkStatsLog
-                                                .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
-                                        sessionState.inputId, sessionState);
+                                            FrameworkStatsLog
+                                                    .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
+                                            actualInputId, sessionState);
                                 }
-                                mOnScreenInputId = sessionState.inputId;
+                                mOnScreenInputId = actualInputId;
                                 mOnScreenSessionState = sessionState;
                             }
                         }
@@ -2977,6 +2977,31 @@
         }
     }
 
+    // get the actual input id of the specific sessionState.
+    // e.g. if an HDMI port has a CEC device plugged in, the actual input id of the HDMI
+    // session should be the input id of CEC device instead of the default HDMI input id.
+    @GuardedBy("mLock")
+    private String getActualInputId(SessionState sessionState) {
+        UserState userState = getOrCreateUserStateLocked(sessionState.userId);
+        TvInputState tvInputState = userState.inputMap.get(sessionState.inputId);
+        TvInputInfo tvInputInfo = tvInputState.info;
+        String actualInputId = sessionState.inputId;
+        switch (tvInputInfo.getType()) {
+            case TvInputInfo.TYPE_HDMI:
+                // TODO: find a better approach towards active CEC device in future
+                Map<String, List<String>> hdmiParentInputMap =
+                        mTvInputHardwareManager.getHdmiParentInputMap();
+                if (hdmiParentInputMap.containsKey(sessionState.inputId)) {
+                    List<String> parentInputList = hdmiParentInputMap.get(sessionState.inputId);
+                    actualInputId = parentInputList.get(0);
+                }
+                break;
+            default:
+                break;
+        }
+        return actualInputId;
+    }
+
     @Nullable
     private static TvInputState getTvInputState(
             SessionState sessionState,
@@ -3078,6 +3103,7 @@
                 hdmiPort);
     }
 
+    @GuardedBy("mLock")
     private void logExternalInputEvent(int eventType, String inputId, SessionState sessionState) {
         UserState userState = getOrCreateUserStateLocked(sessionState.userId);
         TvInputState tvInputState = userState.inputMap.get(inputId);
@@ -3617,13 +3643,14 @@
                         mSessionState.currentChannel = channelUri;
                         notifyCurrentChannelInfosUpdatedLocked(userState);
                         if (!mSessionState.isRecordingSession) {
-                            if (mOnScreenInputId == null
-                                || !TextUtils.equals(mOnScreenInputId, mSessionState.inputId)) {
+                            String actualInputId = getActualInputId(mSessionState);
+                            if (!TextUtils.equals(mOnScreenInputId, actualInputId)) {
                                 logExternalInputEvent(
-                                    FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
-                                    mSessionState.inputId, mSessionState);
+                                        FrameworkStatsLog
+                                                .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
+                                        actualInputId, mSessionState);
                             }
-                            mOnScreenInputId = mSessionState.inputId;
+                            mOnScreenInputId = actualInputId;
                             mOnScreenSessionState = mSessionState;
                         }
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 90e67df..582536b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2929,7 +2929,7 @@
         reparent(newTaskFrag, position);
     }
 
-    private boolean isHomeIntent(Intent intent) {
+    static boolean isHomeIntent(Intent intent) {
         return ACTION_MAIN.equals(intent.getAction())
                 && (intent.hasCategory(CATEGORY_HOME)
                 || intent.hasCategory(CATEGORY_SECONDARY_HOME))
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1eb56f1..a5b1132 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -55,6 +55,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -77,7 +78,6 @@
 
     private final ActivityTaskManagerService mService;
     private final ActivityTaskSupervisor mSupervisor;
-    private final RootWindowContainer mRootWindowContainer;
     private final Context mServiceContext;
 
     // UserManager cannot be final as it's not ready when this class is instantiated during boot
@@ -110,17 +110,23 @@
     TaskFragment mInTaskFragment;
     ActivityOptions mActivityOptions;
 
+    /*
+     * Note that this is just a hint of what the launch display area will be as it is
+     * based only on the information at the early pre-interception stage of starting the
+     * intent. The real launch display area calculated later may be different from this one.
+     */
+    TaskDisplayArea mPresumableLaunchDisplayArea;
+
     ActivityStartInterceptor(
             ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) {
-        this(service, supervisor, service.mRootWindowContainer, service.mContext);
+        this(service, supervisor, service.mContext);
     }
 
     @VisibleForTesting
     ActivityStartInterceptor(ActivityTaskManagerService service, ActivityTaskSupervisor supervisor,
-            RootWindowContainer root, Context context) {
+            Context context) {
         mService = service;
         mSupervisor = supervisor;
-        mRootWindowContainer = root;
         mServiceContext = context;
     }
 
@@ -162,7 +168,7 @@
     /**
      * A helper function to obtain the targeted {@link TaskFragment} during
      * {@link #intercept(Intent, ResolveInfo, ActivityInfo, String, Task, TaskFragment, int, int,
-     * ActivityOptions)} if any.
+     * ActivityOptions, TaskDisplayArea)} if any.
      */
     @Nullable
     private TaskFragment getLaunchTaskFragment() {
@@ -187,7 +193,7 @@
      */
     boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
             Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
-            ActivityOptions activityOptions) {
+            ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea) {
         mUserManager = UserManager.get(mServiceContext);
 
         mIntent = intent;
@@ -199,6 +205,7 @@
         mInTask = inTask;
         mInTaskFragment = inTaskFragment;
         mActivityOptions = activityOptions;
+        mPresumableLaunchDisplayArea = presumableLaunchDisplayArea;
 
         if (interceptQuietProfileIfNeeded()) {
             // If work profile is turned off, skip the work challenge since the profile can only
@@ -221,6 +228,11 @@
         if (interceptLockedManagedProfileIfNeeded()) {
             return true;
         }
+        if (interceptHomeIfNeeded()) {
+            // Replace primary home intents directed at displays that do not support primary home
+            // but support secondary home with the relevant secondary home activity.
+            return true;
+        }
 
         final SparseArray<ActivityInterceptorCallback> callbacks =
                 mService.getActivityInterceptorCallbacks();
@@ -470,6 +482,47 @@
         return true;
     }
 
+    private boolean interceptHomeIfNeeded() {
+        if (mPresumableLaunchDisplayArea == null || mService.mRootWindowContainer == null) {
+            return false;
+        }
+        if (!ActivityRecord.isHomeIntent(mIntent)) {
+            return false;
+        }
+        if (!mIntent.hasCategory(Intent.CATEGORY_HOME)) {
+            // Already a secondary home intent, leave it alone.
+            return false;
+        }
+        if (mService.mRootWindowContainer.shouldPlacePrimaryHomeOnDisplay(
+                mPresumableLaunchDisplayArea.getDisplayId())) {
+            // Primary home can be launched to the display area.
+            return false;
+        }
+        if (!mService.mRootWindowContainer.shouldPlaceSecondaryHomeOnDisplayArea(
+                mPresumableLaunchDisplayArea)) {
+            // Secondary home cannot be launched on the display area.
+            return false;
+        }
+
+        // At this point we have a primary home intent for a display that does not support primary
+        // home activity but it supports secondary home one. So replace it with secondary home.
+        Pair<ActivityInfo, Intent> info = mService.mRootWindowContainer
+                .resolveSecondaryHomeActivity(mUserId, mPresumableLaunchDisplayArea);
+        mIntent = info.second;
+        // The new task flag is needed because the home activity should already be in the root task
+        // and should not be moved to the caller's task. Also, activities cannot change their type,
+        // e.g. a standard activity cannot become a home activity.
+        mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, /* flags= */ 0,
+                mRealCallingUid, mRealCallingPid);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, /*profilerInfo=*/ null);
+        return true;
+    }
+
     private boolean isPackageSuspended() {
         return mAInfo != null && mAInfo.applicationInfo != null
                 && (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) != 0;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1bc78d6..458d1e8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1154,10 +1154,12 @@
             }
         }
 
+        final TaskDisplayArea suggestedLaunchDisplayArea =
+                computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions);
         mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
                 callingFeatureId);
         if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
-                callingPid, callingUid, checkedOptions)) {
+                callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
             // activity start was intercepted, e.g. because the target user is currently in quiet
             // mode (turn off work) or the target application is suspended
             intent = mInterceptor.mIntent;
@@ -1890,6 +1892,15 @@
         mPreferredWindowingMode = mLaunchParams.mWindowingMode;
     }
 
+    private TaskDisplayArea computeSuggestedLaunchDisplayArea(
+            Task task, ActivityRecord source, ActivityOptions options) {
+        mSupervisor.getLaunchParamsController().calculate(task, /*layout=*/null,
+                /*activity=*/ null, source, options, mRequest, PHASE_DISPLAY, mLaunchParams);
+        return mLaunchParams.hasPreferredTaskDisplayArea()
+                ? mLaunchParams.mPreferredTaskDisplayArea
+                : mRootWindowContainer.getDefaultTaskDisplayArea();
+    }
+
     @VisibleForTesting
     int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) {
         if (r.packageName == null) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 57f8268..d56acaa 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1608,6 +1608,19 @@
     }
 
     /**
+     * Check if the display is valid for primary home activity.
+     *
+     * @param displayId The target display ID
+     * @return {@code true} if allowed to launch, {@code false} otherwise.
+     */
+    boolean shouldPlacePrimaryHomeOnDisplay(int displayId) {
+        // No restrictions to default display, vr 2d display or main display for visible users.
+        return displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+                && (displayId == mService.mVr2dDisplayId
+                || mWmService.shouldPlacePrimaryHomeOnDisplay(displayId)));
+    }
+
+    /**
      * Check if the display area is valid for secondary home activity.
      *
      * @param taskDisplayArea The target display area.
@@ -1680,10 +1693,7 @@
 
         final int displayId = taskDisplayArea != null ? taskDisplayArea.getDisplayId()
                 : INVALID_DISPLAY;
-        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
-                && (displayId == mService.mVr2dDisplayId
-                || mWmService.shouldPlacePrimaryHomeOnDisplay(displayId)))) {
-            // No restrictions to default display, vr 2d display or main display for visible users.
+        if (shouldPlacePrimaryHomeOnDisplay(displayId)) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 387a876..21a4fe8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2815,7 +2815,7 @@
         final WindowManager.LayoutParams attrs = win.mAttrs;
         visibleFrame.set(win.getFrame());
         visibleFrame.inset(win.getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
-                visibleFrame, attrs.type, win.getWindowingMode(), attrs.softInputMode,
+                visibleFrame, attrs.type, win.getActivityType(), attrs.softInputMode,
                 attrs.flags));
         out.union(visibleFrame);
     }
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index ad46770..fa620db 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -118,12 +118,14 @@
             root = activity;
         }
 
-        if (root == null) {
+        if (root == null && phase != PHASE_DISPLAY) {
             // There is a case that can lead us here. The caller is moving the top activity that is
             // in a task that has multiple activities to PIP mode. For that the caller is creating a
             // new task to host the activity so that we only move the top activity to PIP mode and
             // keep other activities in the previous task. There is no point to apply the launch
             // logic in this case.
+            // However, for PHASE_DISPLAY the root may be null, but we still want to get a hint of
+            // what the suggested launch display area would be.
             return RESULT_SKIP;
         }
 
@@ -395,8 +397,9 @@
     }
 
     private TaskDisplayArea getPreferredLaunchTaskDisplayArea(@Nullable Task task,
-            @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams,
-            @NonNull ActivityRecord activityRecord, @Nullable Request request) {
+            @Nullable ActivityOptions options, @Nullable ActivityRecord source,
+            @Nullable LaunchParams currentParams, @Nullable ActivityRecord activityRecord,
+            @Nullable Request request) {
         TaskDisplayArea taskDisplayArea = null;
 
         final WindowContainerToken optionLaunchTaskDisplayAreaToken = options != null
@@ -438,8 +441,7 @@
 
         // If the source activity is a no-display activity, pass on the launch display area token
         // from source activity as currently preferred.
-        if (taskDisplayArea == null && source != null
-                && source.noDisplay) {
+        if (taskDisplayArea == null && source != null && source.noDisplay) {
             taskDisplayArea = source.mHandoverTaskDisplayArea;
             if (taskDisplayArea != null) {
                 if (DEBUG) appendLog("display-area-from-no-display-source=" + taskDisplayArea);
@@ -478,21 +480,24 @@
             }
         }
 
-        if (taskDisplayArea == null) {
+        if (taskDisplayArea == null && currentParams != null) {
             taskDisplayArea = currentParams.mPreferredTaskDisplayArea;
+            if (DEBUG) appendLog("display-area-from-current-params=" + taskDisplayArea);
         }
 
         // Re-route to default display if the device didn't declare support for multi-display
         if (taskDisplayArea != null && !mSupervisor.mService.mSupportsMultiDisplay
                 && taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY) {
             taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+            if (DEBUG) appendLog("display-area-from-no-multidisplay=" + taskDisplayArea);
         }
 
         // Re-route to default display if the home activity doesn't support multi-display
-        if (taskDisplayArea != null && activityRecord.isActivityTypeHome()
+        if (taskDisplayArea != null && activityRecord != null && activityRecord.isActivityTypeHome()
                 && !mSupervisor.mRootWindowContainer.canStartHomeOnDisplayArea(activityRecord.info,
                         taskDisplayArea, false /* allowInstrumenting */)) {
             taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+            if (DEBUG) appendLog("display-area-from-home=" + taskDisplayArea);
         }
 
         return (taskDisplayArea != null)
@@ -516,34 +521,56 @@
      * @return {@link TaskDisplayArea} to house the task
      */
     private TaskDisplayArea getFallbackDisplayAreaForActivity(
-            @NonNull ActivityRecord activityRecord, @Nullable Request request) {
+            @Nullable ActivityRecord activityRecord, @Nullable Request request) {
+        if (activityRecord != null) {
+            WindowProcessController controllerFromLaunchingRecord =
+                    mSupervisor.mService.getProcessController(
+                            activityRecord.launchedFromPid, activityRecord.launchedFromUid);
+            if (controllerFromLaunchingRecord != null) {
+                final TaskDisplayArea taskDisplayAreaForLaunchingRecord =
+                        controllerFromLaunchingRecord.getTopActivityDisplayArea();
+                if (taskDisplayAreaForLaunchingRecord != null) {
+                    if (DEBUG) {
+                        appendLog("display-area-for-launching-record="
+                                + taskDisplayAreaForLaunchingRecord);
+                    }
+                    return taskDisplayAreaForLaunchingRecord;
+                }
+            }
 
-        WindowProcessController controllerFromLaunchingRecord = mSupervisor.mService
-                .getProcessController(activityRecord.launchedFromPid,
-                        activityRecord.launchedFromUid);
-        final TaskDisplayArea displayAreaForLaunchingRecord = controllerFromLaunchingRecord == null
-                ? null : controllerFromLaunchingRecord.getTopActivityDisplayArea();
-        if (displayAreaForLaunchingRecord != null) {
-            return displayAreaForLaunchingRecord;
+            WindowProcessController controllerFromProcess =
+                    mSupervisor.mService.getProcessController(
+                            activityRecord.getProcessName(), activityRecord.getUid());
+            if (controllerFromProcess != null) {
+                final TaskDisplayArea displayAreaForRecord =
+                        controllerFromProcess.getTopActivityDisplayArea();
+                if (displayAreaForRecord != null) {
+                    if (DEBUG) appendLog("display-area-for-record=" + displayAreaForRecord);
+                    return displayAreaForRecord;
+                }
+            }
         }
 
-        WindowProcessController controllerFromProcess = mSupervisor.mService.getProcessController(
-                activityRecord.getProcessName(), activityRecord.getUid());
-        final TaskDisplayArea displayAreaForRecord = controllerFromProcess == null ? null
-                : controllerFromProcess.getTopActivityDisplayArea();
-        if (displayAreaForRecord != null) {
-            return displayAreaForRecord;
+        if (request != null) {
+            WindowProcessController controllerFromRequest =
+                    mSupervisor.mService.getProcessController(
+                            request.realCallingPid, request.realCallingUid);
+            if (controllerFromRequest != null) {
+                final TaskDisplayArea displayAreaFromSourceProcess =
+                            controllerFromRequest.getTopActivityDisplayArea();
+                if (displayAreaFromSourceProcess != null) {
+                    if (DEBUG) {
+                        appendLog("display-area-source-process=" + displayAreaFromSourceProcess);
+                    }
+                    return displayAreaFromSourceProcess;
+                }
+            }
         }
 
-        WindowProcessController controllerFromRequest = request == null ? null : mSupervisor
-                .mService.getProcessController(request.realCallingPid, request.realCallingUid);
-        final TaskDisplayArea displayAreaFromSourceProcess = controllerFromRequest == null ? null
-                : controllerFromRequest.getTopActivityDisplayArea();
-        if (displayAreaFromSourceProcess != null) {
-            return displayAreaFromSourceProcess;
-        }
-
-        return mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+        final TaskDisplayArea defaultTaskDisplayArea =
+                mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+        if (DEBUG) appendLog("display-area-from-default-fallback=" + defaultTaskDisplayArea);
+        return defaultTaskDisplayArea;
     }
 
     private boolean canInheritWindowingModeFromSource(@NonNull DisplayContent display,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a172d99..9c04e0a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1759,7 +1759,7 @@
 
         bounds.set(mWindowFrames.mFrame);
         bounds.inset(getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
-                bounds, mAttrs.type, getWindowingMode(), mAttrs.softInputMode, mAttrs.flags));
+                bounds, mAttrs.type, getActivityType(), mAttrs.softInputMode, mAttrs.flags));
         if (intersectWithRootTaskBounds) {
             bounds.intersect(mTmpRect);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index bcb0c6b..0989db4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -59,6 +59,7 @@
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 import android.testing.DexmakerShareClassLoaderRule;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
@@ -128,6 +129,8 @@
     private ActivityManagerInternal mAmInternal;
     @Mock
     private LockTaskController mLockTaskController;
+    @Mock
+    private TaskDisplayArea mTaskDisplayArea;
 
     private ActivityStartInterceptor mInterceptor;
     private ActivityInfo mAInfo = new ActivityInfo();
@@ -139,8 +142,8 @@
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         mService.mAmInternal = mAmInternal;
-        mInterceptor = new ActivityStartInterceptor(
-                mService, mSupervisor, mRootWindowContainer, mContext);
+        mService.mRootWindowContainer = mRootWindowContainer;
+        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
         mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
                 TEST_START_FLAGS, TEST_CALLING_PACKAGE, null);
 
@@ -201,7 +204,7 @@
                 .thenReturn(PLATFORM_PACKAGE_NAME);
 
         // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
 
         // THEN the returned intent is the admin support intent
         assertEquals(ADMIN_SUPPORT_INTENT, mInterceptor.mIntent);
@@ -212,7 +215,7 @@
         final String suspendingPackage = "com.test.suspending.package";
         final SuspendDialogInfo dialogInfo = suspendPackage(suspendingPackage);
         // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
 
         // Check intent parameters
         assertEquals(dialogInfo,
@@ -243,7 +246,7 @@
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
                 .thenReturn(false);
 
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
 
         assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
                 .filterEquals(mInterceptor.mIntent));
@@ -257,7 +260,8 @@
         when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
 
         // THEN calling intercept returns false because package also has to be suspended.
-        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null,  null, 0, 0, null));
+        assertFalse(
+                mInterceptor.intercept(null, null, mAInfo, null, null,  null, 0, 0, null, null));
     }
 
     @Test
@@ -268,7 +272,7 @@
         when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
 
         // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null,  null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null,  null, 0, 0, null, null));
 
         // THEN the returned intent is the quiet mode intent
         assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
@@ -284,7 +288,7 @@
         when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
 
         // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
 
         // THEN the returned intent is the quiet mode intent
         assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
@@ -300,7 +304,7 @@
         when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
 
         // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
 
         // THEN the returned intent is the quiet mode intent
         assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
@@ -313,7 +317,7 @@
         when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
 
         // THEN calling intercept returns true
-        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // THEN the returned intent is the confirm credentials intent
         assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
@@ -329,7 +333,7 @@
         mAInfo.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
 
         // THEN calling intercept returns true
-        mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // THEN the returned intent is original intent
         assertSame(originalIntent, mInterceptor.mIntent);
@@ -345,7 +349,7 @@
         mAInfo.directBootAware = false;
 
         // THEN calling intercept returns true
-        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // THEN the returned intent is the confirm credentials intent
         assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
@@ -362,7 +366,7 @@
         mAInfo.directBootAware = true;
 
         // THEN calling intercept returns true
-        mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // THEN the returned intent is original intent
         assertSame(originalIntent, mInterceptor.mIntent);
@@ -375,7 +379,7 @@
                 .thenReturn("This app is bad");
 
         // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
 
         // THEN the returned intent is the harmful app warning intent
         assertEquals(HarmfulAppWarningActivity.class.getName(),
@@ -383,11 +387,40 @@
     }
 
     @Test
+    public void testHomeIntentInterception() {
+        // GIVEN a primary home intent and a display area that doesn't support it but supports
+        // secondary home activities
+        Intent originalIntent = new Intent(Intent.ACTION_MAIN);
+        originalIntent.addCategory(Intent.CATEGORY_HOME);
+
+        Intent expectedIntent = new Intent(Intent.ACTION_MAIN);
+        expectedIntent.addCategory(Intent.CATEGORY_SECONDARY_HOME);
+        expectedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final int secondaryDisplayId = 7;
+        when(mTaskDisplayArea.getDisplayId()).thenReturn(secondaryDisplayId);
+        when(mRootWindowContainer.shouldPlacePrimaryHomeOnDisplay(eq(secondaryDisplayId)))
+                .thenReturn(false);
+        when(mRootWindowContainer.shouldPlaceSecondaryHomeOnDisplayArea(eq(mTaskDisplayArea)))
+                .thenReturn(true);
+        when(mRootWindowContainer.resolveSecondaryHomeActivity(
+                eq(TEST_USER_ID), eq(mTaskDisplayArea)))
+                .thenReturn(Pair.create(null, expectedIntent));
+
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0,
+                null, mTaskDisplayArea));
+
+        // THEN the returned intent is the secondary home intent
+        assertSame(expectedIntent, mInterceptor.mIntent);
+    }
+
+    @Test
     public void testNoInterception() {
         // GIVEN that none of the interception conditions are met
 
         // THEN calling intercept returns false
-        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
     }
 
     public void addMockInterceptorCallback(
@@ -420,7 +453,7 @@
                 new Intent("android.test.foo"),
                 ActivityOptions.makeBasic().setLaunchDisplayId(3));
 
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
         assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
         assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
     }
@@ -429,7 +462,7 @@
     public void testInterceptionCallback_singleCallbackReturnsNull() {
         addMockInterceptorCallback(null, null);
 
-        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
     }
 
     @Test
@@ -437,7 +470,7 @@
         addMockInterceptorCallback(null, null);
         addMockInterceptorCallback(new Intent("android.test.second"), null);
 
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
         assertEquals("android.test.second", mInterceptor.mIntent.getAction());
     }
 
@@ -447,7 +480,7 @@
                 new Intent("android.test.foo"),
                 ActivityOptions.makeBasic().setLaunchDisplayId(3), true);
         ActivityInfo aInfo = mAInfo;
-        assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null, null));
         assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
         assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
         assertEquals(aInfo, mInterceptor.mAInfo); // mAInfo should not be resolved
@@ -459,7 +492,7 @@
                 new Intent("android.test.foo"),
                 ActivityOptions.makeBasic().setLaunchDisplayId(3));
         ActivityInfo aInfo = mAInfo;
-        assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null));
+        assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null, null));
         assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
         assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
         assertNotEquals(aInfo, mInterceptor.mAInfo); // mAInfo should be resolved after intercept
@@ -488,7 +521,7 @@
         when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
 
         Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         verify(spyCallback, times(1)).onInterceptActivityLaunch(
                 any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
@@ -505,7 +538,7 @@
         when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
 
         Intent intent = new Intent().setPackage(sandboxPackageNameMock);
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         verify(spyCallback, times(1)).onInterceptActivityLaunch(
                 any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
@@ -522,7 +555,7 @@
         when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
 
         Intent intent = new Intent().setComponent(new ComponentName(sandboxPackageNameMock, ""));
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         verify(spyCallback, times(1)).onInterceptActivityLaunch(
                 any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
@@ -539,23 +572,23 @@
         when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
 
         // Intent: null
-        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // Action: null, Package: null, ComponentName: null
         Intent intent = new Intent();
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // Wrong Action
         intent = new Intent().setAction(Intent.ACTION_VIEW);
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // Wrong Package
         intent = new Intent().setPackage("Random");
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         // Wrong ComponentName's package
         intent = new Intent().setComponent(new ComponentName("Random", ""));
-        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null, null);
 
         verify(spyCallback, never()).onInterceptActivityLaunch(
                 any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
diff --git a/startop/apps/ColorChanging/app/build.gradle b/startop/apps/ColorChanging/app/build.gradle
index ab955aa..11b14c0 100644
--- a/startop/apps/ColorChanging/app/build.gradle
+++ b/startop/apps/ColorChanging/app/build.gradle
@@ -14,7 +14,7 @@
     buildTypes {
         release {
             minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
         }
     }
 }
diff --git a/startop/apps/ColorChanging/app/proguard-rules.pro b/startop/apps/ColorChanging/app/proguard-rules.pro
deleted file mode 100644
index f1b4245..0000000
--- a/startop/apps/ColorChanging/app/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-#   public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0bb75d8..ac8200a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9456,6 +9456,19 @@
             "carrier_supported_satellite_services_per_provider_bundle";
 
     /**
+     * This config enables modem to scan satellite PLMNs specified as per
+     * {@link #KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE} and attach to same
+     * in case cellular networks are not enabled. This will need specific agreement between
+     * satellite provider and the carrier before enabling this flag.
+     *
+     * The default value is false.
+     *
+     * @hide
+     */
+    public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL =
+            "satellite_attach_supported_bool";
+
+    /**
      * Indicating whether DUN APN should be disabled when the device is roaming. In that case,
      * the default APN (i.e. internet) will be used for tethering.
      *
@@ -10465,6 +10478,7 @@
         sDefaults.putPersistableBundle(
                 KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
                 PersistableBundle.EMPTY);
+        sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
         sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 75b5f55..5f6c14a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -44,10 +44,14 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.time.Duration;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * Manages satellite operations such as provisioning, pointing, messaging, location sharing, etc.
@@ -79,6 +83,23 @@
     @Nullable private final Context mContext;
 
     /**
+     * Create a new SatelliteManager object pinned to the given subscription ID.
+     * This is needed only to handle carrier specific satellite features.
+     * For eg: requestSatelliteAttachEnabledForCarrier and
+     *         requestIsSatelliteAttachEnabledForCarrier
+     *
+     * @return a SatelliteManager that uses the given subId for all satellite activities.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @hide
+     */
+    public SatelliteManager createForSubscriptionId(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+        return new SatelliteManager(mContext, subId);
+    }
+
+    /**
      * Create an instance of the SatelliteManager.
      *
      * @param context The context the SatelliteManager belongs to.
@@ -791,6 +812,27 @@
     public @interface DatagramType {}
 
     /**
+     * Satellite communication restricted by user.
+     * @hide
+     */
+    public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER = 0;
+
+    /**
+     * Satellite communication restricted by geolocation. This can be
+     * triggered based upon geofence input provided by carrier to enable or disable satellite.
+     * @hide
+     */
+    public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1;
+
+    /** @hide */
+    @IntDef(prefix = "SATELLITE_COMMUNICATION_RESTRICTION_REASON_", value = {
+            SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER,
+            SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SatelliteCommunicationRestrictionReason {}
+
+    /**
      * Start receiving satellite transmission updates.
      * This can be called by the pointing UI when the user starts pointing to the satellite.
      * Modem should continue to report the pointing input as the device or satellite moves.
@@ -1559,6 +1601,182 @@
         }
     }
 
+    /**
+     * User request to enable or disable carrier supported satellite plmn scan and attach by modem.
+     * <p>
+     * This API should be called by only settings app to pass down the user input for
+     * enabling/disabling satellite. This user input will be persisted across device reboots.
+     * <p>
+     * Satellite will be enabled only when the following conditions are met:
+     * <ul>
+     * <li>Users want to enable it.</li>
+     * <li>There is no satellite communication restriction, which is added by
+     * {@link #addSatelliteAttachRestrictionForCarrier(int, Executor, Consumer)}</li>
+     * <li>The carrier config {@link
+     * android.telephony.CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to
+     * {@code true}.</li>
+     * </ul>
+     *
+     * @param enableSatellite {@code true} to enable the satellite and {@code false} to disable.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void requestSatelliteAttachEnabledForCarrier(boolean enableSatellite,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        if (enableSatellite) {
+            removeSatelliteAttachRestrictionForCarrier(
+                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
+        } else {
+            addSatelliteAttachRestrictionForCarrier(
+                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
+        }
+    }
+
+    /**
+     * Request to get whether the carrier supported satellite plmn scan and attach by modem is
+     * enabled by user.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the satellite
+     *                 is enabled and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteError}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void requestIsSatelliteAttachEnabledForCarrier(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier();
+        executor.execute(() -> callback.onResult(
+                !restrictionReason.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)));
+    }
+
+    /**
+     * Add a restriction reason for disallowing carrier supported satellite plmn scan and attach
+     * by modem.
+     *
+     * @param reason Reason for disallowing satellite communication.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void addSatelliteAttachRestrictionForCarrier(
+            @SatelliteCommunicationRestrictionReason int reason,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.addSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("addSatelliteAttachRestrictionForCarrier() RemoteException:" + ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove a restriction reason for disallowing carrier supported satellite plmn scan and attach
+     * by modem.
+     *
+     * @param reason Reason for disallowing satellite communication.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void removeSatelliteAttachRestrictionForCarrier(
+            @SatelliteCommunicationRestrictionReason int reason,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.removeSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("removeSatelliteAttachRestrictionForCarrier() RemoteException:" + ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get reasons for disallowing satellite attach, as requested by
+     * {@link #addSatelliteAttachRestrictionForCarrier(int, Executor, Consumer)}
+     *
+     * @return Set of reasons for disallowing satellite communication.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteCommunicationRestrictionReason
+    public @NonNull Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int[] receivedArray =
+                        telephony.getSatelliteAttachRestrictionReasonsForCarrier(mSubId);
+                if (receivedArray.length == 0) {
+                    logd("received set is empty, create empty set");
+                    return new HashSet<>();
+                } else {
+                    return Arrays.stream(receivedArray).boxed().collect(Collectors.toSet());
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("getSatelliteAttachRestrictionReasonsForCarrier() RemoteException: " + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return null;
+    }
+
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index ea4e2e2..02661de 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -382,4 +382,64 @@
      */
     void requestTimeForNextSatelliteVisibility(in IIntegerConsumer resultCallback,
             in IIntegerConsumer callback);
+
+    /**
+     * Set the non-terrestrial PLMN with lower priority than terrestrial networks.
+     * MCC/MNC broadcast by the non-terrestrial networks may not be included in OPLMNwACT file on
+     * SIM profile. Acquisition of satellite based system is lower priority to terrestrial
+     * networks. UE shall make all attempts to acquire terrestrial service prior to camping on
+     * satellite LTE service.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid error codes returned:
+     *   SatelliteError:NONE
+     *   SatelliteError:INVALID_ARGUMENTS
+     *   SatelliteError:INVALID_MODEM_STATE
+     *   SatelliteError:MODEM_ERR
+     *   SatelliteError:NO_RESOURCES
+     *   SatelliteError:RADIO_NOT_AVAILABLE
+     *   SatelliteError:REQUEST_NOT_SUPPORTED
+     */
+    void setSatellitePlmn(int simSlot, in List<String> plmnList,
+            in IIntegerConsumer resultCallback);
+
+    /**
+     * Enable or disable satellite in the cellular modem associated with a carrier.
+     * Refer setSatellitePlmn for the details of satellite PLMN scanning process.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param serial Serial number of request.
+     * @param enable {@code true} to enable satellite, {@code false} to disable satellite.
+     *
+     * Valid errors returned:
+     *   SatelliteError:NONE
+     *   SatelliteError:INVALID_MODEM_STATE
+     *   SatelliteError:MODEM_ERR
+     *   SatelliteError:RADIO_NOT_AVAILABLE
+     *   SatelliteError:REQUEST_NOT_SUPPORTED
+     */
+    void setSatelliteEnabledForCarrier(int simSlot, boolean satelliteEnabled,
+         in IIntegerConsumer callback);
+
+    /**
+     * Check whether satellite is enabled in the cellular modem associated with a carrier.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param serial Serial number of request.
+     *
+     * Valid errors returned:
+     *   SatelliteError:NONE
+     *   SatelliteError:INVALID_MODEM_STATE
+     *   SatelliteError:MODEM_ERR
+     *   SatelliteError:RADIO_NOT_AVAILABLE
+     *   SatelliteError:REQUEST_NOT_SUPPORTED
+     */
+    void requestIsSatelliteEnabledForCarrier(int simSlot, in IIntegerConsumer resultCallback,
+            in IBooleanConsumer callback);
 }
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index 17d026c..6451daf 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -25,6 +25,7 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.util.TelephonyUtils;
 
+import java.util.List;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
@@ -212,6 +213,34 @@
                     "requestTimeForNextSatelliteVisibility");
         }
 
+        @Override
+        public void setSatellitePlmn(int simSlot, List<String> plmnList,
+                IIntegerConsumer errorCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .setSatellitePlmn(simSlot, plmnList, errorCallback),
+                    "setSatellitePlmn");
+        }
+
+        @Override
+        public void setSatelliteEnabledForCarrier(int simSlot, boolean enableSatellite,
+                IIntegerConsumer errorCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .setSatelliteEnabledForCarrier(simSlot, enableSatellite, errorCallback),
+                    "setSatelliteEnabledForCarrier");
+        }
+
+        @Override
+        public void requestIsSatelliteEnabledForCarrier(int simSlot, IIntegerConsumer errorCallback,
+                IBooleanConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestIsSatelliteEnabledForCarrier(simSlot, errorCallback, callback),
+                    "requestIsSatelliteEnabledForCarrier");
+        }
+
         // Call the methods with a clean calling identity on the executor and wait indefinitely for
         // the future to return.
         private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
@@ -618,4 +647,75 @@
             @NonNull IIntegerConsumer callback) {
         // stub implementation
     }
+
+
+    /**
+     * Set the non-terrestrial PLMN with lower priority than terrestrial networks.
+     * MCC/MNC broadcast by the non-terrestrial networks may not be included in OPLMNwACT file on
+     * SIM profile. Acquisition of satellite based system is lower priority to terrestrial
+     * networks. UE shall make all attempts to acquire terrestrial service prior to camping on
+     * satellite LTE service.
+     * This method must only take effect if {@link #setSatelliteEnabledForCarrier} is {@code true},
+     * and return an error otherwise.
+     *
+     * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
+     * applied. The modem will use this information to determine the relevant carrier.
+     * @param errorCallback The callback to receive the error code result of the operation.
+     * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+     *
+     * Valid error codes returned:
+     *   SatelliteError:NONE
+     *   SatelliteError:INVALID_ARGUMENTS
+     *   SatelliteError:INVALID_MODEM_STATE
+     *   SatelliteError:MODEM_ERR
+     *   SatelliteError:NO_RESOURCES
+     *   SatelliteError:RADIO_NOT_AVAILABLE
+     *   SatelliteError:REQUEST_NOT_SUPPORTED
+     */
+    public void setSatellitePlmn(@NonNull int simLogicalSlotIndex, @NonNull List<String> plmnList,
+            @NonNull IIntegerConsumer errorCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to enable or disable carrier supported satellite plmn scan and attach by modem.
+     * Refer {@link #setSatellitePlmn} for the details of satellite PLMN scanning process.
+     *
+     * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
+     * applied. The modem will use this information to determine the relevant carrier.
+     * @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite.
+     * @param callback {@code true} to enable satellite, {@code false} to disable satellite.
+     *
+     * Valid errors returned:
+     *   SatelliteError:NONE
+     *   SatelliteError:INVALID_MODEM_STATE
+     *   SatelliteError:MODEM_ERR
+     *   SatelliteError:RADIO_NOT_AVAILABLE
+     *   SatelliteError:REQUEST_NOT_SUPPORTED
+     */
+    public void setSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
+            @NonNull boolean satelliteEnabled, @NonNull IIntegerConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get whether the satellite is enabled in the cellular modem associated with a
+     * carrier.
+     *
+     * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
+     * applied. The modem will use this information to determine the relevant carrier.
+     * @param errorCallback The callback to receive the error code result of the operation.
+     * @param callback {@code true} to satellite enabled, {@code false} to satellite disabled.
+     *
+     * Valid errors returned:
+     *   SatelliteError:NONE
+     *   SatelliteError:INVALID_MODEM_STATE
+     *   SatelliteError:MODEM_ERR
+     *   SatelliteError:RADIO_NOT_AVAILABLE
+     *   SatelliteError:REQUEST_NOT_SUPPORTED
+     */
+    public void requestIsSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
+            @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) {
+        // stub implementation
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 06071fe..3aa5a5a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3033,4 +3033,42 @@
      @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
                  + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
      List<String> getShaIdFromAllowList(String pkgName, int carrierId);
+
+    /**
+     * Add a restriction reason for disallowing satellite communication.
+     *
+     * @param subId The subId of the subscription to request for.
+     * @param reason Reason for disallowing satellite communication for carrier.
+     * @param callback Listener for the {@link SatelliteManager.SatelliteError} result of the
+     * operation.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void addSatelliteAttachRestrictionForCarrier(int subId, int reason,
+            in IIntegerConsumer callback);
+
+    /**
+     * Remove a restriction reason for disallowing satellite communication.
+     *
+     * @param subId The subId of the subscription to request for.
+     * @param reason Reason for disallowing satellite communication.
+     * @param callback Listener for the {@link SatelliteManager.SatelliteError} result of the
+     * operation.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void removeSatelliteAttachRestrictionForCarrier(int subId, int reason,
+            in IIntegerConsumer callback);
+
+    /**
+     * Get reasons for disallowing satellite communication, as requested by
+     * {@link #addSatelliteAttachRestrictionForCarrier(int, int)}.
+     *
+     * @param subId The subId of the subscription to request for.
+     *
+     * @return Set of reasons for disallowing satellite communication.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    int[] getSatelliteAttachRestrictionReasonsForCarrier(int subId);
 }
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index f026bea..53606a3 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -22,7 +22,7 @@
 }
 
 java_test_host {
-    name: "ApkVerityTest",
+    name: "FsVerityTest",
     srcs: ["src/**/*.java"],
     libs: [
         "tradefed",
@@ -30,8 +30,10 @@
         "compatibility-host-util",
     ],
     static_libs: [
+        "android.security.flags-aconfig-java-host",
         "block_device_writer_jar",
         "frameworks-base-hostutils",
+        "flag-junit-host",
     ],
     test_suites: [
         "general-tests",
@@ -41,14 +43,6 @@
         "block_device_writer",
     ],
     data: [
-        ":ApkVerityTestCertDer",
-        ":ApkVerityTestApp",
-        ":ApkVerityTestAppFsvSig",
-        ":ApkVerityTestAppDm",
-        ":ApkVerityTestAppDmFsvSig",
-        ":ApkVerityTestAppSplit",
-        ":ApkVerityTestAppSplitFsvSig",
-        ":ApkVerityTestAppSplitDm",
-        ":ApkVerityTestAppSplitDmFsvSig",
+        ":FsVerityTestApp",
     ],
 }
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml
index 4487cef..49cbde0 100644
--- a/tests/ApkVerityTest/AndroidTest.xml
+++ b/tests/ApkVerityTest/AndroidTest.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="APK fs-verity integration/regression test">
+<configuration description="fs-verity end-to-end test">
     <option name="test-suite-tag" value="apct" />
 
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController">
@@ -24,19 +24,9 @@
     <!-- This test requires root to write against block device. -->
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
 
-    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
-        <!-- Disable package verifier prevents it holding the target APK's fd that prevents cache
-             eviction. -->
-        <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
-        <option name="restore-settings" value="true" />
-
-        <!-- Skip in order to prevent reboot that confuses the test flow. -->
-        <option name="force-skip-system-props" value="true" />
-    </target_preparer>
-
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="ApkVerityTestCert.der->/data/local/tmp/ApkVerityTestCert.der" />
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="FsVerityTestApp.apk"/>
+        <option name="cleanup-apks" value="true"/>
     </target_preparer>
 
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
@@ -48,9 +38,7 @@
         <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
     </target_preparer>
 
-    <!-- Skip on HWASan. TODO(b/232288278): Re-enable -->
-    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="jar" value="ApkVerityTest.jar" />
+        <option name="jar" value="FsVerityTest.jar" />
     </test>
 </configuration>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
deleted file mode 100644
index 3f1a4f3..0000000
--- a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  * 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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.apkverity"
-    android:isFeatureSplit="true"
-    split="feature_x">
-    <application>
-        <activity android:name=".feature_x.DummyActivity"/>
-    </application>
-</manifest>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
deleted file mode 100644
index fe91260..0000000
--- a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
+++ /dev/null
@@ -1,22 +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.apkverity.feature_x;
-
-import android.app.Activity;
-
-/** Placeholder class just to generate some dex */
-public class DummyActivity extends Activity {}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
deleted file mode 100644
index a7bd771..0000000
--- a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
+++ /dev/null
@@ -1,22 +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.apkverity;
-
-import android.app.Activity;
-
-/** Placeholder class just to generate some dex */
-public class DummyActivity extends Activity {}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp b/tests/ApkVerityTest/FsVerityTestApp/Android.bp
similarity index 74%
rename from tests/ApkVerityTest/ApkVerityTestApp/Android.bp
rename to tests/ApkVerityTest/FsVerityTestApp/Android.bp
index adf8f9f..43da3ff 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
+++ b/tests/ApkVerityTest/FsVerityTestApp/Android.bp
@@ -22,17 +22,8 @@
 }
 
 android_test_helper_app {
-  name: "ApkVerityTestApp",
-  manifest: "AndroidManifest.xml",
-  srcs: ["src/**/*.java"],
-}
-
-android_test_helper_app {
-  name: "ApkVerityTestAppSplit",
-  manifest: "feature_split/AndroidManifest.xml",
-  srcs: ["src/**/*.java"],
-  aaptflags: [
-      "--custom-package com.android.apkverity.feature_x",
-      "--package-id 0x80",
-  ],
+    name: "FsVerityTestApp",
+    manifest: "AndroidManifest.xml",
+    srcs: ["src/**/*.java"],
+    static_libs: ["compatibility-device-util-axt"],
 }
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml b/tests/ApkVerityTest/FsVerityTestApp/AndroidManifest.xml
similarity index 75%
rename from tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml
rename to tests/ApkVerityTest/FsVerityTestApp/AndroidManifest.xml
index 0b3ff77..42fe49b 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml
+++ b/tests/ApkVerityTest/FsVerityTestApp/AndroidManifest.xml
@@ -16,8 +16,12 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.apkverity">
+    package="com.android.fsverity">
     <application>
         <activity android:name=".DummyActivity"/>
     </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.fsverity"
+                     android:label="Helper app of fs-verity test">
+    </instrumentation>/>
 </manifest>
diff --git a/tests/ApkVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java b/tests/ApkVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
new file mode 100644
index 0000000..2ed4fec
--- /dev/null
+++ b/tests/ApkVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.security.FileIntegrityManager;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Test helper that works with the host-side test to set up a test file, and to verify fs-verity
+ * verification is done expectedly.
+ */
+public class Helper {
+    private static final String TAG = "FsVerityTest";
+
+    private static final String FILENAME = "test.file";
+
+    private static final long BLOCK_SIZE = 4096;
+
+    @Test
+    public void prepareTest() throws Exception {
+        Context context = ApplicationProvider.getApplicationContext();
+        android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+
+        String basename = testArgs.getString("basename");
+        context.deleteFile(basename);
+
+        assertThat(testArgs).isNotNull();
+        int fileSize = Integer.parseInt(testArgs.getString("fileSize"));
+        Log.d(TAG, "Preparing test file with size " + fileSize);
+
+        byte[] bytes = new byte[8192];
+        Arrays.fill(bytes, (byte) '1');
+        try (FileOutputStream os = context.openFileOutput(basename, Context.MODE_PRIVATE)) {
+            for (int i = 0; i < fileSize; i += bytes.length) {
+                if (i + bytes.length > fileSize) {
+                    os.write(bytes, 0, fileSize % bytes.length);
+                } else {
+                    os.write(bytes);
+                }
+            }
+        }
+
+        // Enable fs-verity
+        FileIntegrityManager fim = context.getSystemService(FileIntegrityManager.class);
+        fim.setupFsVerity(context.getFileStreamPath(basename));
+    }
+
+    @Test
+    public void verifyFileRead() throws Exception {
+        Context context = ApplicationProvider.getApplicationContext();
+
+        // Collect indices that the backing blocks are supposed to be corrupted.
+        android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+        assertThat(testArgs).isNotNull();
+        String filePath = testArgs.getString("filePath");
+        String csv = testArgs.getString("brokenBlockIndicesCsv");
+        Log.d(TAG, "brokenBlockIndicesCsv: " + csv);
+        String[] strings = csv.split(",");
+        var corrupted = new ArrayList(strings.length);
+        for (int i = 0; i < strings.length; i++) {
+            corrupted.add(Integer.parseInt(strings[i]));
+        }
+
+        // Expect the read to succeed or fail per the prior.
+        try (var file = new RandomAccessFile(filePath, "r")) {
+            long total_blocks = (file.length() + BLOCK_SIZE - 1) / BLOCK_SIZE;
+            for (int i = 0; i < (int) total_blocks; i++) {
+                file.seek(i * BLOCK_SIZE);
+                if (corrupted.contains(i)) {
+                    Log.d(TAG, "Expecting read at block #" + i + " to fail");
+                    assertThrows(IOException.class, () -> file.read());
+                } else {
+                    assertThat(file.readByte()).isEqualTo('1');
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ApkVerityTest/TEST_MAPPING b/tests/ApkVerityTest/TEST_MAPPING
index 72d9614..39944be 100644
--- a/tests/ApkVerityTest/TEST_MAPPING
+++ b/tests/ApkVerityTest/TEST_MAPPING
@@ -1,11 +1,11 @@
 {
-  "presubmit": [
+  "postsubmit": [
     {
-      "name": "ApkVerityTest"
+      "name": "FsVerityTest"
     },
     // nextgen test only runs during postsubmit.
     {
-      "name": "ApkVerityTest",
+      "name": "FsVerityTest",
       "keywords": ["nextgen"]
     }
   ]
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
deleted file mode 100644
index 482f633..0000000
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ /dev/null
@@ -1,579 +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.apkverity;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.RootPermissionTest;
-
-import com.android.blockdevicewriter.BlockDeviceWriter;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This test makes sure app installs with fs-verity signature, and on-access verification works.
- *
- * <p>When an app is installed, all or none of the files should have their corresponding .fsv_sig
- * signature file. Otherwise, install will fail.
- *
- * <p>Once installed, file protected by fs-verity is verified by kernel every time a block is loaded
- * from disk to memory. The file is immutable by design, enforced by filesystem.
- *
- * <p>In order to make sure a block of the file is readable only if the underlying block on disk
- * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
- * address against the block device.
- *
- * <p>Requirements to run this test:
- * <ul>
- *   <li>Device is rootable</li>
- *   <li>The filesystem supports fs-verity</li>
- *   <li>The feature flag is enabled</li>
- * </ul>
- */
-@RootPermissionTest
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class ApkVerityTest extends BaseHostJUnit4Test {
-    private static final String TARGET_PACKAGE = "com.android.apkverity";
-
-    private static final String BASE_APK = "ApkVerityTestApp.apk";
-    private static final String BASE_APK_DM = "ApkVerityTestApp.dm";
-    private static final String SPLIT_APK = "ApkVerityTestAppSplit.apk";
-    private static final String SPLIT_APK_DM = "ApkVerityTestAppSplit.dm";
-
-    private static final String INSTALLED_BASE_APK = "base.apk";
-    private static final String INSTALLED_BASE_DM = "base.dm";
-    private static final String INSTALLED_SPLIT_APK = "split_feature_x.apk";
-    private static final String INSTALLED_SPLIT_DM = "split_feature_x.dm";
-    private static final String INSTALLED_BASE_APK_FSV_SIG = "base.apk.fsv_sig";
-    private static final String INSTALLED_BASE_DM_FSV_SIG = "base.dm.fsv_sig";
-    private static final String INSTALLED_SPLIT_APK_FSV_SIG = "split_feature_x.apk.fsv_sig";
-    private static final String INSTALLED_SPLIT_DM_FSV_SIG = "split_feature_x.dm.fsv_sig";
-
-    private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
-    private static final String CERT_PATH = "/data/local/tmp/ApkVerityTestCert.der";
-
-    /** Only 4K page is supported by fs-verity currently. */
-    private static final int FSVERITY_PAGE_SIZE = 4096;
-
-    private ITestDevice mDevice;
-    private boolean mDmRequireFsVerity;
-
-    @Before
-    public void setUp() throws DeviceNotAvailableException {
-        mDevice = getDevice();
-        mDmRequireFsVerity = "true".equals(
-                mDevice.getProperty("pm.dexopt.dm.require_fsverity"));
-
-        expectRemoteCommandToSucceed("cmd file_integrity append-cert " + CERT_PATH);
-        uninstallPackage(TARGET_PACKAGE);
-    }
-
-    @After
-    public void tearDown() throws DeviceNotAvailableException {
-        expectRemoteCommandToSucceed("cmd file_integrity remove-last-cert");
-        uninstallPackage(TARGET_PACKAGE);
-    }
-
-    @Test
-    public void testFsverityKernelSupports() throws DeviceNotAvailableException {
-        ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
-        expectRemoteCommandToSucceed("test -f /sys/fs/" + mountPoint.type + "/features/verity");
-    }
-
-    @Test
-    public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG);
-        verifyInstalledFilesHaveFsverity(INSTALLED_BASE_APK);
-    }
-
-    @Test
-    public void testInstallBaseWithWrongSignature()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFile(BASE_APK)
-                .addFile(SPLIT_APK_DM + ".fsv_sig",
-                        BASE_APK + ".fsv_sig")
-                .runExpectingFailure();
-    }
-
-    @Test
-    public void testInstallBaseWithSplit()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .addFileAndSignature(SPLIT_APK)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG,
-                INSTALLED_SPLIT_APK,
-                INSTALLED_SPLIT_APK_FSV_SIG);
-        verifyInstalledFilesHaveFsverity(
-                INSTALLED_BASE_APK,
-                INSTALLED_SPLIT_APK);
-    }
-
-    @Test
-    public void testInstallBaseWithDm() throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .addFileAndSignature(BASE_APK_DM)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG,
-                INSTALLED_BASE_DM,
-                INSTALLED_BASE_DM_FSV_SIG);
-        verifyInstalledFilesHaveFsverity(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_DM);
-    }
-
-    @Test
-    public void testInstallEverything() throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .addFileAndSignature(BASE_APK_DM)
-                .addFileAndSignature(SPLIT_APK)
-                .addFileAndSignature(SPLIT_APK_DM)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG,
-                INSTALLED_BASE_DM,
-                INSTALLED_BASE_DM_FSV_SIG,
-                INSTALLED_SPLIT_APK,
-                INSTALLED_SPLIT_APK_FSV_SIG,
-                INSTALLED_SPLIT_DM,
-                INSTALLED_SPLIT_DM_FSV_SIG);
-        verifyInstalledFilesHaveFsverity(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_DM,
-                INSTALLED_SPLIT_APK,
-                INSTALLED_SPLIT_DM);
-    }
-
-    @Test
-    public void testInstallSplitOnly()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG);
-
-        new InstallMultiple()
-                .inheritFrom(TARGET_PACKAGE)
-                .addFileAndSignature(SPLIT_APK)
-                .run();
-
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG,
-                INSTALLED_SPLIT_APK,
-                INSTALLED_SPLIT_APK_FSV_SIG);
-        verifyInstalledFilesHaveFsverity(
-                INSTALLED_BASE_APK,
-                INSTALLED_SPLIT_APK);
-    }
-
-    @Test
-    public void testInstallSplitOnlyMissingSignature()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG);
-
-        new InstallMultiple()
-                .inheritFrom(TARGET_PACKAGE)
-                .addFile(SPLIT_APK)
-                .runExpectingFailure();
-    }
-
-    @Test
-    public void testInstallSplitOnlyWithoutBaseSignature()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFile(BASE_APK)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-        verifyInstalledFiles(INSTALLED_BASE_APK);
-
-        new InstallMultiple()
-                .inheritFrom(TARGET_PACKAGE)
-                .addFileAndSignature(SPLIT_APK)
-                .run();
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_SPLIT_APK,
-                INSTALLED_SPLIT_APK_FSV_SIG);
-    }
-
-    @Test
-    public void testInstallOnlyDmHasFsvSig()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFile(BASE_APK)
-                .addFileAndSignature(BASE_APK_DM)
-                .addFile(SPLIT_APK)
-                .addFileAndSignature(SPLIT_APK_DM)
-                .run();
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_DM,
-                INSTALLED_BASE_DM_FSV_SIG,
-                INSTALLED_SPLIT_APK,
-                INSTALLED_SPLIT_DM,
-                INSTALLED_SPLIT_DM_FSV_SIG);
-        verifyInstalledFilesHaveFsverity(
-                INSTALLED_BASE_DM,
-                INSTALLED_SPLIT_DM);
-    }
-
-    @Test
-    public void testInstallDmWithoutFsvSig_Base()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        InstallMultiple installer = new InstallMultiple()
-                .addFile(BASE_APK)
-                .addFile(BASE_APK_DM)
-                .addFile(SPLIT_APK)
-                .addFileAndSignature(SPLIT_APK_DM);
-        if (mDmRequireFsVerity) {
-            installer.runExpectingFailure();
-        } else {
-            installer.run();
-            verifyInstalledFiles(
-                    INSTALLED_BASE_APK,
-                    INSTALLED_BASE_DM,
-                    INSTALLED_SPLIT_APK,
-                    INSTALLED_SPLIT_DM,
-                    INSTALLED_SPLIT_DM_FSV_SIG);
-            verifyInstalledFilesHaveFsverity(INSTALLED_SPLIT_DM);
-        }
-    }
-
-    @Test
-    public void testInstallDmWithoutFsvSig_Split()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        InstallMultiple installer = new InstallMultiple()
-                .addFile(BASE_APK)
-                .addFileAndSignature(BASE_APK_DM)
-                .addFile(SPLIT_APK)
-                .addFile(SPLIT_APK_DM);
-        if (mDmRequireFsVerity) {
-            installer.runExpectingFailure();
-        } else {
-            installer.run();
-            verifyInstalledFiles(
-                    INSTALLED_BASE_APK,
-                    INSTALLED_BASE_DM,
-                    INSTALLED_BASE_DM_FSV_SIG,
-                    INSTALLED_SPLIT_APK,
-                    INSTALLED_SPLIT_DM);
-            verifyInstalledFilesHaveFsverity(INSTALLED_BASE_DM);
-        }
-    }
-
-    @Test
-    public void testInstallSomeApkIsMissingFsvSig_Base()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .addFileAndSignature(BASE_APK_DM)
-                .addFile(SPLIT_APK)
-                .addFileAndSignature(SPLIT_APK_DM)
-                .runExpectingFailure();
-    }
-
-    @Test
-    public void testInstallSomeApkIsMissingFsvSig_Split()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFile(BASE_APK)
-                .addFileAndSignature(BASE_APK_DM)
-                .addFileAndSignature(SPLIT_APK)
-                .addFileAndSignature(SPLIT_APK_DM)
-                .runExpectingFailure();
-    }
-
-    @Test
-    public void testInstallBaseWithFsvSigThenSplitWithout()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFileAndSignature(BASE_APK)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-        verifyInstalledFiles(
-                INSTALLED_BASE_APK,
-                INSTALLED_BASE_APK_FSV_SIG);
-
-        new InstallMultiple()
-                .addFile(SPLIT_APK)
-                .runExpectingFailure();
-    }
-
-    @Test
-    public void testInstallBaseWithoutFsvSigThenSplitWith()
-            throws DeviceNotAvailableException, FileNotFoundException {
-        new InstallMultiple()
-                .addFile(BASE_APK)
-                .run();
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-        verifyInstalledFiles(INSTALLED_BASE_APK);
-
-        new InstallMultiple()
-                .addFileAndSignature(SPLIT_APK)
-                .runExpectingFailure();
-    }
-
-    @Test
-    public void testFsverityFileIsImmutableAndReadable() throws DeviceNotAvailableException {
-        new InstallMultiple().addFileAndSignature(BASE_APK).run();
-        String apkPath = getApkPath(TARGET_PACKAGE);
-
-        assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-        expectRemoteCommandToFail("echo -n '' >> " + apkPath);
-        expectRemoteCommandToSucceed("cat " + apkPath + " > /dev/null");
-    }
-
-    @Test
-    public void testFsverityFailToReadModifiedBlockAtFront() throws DeviceNotAvailableException {
-        new InstallMultiple().addFileAndSignature(BASE_APK).run();
-        String apkPath = getApkPath(TARGET_PACKAGE);
-
-        long apkSize = getFileSizeInBytes(apkPath);
-        long offsetFirstByte = 0;
-
-        // The first two pages should be both readable at first.
-        assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
-        if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
-            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
-                    offsetFirstByte + FSVERITY_PAGE_SIZE));
-        }
-
-        // Damage the file directly against the block device.
-        damageFileAgainstBlockDevice(apkPath, offsetFirstByte);
-
-        // Expect actual read from disk to fail but only at damaged page.
-        expectReadFromBlockDeviceToFail(apkPath, offsetFirstByte);
-        if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
-            long lastByteOfTheSamePage =
-                    offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
-            assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage));
-            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1));
-        }
-    }
-
-    @Test
-    public void testFsverityFailToReadModifiedBlockAtBack() throws DeviceNotAvailableException {
-        new InstallMultiple().addFileAndSignature(BASE_APK).run();
-        String apkPath = getApkPath(TARGET_PACKAGE);
-
-        long apkSize = getFileSizeInBytes(apkPath);
-        long offsetOfLastByte = apkSize - 1;
-
-        // The first two pages should be both readable at first.
-        assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
-        if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
-            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
-                    offsetOfLastByte - FSVERITY_PAGE_SIZE));
-        }
-
-        // Damage the file directly against the block device.
-        damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);
-
-        // Expect actual read from disk to fail but only at damaged page.
-        expectReadFromBlockDeviceToFail(apkPath, offsetOfLastByte);
-        if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
-            long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
-            assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage));
-            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1));
-        }
-    }
-
-    private void verifyInstalledFilesHaveFsverity(String... filenames)
-            throws DeviceNotAvailableException {
-        // Verify that all files are protected by fs-verity
-        String apkPath = getApkPath(TARGET_PACKAGE);
-        String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
-        long kTargetOffset = 0;
-        for (String basename : filenames) {
-            String path = appDir + "/" + basename;
-            damageFileAgainstBlockDevice(path, kTargetOffset);
-
-            expectReadFromBlockDeviceToFail(path, kTargetOffset);
-        }
-    }
-
-    private void expectReadFromBlockDeviceToFail(String readPath, long offset)
-            throws DeviceNotAvailableException {
-        // Retry is sometimes needed to pass the test. Package manager may have FD leaks
-        // (see b/122744005 as example) that prevents the file in question to be evicted
-        // from filesystem cache. Forcing GC workarounds the problem.
-        int retry = 5;
-        for (; retry > 0; retry--) {
-            BlockDeviceWriter.dropCaches(mDevice);
-            if (!BlockDeviceWriter.canReadByte(mDevice, readPath, offset)) {
-                break;
-            }
-            try {
-                String openFiles = expectRemoteCommandToSucceed("lsof " + readPath);
-                CLog.d("lsof: " + openFiles);
-                Thread.sleep(1000);
-                forceGCOnOpenFilesProcess(getOpenFilesPIDs(openFiles));
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                return;
-            }
-        }
-        assertTrue("Read from " + readPath + " should fail", retry > 0);
-    }
-
-    /**
-     * This is a helper method that parses the lsof output to get PIDs of process holding FD.
-     * Here is an example output of lsof. This method extracts the second columns(PID).
-     *
-     * Example lsof output:
-     *  COMMAND      PID     USER    FD     TYPE   DEVICE   SIZE/OFF  NODE   NAME
-     * .example.app  1063    u0_a38  mem    REG    253,6    8599      12826  example.apk
-     * .example.app  1063    u0_a38  99r    REG    253,6    8599      12826  example.apk
-     */
-    private Set<String> getOpenFilesPIDs(String lsof) {
-        Set<String> openFilesPIDs = new HashSet<>();
-        String[] lines = lsof.split("\n");
-        for (int i = 1; i < lines.length; i++) {
-            openFilesPIDs.add(lines[i].split("\\s+")[1]);
-        }
-        return openFilesPIDs;
-    }
-
-    /**
-     * This is a helper method that forces GC on processes given their PIDs.
-     * That is to execute shell command "kill -10" on PIDs.
-     */
-    private void forceGCOnOpenFilesProcess(Set<String> openFilesPIDs)
-            throws DeviceNotAvailableException {
-        for (String openFilePID : openFilesPIDs) {
-            mDevice.executeShellV2Command("kill -10 " + openFilePID);
-        }
-    }
-
-    private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException {
-        String apkPath = getApkPath(TARGET_PACKAGE);
-        String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
-        // Exclude directories since we only care about files.
-        HashSet<String> actualFiles = new HashSet<>(Arrays.asList(
-                expectRemoteCommandToSucceed("ls -p " + appDir + " | grep -v '/'").split("\n")));
-
-        HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames));
-        assertEquals(expectedFiles, actualFiles);
-    }
-
-    private void damageFileAgainstBlockDevice(String path, long offsetOfTargetingByte)
-            throws DeviceNotAvailableException {
-        assertTrue(path.startsWith("/data/"));
-        ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
-        ArrayList<String> args = new ArrayList<>();
-        args.add(DAMAGING_EXECUTABLE);
-        if ("f2fs".equals(mountPoint.type)) {
-            args.add("--use-f2fs-pinning");
-        }
-        args.add(mountPoint.filesystem);
-        args.add(path);
-        args.add(Long.toString(offsetOfTargetingByte));
-        expectRemoteCommandToSucceed(String.join(" ", args));
-    }
-
-    private String getApkPath(String packageName) throws DeviceNotAvailableException {
-        String line = expectRemoteCommandToSucceed("pm path " + packageName + " | grep base.apk");
-        int index = line.trim().indexOf(":");
-        assertTrue(index >= 0);
-        return line.substring(index + 1);
-    }
-
-    private long getFileSizeInBytes(String packageName) throws DeviceNotAvailableException {
-        return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
-    }
-
-    private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
-        CommandResult result = mDevice.executeShellV2Command(cmd);
-        assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
-                result.getStatus());
-        return result.getStdout();
-    }
-
-    private void expectRemoteCommandToFail(String cmd) throws DeviceNotAvailableException {
-        CommandResult result = mDevice.executeShellV2Command(cmd);
-        assertTrue("Unexpected success from `" + cmd + "`: " + result.getStderr(),
-                result.getStatus() != CommandStatus.SUCCESS);
-    }
-
-    private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
-        InstallMultiple() {
-            super(getDevice(), getBuild());
-        }
-
-        InstallMultiple addFileAndSignature(String filename) {
-            try {
-                addFile(filename);
-                addFile(filename + ".fsv_sig");
-            } catch (FileNotFoundException e) {
-                fail("Missing test file: " + e);
-            }
-            return this;
-        }
-    }
-}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java b/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
deleted file mode 100644
index 02e73d1..0000000
--- a/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
+++ /dev/null
@@ -1,140 +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.apkverity;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Base class for invoking the install-multiple command via ADB. Subclass this for less typing:
- *
- * <code> private class InstallMultiple extends BaseInstallMultiple&lt;InstallMultiple&gt; { public
- * InstallMultiple() { super(getDevice(), null); } } </code>
- */
-/*package*/ class BaseInstallMultiple<T extends BaseInstallMultiple<?>> {
-
-    private final ITestDevice mDevice;
-    private final IBuildInfo mBuild;
-
-    private final List<String> mArgs = new ArrayList<>();
-    private final Map<File, String> mFileToRemoteMap = new HashMap<>();
-
-    /*package*/ BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo) {
-        mDevice = device;
-        mBuild = buildInfo;
-        addArg("-g");
-    }
-
-    T addArg(String arg) {
-        mArgs.add(arg);
-        return (T) this;
-    }
-
-    T addFile(String filename) throws FileNotFoundException {
-        return addFile(filename, filename);
-    }
-
-    T addFile(String filename, String remoteName) throws FileNotFoundException {
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
-        mFileToRemoteMap.put(buildHelper.getTestFile(filename), remoteName);
-        return (T) this;
-    }
-
-    T inheritFrom(String packageName) {
-        addArg("-r");
-        addArg("-p " + packageName);
-        return (T) this;
-    }
-
-    void run() throws DeviceNotAvailableException {
-        run(true);
-    }
-
-    void runExpectingFailure() throws DeviceNotAvailableException {
-        run(false);
-    }
-
-    private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
-        final ITestDevice device = mDevice;
-
-        // Create an install session
-        final StringBuilder cmd = new StringBuilder();
-        cmd.append("pm install-create");
-        for (String arg : mArgs) {
-            cmd.append(' ').append(arg);
-        }
-
-        String result = device.executeShellCommand(cmd.toString());
-        TestCase.assertTrue(result, result.startsWith("Success"));
-
-        final int start = result.lastIndexOf("[");
-        final int end = result.lastIndexOf("]");
-        int sessionId = -1;
-        try {
-            if (start != -1 && end != -1 && start < end) {
-                sessionId = Integer.parseInt(result.substring(start + 1, end));
-            }
-        } catch (NumberFormatException e) {
-            throw new IllegalStateException("Failed to parse install session: " + result);
-        }
-        if (sessionId == -1) {
-            throw new IllegalStateException("Failed to create install session: " + result);
-        }
-
-        // Push our files into session. Ideally we'd use stdin streaming,
-        // but ddmlib doesn't support it yet.
-        for (final Map.Entry<File, String> entry : mFileToRemoteMap.entrySet()) {
-            final File file = entry.getKey();
-            final String remoteName  = entry.getValue();
-            final String remotePath = "/data/local/tmp/" + file.getName();
-            if (!device.pushFile(file, remotePath)) {
-                throw new IllegalStateException("Failed to push " + file);
-            }
-
-            cmd.setLength(0);
-            cmd.append("pm install-write");
-            cmd.append(' ').append(sessionId);
-            cmd.append(' ').append(remoteName);
-            cmd.append(' ').append(remotePath);
-
-            result = device.executeShellCommand(cmd.toString());
-            TestCase.assertTrue(result, result.startsWith("Success"));
-        }
-
-        // Everything staged; let's pull trigger
-        cmd.setLength(0);
-        cmd.append("pm install-commit");
-        cmd.append(' ').append(sessionId);
-
-        result = device.executeShellCommand(cmd.toString());
-        if (expectingSuccess) {
-            TestCase.assertTrue(result, result.contains("Success"));
-        } else {
-            TestCase.assertFalse(result, result.contains("Success"));
-        }
-    }
-}
diff --git a/tests/ApkVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/ApkVerityTest/src/com/android/fsverity/FsVerityHostTest.java
new file mode 100644
index 0000000..be479f2
--- /dev/null
+++ b/tests/ApkVerityTest/src/com/android/fsverity/FsVerityHostTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 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.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.annotations.RootPermissionTest;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.host.HostFlagsValueProvider;
+import android.security.Flags;
+
+import com.android.blockdevicewriter.BlockDeviceWriter;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This test verifies fs-verity works end-to-end. There is a corresponding helper app.
+ *
+ * <p>The helper app uses a FileIntegrityManager API to enable fs-verity to a file. The host test
+ * here * tampers with the file's backing storage, then tells the helper app to read and expect
+ * success/failure on read.
+ *
+ * <p>In order to make sure a block of the file is readable only if the underlying block on disk
+ * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
+ * address against the block device.
+ */
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+@RequiresFlagsEnabled(Flags.FLAG_FSVERITY_API)
+public class FsVerityHostTest extends BaseHostJUnit4Test {
+    private static final String TARGET_PACKAGE = "com.android.fsverity";
+
+    private static final String BASENAME = "test.file";
+    private static final String TARGET_PATH = "/data/data/" + TARGET_PACKAGE + "/files/" + BASENAME;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
+
+    @Test
+    public void testFsVeritySmallFile() throws Exception {
+        prepareTest(10000);
+
+        ITestDevice device = getDevice();
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 0);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 8192);
+        BlockDeviceWriter.dropCaches(device);
+
+        verifyRead(TARGET_PATH, "0,2");
+    }
+
+    @Test
+    public void testFsVerityLargerFileWithOneMoreMerkleTreeLevel() throws Exception {
+        prepareTest(128 * 4096 + 1);
+
+        ITestDevice device = getDevice();
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 4096);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 100 * 4096);
+        BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 128 * 4096 + 1);
+        BlockDeviceWriter.dropCaches(device);
+
+        verifyRead(TARGET_PATH, "1,100,128");
+    }
+
+    private void prepareTest(int fileSize) throws Exception {
+        DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE);
+        options.setTestClassName(TARGET_PACKAGE + ".Helper");
+        options.setTestMethodName("prepareTest");
+        options.addInstrumentationArg("basename", BASENAME);
+        options.addInstrumentationArg("fileSize", String.valueOf(fileSize));
+        assertThat(runDeviceTests(options)).isTrue();
+    }
+
+    private void verifyRead(String path, String indicesCsv) throws Exception {
+        DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE);
+        options.setTestClassName(TARGET_PACKAGE + ".Helper");
+        options.setTestMethodName("verifyFileRead");
+        options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv);
+        options.addInstrumentationArg("filePath", TARGET_PATH);
+        assertThat(runDeviceTests(options)).isTrue();
+    }
+}
diff --git a/tests/ApkVerityTest/testdata/Android.bp b/tests/ApkVerityTest/testdata/Android.bp
index ccfc4c9..2d578d3 100644
--- a/tests/ApkVerityTest/testdata/Android.bp
+++ b/tests/ApkVerityTest/testdata/Android.bp
@@ -37,51 +37,3 @@
     name: "ApkVerityTestCertDer",
     srcs: ["ApkVerityTestCert.der"],
 }
-
-filegroup {
-    name: "ApkVerityTestAppDm",
-    srcs: ["ApkVerityTestApp.dm"],
-}
-
-filegroup {
-    name: "ApkVerityTestAppSplitDm",
-    srcs: ["ApkVerityTestAppSplit.dm"],
-}
-
-genrule_defaults {
-    name: "apk_verity_sig_gen_default",
-    tools: ["fsverity"],
-    tool_files: [":ApkVerityTestKeyPem", ":ApkVerityTestCertPem"],
-    cmd: "$(location fsverity) sign $(in) $(out) " +
-        "--key=$(location :ApkVerityTestKeyPem) " +
-        "--cert=$(location :ApkVerityTestCertPem) " +
-        "> /dev/null",
-}
-
-genrule {
-    name: "ApkVerityTestAppFsvSig",
-    defaults: ["apk_verity_sig_gen_default"],
-    srcs: [":ApkVerityTestApp"],
-    out: ["ApkVerityTestApp.apk.fsv_sig"],
-}
-
-genrule {
-    name: "ApkVerityTestAppDmFsvSig",
-    defaults: ["apk_verity_sig_gen_default"],
-    srcs: [":ApkVerityTestAppDm"],
-    out: ["ApkVerityTestApp.dm.fsv_sig"],
-}
-
-genrule {
-    name: "ApkVerityTestAppSplitFsvSig",
-    defaults: ["apk_verity_sig_gen_default"],
-    srcs: [":ApkVerityTestAppSplit"],
-    out: ["ApkVerityTestAppSplit.apk.fsv_sig"],
-}
-
-genrule {
-    name: "ApkVerityTestAppSplitDmFsvSig",
-    defaults: ["apk_verity_sig_gen_default"],
-    srcs: [":ApkVerityTestAppSplitDm"],
-    out: ["ApkVerityTestAppSplit.dm.fsv_sig"],
-}
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm b/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
deleted file mode 100644
index e53a861..0000000
--- a/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
+++ /dev/null
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm b/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
deleted file mode 100644
index 75396f1..0000000
--- a/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
+++ /dev/null
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/README.md b/tests/ApkVerityTest/testdata/README.md
deleted file mode 100644
index 163cb18..0000000
--- a/tests/ApkVerityTest/testdata/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-This test only runs on rooted / debuggable device.
-
-The test tries to install subsets of base.{apk,dm}, split.{apk,dm} and their
-corresponding .fsv_sig files (generated by build rule). If installed, the
-tests also tries to tamper with the file at absolute disk offset to verify
-if fs-verity is effective.
-
-How to generate dex metadata (.dm)
-==================================
-
-  adb shell profman --generate-test-profile=/data/local/tmp/primary.prof
-  adb pull /data/local/tmp/primary.prof
-  zip foo.dm primary.prof
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index 44a8245..8780385 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -61,7 +61,9 @@
         <option name="shell-timeout" value="6600s"/>
         <option name="test-timeout" value="6600s"/>
         <option name="hidden-api-checks" value="false"/>
+        <!-- TODO(b/288396763): re-enable when PerfettoListener is fixed
         <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        -->
         <!-- PerfettoListener related arguments -->
         <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
         <option name="instrumentation-arg"