Merge "[20/n] Encapsulate Fake Focus policy" into main
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4da7e53..d039b04 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -10890,12 +10890,8 @@
      * Whether we should send fake focus when the activity is resumed. This is done because some
      * game engines wait to get focus before drawing the content of the app.
      */
-    // TODO(b/263593361): Explore enabling compat fake focus for freeform.
-    // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
-    // covered with bubbles.
     boolean shouldSendCompatFakeFocus() {
-        return mLetterboxUiController.shouldSendFakeFocus() && inMultiWindowMode()
-                && !inPinnedWindowingMode() && !inFreeformWindowingMode();
+        return mAppCompatController.getAppCompatFocusOverrides().shouldSendFakeFocus();
     }
 
     boolean canCaptureSnapshot() {
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index f9e2507..998d65d 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -94,4 +94,9 @@
         }
         return null;
     }
+
+    @NonNull
+    AppCompatFocusOverrides getAppCompatFocusOverrides() {
+        return mAppCompatOverrides.getAppCompatFocusOverrides();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java b/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java
new file mode 100644
index 0000000..ab4bb14
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+
+import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
+
+import android.annotation.NonNull;
+
+import com.android.server.wm.utils.OptPropFactory;
+
+/**
+ * Encapsulates app compat focus policy.
+ */
+class AppCompatFocusOverrides {
+
+    @NonNull
+    final ActivityRecord mActivityRecord;
+    @NonNull
+    private final OptPropFactory.OptProp mFakeFocusOptProp;
+
+    AppCompatFocusOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatConfiguration appCompatConfiguration,
+            @NonNull OptPropFactory optPropBuilder) {
+        mActivityRecord = activityRecord;
+        mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
+                appCompatConfiguration::isCompatFakeFocusEnabled);
+    }
+
+    /**
+     * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
+     * because some game engines wait to get focus before drawing the content of the app which isn't
+     * guaranteed by default in multi-window modes.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Flag gating the treatment is enabled
+     *     <li>Component property is NOT set to false
+     *     <li>Component property is set to true or per-app override is enabled
+     * </ul>
+     */
+    boolean shouldSendFakeFocus() {
+        // TODO(b/263593361): Explore enabling compat fake focus for freeform.
+        // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
+        // covered with bubbles.
+        return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty(
+                isChangeEnabled(mActivityRecord, OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS))
+                && mActivityRecord.inMultiWindowMode() && !mActivityRecord.inPinnedWindowingMode()
+                && !mActivityRecord.inFreeformWindowingMode();
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index b611ba9..cde48d6 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -18,17 +18,12 @@
 
 import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
 import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
-
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.NonNull;
 
@@ -39,17 +34,10 @@
  */
 public class AppCompatOverrides {
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatOverrides" : TAG_ATM;
-
-    @NonNull
-    private final AppCompatConfiguration mAppCompatConfiguration;
-
     @NonNull
     private final ActivityRecord mActivityRecord;
 
     @NonNull
-    private final OptPropFactory.OptProp mFakeFocusOptProp;
-    @NonNull
     private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp;
     @NonNull
     private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp;
@@ -61,26 +49,25 @@
     private final AppCompatCameraOverrides mAppCompatCameraOverrides;
     @NonNull
     private final AppCompatAspectRatioOverrides mAppCompatAspectRatioOverrides;
+    @NonNull
+    private final AppCompatFocusOverrides mAppCompatFocusOverrides;
 
     AppCompatOverrides(@NonNull ActivityRecord activityRecord,
             @NonNull AppCompatConfiguration appCompatConfiguration,
             @NonNull OptPropFactory optPropBuilder) {
-        mAppCompatConfiguration = appCompatConfiguration;
         mActivityRecord = activityRecord;
 
         mAppCompatCameraOverrides = new AppCompatCameraOverrides(mActivityRecord,
-                mAppCompatConfiguration, optPropBuilder);
+                appCompatConfiguration, optPropBuilder);
         mAppCompatOrientationOverrides = new AppCompatOrientationOverrides(mActivityRecord,
-                mAppCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides);
+                appCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides);
         // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with reachability.
         mAppCompatAspectRatioOverrides = new AppCompatAspectRatioOverrides(activityRecord,
-                mAppCompatConfiguration, optPropBuilder,
+                appCompatConfiguration, optPropBuilder,
                 activityRecord.mLetterboxUiController::isDisplayFullScreenAndInPosture,
                 activityRecord.mLetterboxUiController::getHorizontalPositionMultiplier);
-
-        mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
-                mAppCompatConfiguration::isCompatFakeFocusEnabled);
-
+        mAppCompatFocusOverrides = new AppCompatFocusOverrides(mActivityRecord,
+                appCompatConfiguration, optPropBuilder);
 
         mAllowOrientationOverrideOptProp = optPropBuilder.create(
                 PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
@@ -114,21 +101,9 @@
         return mAppCompatAspectRatioOverrides;
     }
 
-    /**
-     * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
-     * because some game engines wait to get focus before drawing the content of the app which isn't
-     * guaranteed by default in multi-window modes.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the treatment is enabled
-     *     <li>Component property is NOT set to false
-     *     <li>Component property is set to true or per-app override is enabled
-     * </ul>
-     */
-    boolean shouldSendFakeFocus() {
-        return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS));
+    @NonNull
+    AppCompatFocusOverrides getAppCompatFocusOverrides() {
+        return mAppCompatFocusOverrides;
     }
 
     boolean isAllowOrientationOverrideOptOut() {
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 1b30a20..fd816067 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -73,4 +73,13 @@
     static boolean isInVrUiMode(Configuration config) {
         return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET;
     }
+
+    /**
+     * @param activityRecord The {@link ActivityRecord} for the app package.
+     * @param overrideChangeId The per-app override identifier.
+     * @return {@code true} if the per-app override is enable for the given activity.
+     */
+    static boolean isChangeEnabled(@NonNull ActivityRecord activityRecord, long overrideChangeId) {
+        return activityRecord.info.isChangeEnabled(overrideChangeId);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index be8e806..73f3655 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -117,22 +117,6 @@
     }
 
     /**
-     * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
-     * because some game engines wait to get focus before drawing the content of the app which isn't
-     * guaranteed by default in multi-window modes.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the treatment is enabled
-     *     <li>Component property is NOT set to false
-     *     <li>Component property is set to true or per-app override is enabled
-     * </ul>
-     */
-    boolean shouldSendFakeFocus() {
-        return getAppCompatOverrides().shouldSendFakeFocus();
-    }
-
-    /**
      * Whether we should apply the force resize per-app override. When this override is applied it
      * forces the packages it is applied to to be resizable. It won't change whether the app can be
      * put into multi-windowing mode, but allow the app to resize without going into size-compat
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 867f01f..11f7560 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -190,6 +193,27 @@
         doReturn(embedded).when(mActivityStack.top()).isEmbedded();
     }
 
+    void setTopActivityInMultiWindowMode(boolean multiWindowMode) {
+        doReturn(multiWindowMode).when(mActivityStack.top()).inMultiWindowMode();
+        if (multiWindowMode) {
+            doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivityStack.top()).getWindowingMode();
+        }
+    }
+
+    void setTopActivityInPinnedWindowingMode(boolean pinnedWindowingMode) {
+        doReturn(pinnedWindowingMode).when(mActivityStack.top()).inPinnedWindowingMode();
+        if (pinnedWindowingMode) {
+            doReturn(WINDOWING_MODE_PINNED).when(mActivityStack.top()).getWindowingMode();
+        }
+    }
+
+    void setTopActivityInFreeformWindowingMode(boolean freeformWindowingMode) {
+        doReturn(freeformWindowingMode).when(mActivityStack.top()).inFreeformWindowingMode();
+        if (freeformWindowingMode) {
+            doReturn(WINDOWING_MODE_FREEFORM).when(mActivityStack.top()).getWindowingMode();
+        }
+    }
+
     void destroyTopActivity() {
         mActivityStack.top().removeImmediately();
     }
@@ -401,9 +425,11 @@
     private void pushActivity(@NonNull ActivityRecord activity) {
         mActivityStack.push(activity);
         spyOn(activity);
+        // TODO (b/351763164): Use these spyOn calls only when necessary.
         spyOn(activity.mAppCompatController.getTransparentPolicy());
         spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
         spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+        spyOn(activity.mAppCompatController.getAppCompatFocusOverrides());
         spyOn(activity.mLetterboxUiController);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index 0a1b16b..00a8771 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -66,4 +66,8 @@
         doReturn(enabled).when(mAppCompatConfiguration)
                 .isCameraCompatSplitScreenAspectRatioEnabled();
     }
+
+    void enableCompatFakeFocus(boolean enabled) {
+        doReturn(enabled).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
new file mode 100644
index 0000000..27c5e4e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.testng.Assert;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatFocusOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatFocusOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatFocusOverridesTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_inMultiWindow_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_noMultiWindowMode_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ false);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_pinnedWindowMode_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInPinnedWindowingMode(/* multiWindowMode */ true);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_freeformMode_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInFreeformWindowingMode(/* freeformWindowingMode */ true);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_propertyDisabledAndOverrideOn_fakeFocusDisabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_propertyEnabled_noOverride_fakeFocusEnabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_propertyDisabled_fakeFocusDisabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_propertyEnabled_fakeFocusEnabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<FocusOverridesRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final FocusOverridesRobotTest robot = new FocusOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class FocusOverridesRobotTest extends AppCompatRobotBase {
+
+        FocusOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void checkShouldSendFakeFocusOnTopActivity(boolean expected) {
+            Assert.assertEquals(activity().top().mAppCompatController.getAppCompatFocusOverrides()
+                    .shouldSendFakeFocus(), expected);
+        }
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java
new file mode 100644
index 0000000..439c633
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.MediumTest;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for App Compat specific code about sizes.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:AppCompatSizeCompatTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatSizeCompatTests extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledUnsetProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ true);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledTrueProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ true);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledFalseProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusDisabledUnsetProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusDisabledTrueProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ true);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusDisabledFalseProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledFeatureDisabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ false);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<SizeCompatRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final SizeCompatRobotTest robot = new SizeCompatRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class SizeCompatRobotTest extends AppCompatRobotBase {
+
+        SizeCompatRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void checkShouldSendCompatFakeFocus(boolean expected) {
+            Assert.assertEquals(expected, activity().top().shouldSendCompatFakeFocus());
+        }
+
+        void putTopActivityInMultiWindowMode() {
+            applyOnActivity((a) -> {
+                a.setTopActivityInMultiWindowMode(true);
+                a.setTopActivityInFreeformWindowingMode(false);
+                a.setTopActivityInPinnedWindowingMode(false);
+            });
+        }
+
+        void putTopActivityInPinnedWindowingMode() {
+            applyOnActivity((a) -> {
+                a.setTopActivityInMultiWindowMode(false);
+                a.setTopActivityInPinnedWindowingMode(true);
+                a.setTopActivityInFreeformWindowingMode(false);
+            });
+        }
+
+        void putTopActivityInFreeformWindowingMode() {
+            applyOnActivity((a) -> {
+                a.setTopActivityInMultiWindowMode(false);
+                a.setTopActivityInPinnedWindowingMode(false);
+                a.setTopActivityInFreeformWindowingMode(true);
+            });
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 44c7057b..a0a2904 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -18,14 +18,12 @@
 
 import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
 import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
@@ -362,72 +360,6 @@
     }
 
     @Test
-    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testShouldSendFakeFocus_overrideEnabled_returnsTrue() {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testIsCompatFakeFocusEnabled_propertyDisabledAndOverrideEnabled_fakeFocusDisabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testIsCompatFakeFocusEnabled_propertyEnabled_noOverride_fakeFocusEnabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    public void testIsCompatFakeFocusEnabled_propertyDisabled_fakeFocusDisabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    public void testIsCompatFakeFocusEnabled_propertyEnabled_fakeFocusEnabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldSendFakeFocus());
-    }
-
-    @Test
     @EnableCompatChanges({FORCE_RESIZE_APP})
     public void testshouldOverrideForceResizeApp_overrideEnabled_returnsTrue() {
         mController = new LetterboxUiController(mWm, mActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 8981f71..72747c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -4809,52 +4808,6 @@
         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
     }
 
-    @Test
-    public void testShouldSendFakeFocus_compatFakeFocusEnabled() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .setOnTop(true)
-                // Set the component to be that of the test class in order to enable compat changes
-                .setComponent(ComponentName.createRelative(mContext,
-                        com.android.server.wm.SizeCompatTests.class.getName()))
-                .build();
-        final Task task = activity.getTask();
-        spyOn(activity.mLetterboxUiController);
-        doReturn(true).when(activity.mLetterboxUiController).shouldSendFakeFocus();
-
-        task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        assertTrue(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_PINNED);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-    }
-
-    @Test
-    public void testShouldSendFakeFocus_compatFakeFocusDisabled() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .setOnTop(true)
-                // Set the component to be that of the test class in order to enable compat changes
-                .setComponent(ComponentName.createRelative(mContext,
-                        com.android.server.wm.SizeCompatTests.class.getName()))
-                .build();
-        final Task task = activity.getTask();
-        spyOn(activity.mLetterboxUiController);
-        doReturn(false).when(activity.mLetterboxUiController).shouldSendFakeFocus();
-
-        task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_PINNED);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-    }
-
     private void setUpAllowThinLetterboxed(boolean thinLetterboxAllowed) {
         spyOn(mActivity.mLetterboxUiController);
         doReturn(thinLetterboxAllowed).when(mActivity.mLetterboxUiController)