[26/n] Adding tests to AppCompatReachability* classes

Flag: EXEMPT refactor
Bug: 336807329
Test: atest WmTests:AppCompatReachabilityPolicyTest
Test: atest WmTests:AppCompatReachabilityOverridesTest

Change-Id: I88a589f36631ea9244580ec6f9488ba21d5a5473
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
index b9bdc32..caff96b 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
@@ -35,7 +35,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.window.flags.Flags;
 
 /**
@@ -112,12 +111,10 @@
                 : mAppCompatConfiguration.getLetterboxVerticalPositionMultiplier(tabletopMode);
     }
 
-    @VisibleForTesting
     boolean isHorizontalReachabilityEnabled() {
         return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
     }
 
-    @VisibleForTesting
     boolean isVerticalReachabilityEnabled() {
         return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
     }
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
index 90bfddb..c3bf116 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
@@ -31,6 +31,8 @@
 import android.annotation.Nullable;
 import android.graphics.Rect;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.function.Supplier;
 
 /**
@@ -43,7 +45,8 @@
     @NonNull
     private final AppCompatConfiguration mAppCompatConfiguration;
     @Nullable
-    private Supplier<Rect> mLetterboxInnerBoundsSupplier;
+    @VisibleForTesting
+    Supplier<Rect> mLetterboxInnerBoundsSupplier;
 
     AppCompatReachabilityPolicy(@NonNull ActivityRecord activityRecord,
             @NonNull AppCompatConfiguration appCompatConfiguration) {
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 f8cf97e..d582b07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -36,9 +36,12 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.view.Surface;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.server.wm.utils.TestComponentStack;
 
@@ -74,6 +77,9 @@
     private final int mDisplayHeight;
     private DisplayContent mDisplayContent;
 
+    @Nullable
+    private Consumer<ActivityRecord> mOnPostActivityCreation;
+
     AppCompatActivityRobot(@NonNull WindowManagerService wm,
             @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor,
             int displayWidth, int displayHeight) {
@@ -96,6 +102,10 @@
                 /* inNewDisplay */ false);
     }
 
+    void createActivityWithComponentWithoutTask() {
+        createActivityWithComponentInNewTask(/* inNewTask */ false, /* inNewDisplay */ false);
+    }
+
     void createActivityWithComponentInNewTask() {
         createActivityWithComponentInNewTask(/* inNewTask */ true, /* inNewDisplay */ false);
     }
@@ -130,6 +140,14 @@
         doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
     }
 
+    void configureTaskBounds(@NonNull Rect taskBounds) {
+        doReturn(taskBounds).when(mTaskStack.top()).getBounds();
+    }
+
+    void configureTopActivityBounds(@NonNull Rect activityBounds) {
+        doReturn(activityBounds).when(mActivityStack.top()).getBounds();
+    }
+
     @NonNull
     ActivityRecord top() {
         return mActivityStack.top();
@@ -169,6 +187,10 @@
                 .isActivityEligibleForOrientationOverride(eq(mActivityStack.top()));
     }
 
+    void setTopActivityInTransition(boolean inTransition) {
+        doReturn(inTransition).when(mActivityStack.top()).isInTransition();
+    }
+
     void setShouldApplyUserMinAspectRatioOverride(boolean enabled) {
         doReturn(enabled).when(mActivityStack.top().mAppCompatController
                 .getAppCompatAspectRatioOverrides()).shouldApplyUserMinAspectRatioOverride();
@@ -378,6 +400,32 @@
         pushActivity(newActivity);
     }
 
+    /**
+     * Specific Robots can override this method to add operation to run on a newly created
+     * {@link ActivityRecord}. Common case is to invoke spyOn().
+     *
+     * @param activity The newly created {@link ActivityRecord}.
+     */
+    @CallSuper
+    void onPostActivityCreation(@NonNull ActivityRecord activity) {
+        spyOn(activity);
+        spyOn(activity.mLetterboxUiController);
+        if (mOnPostActivityCreation != null) {
+            mOnPostActivityCreation.accept(activity);
+        }
+    }
+
+    /**
+     * Each Robot can specify its own set of operation to execute on a newly created
+     * {@link ActivityRecord}. Most common the use of spyOn().
+     *
+     * @param onPostActivityCreation The reference to the code to execute after the creation of a
+     *                               new {@link ActivityRecord}.
+     */
+    void setOnPostActivityCreation(@Nullable Consumer<ActivityRecord> onPostActivityCreation) {
+        mOnPostActivityCreation = onPostActivityCreation;
+    }
+
     private void createActivityWithComponentInNewTask(boolean inNewTask, boolean inNewDisplay) {
         if (inNewDisplay) {
             createNewDisplay();
@@ -385,14 +433,16 @@
         if (inNewTask) {
             createNewTask();
         }
-        final ActivityRecord activity = new WindowTestsBase.ActivityBuilder(mAtm)
-                .setOnTop(true)
-                .setTask(mTaskStack.top())
+        final WindowTestsBase.ActivityBuilder activityBuilder =
+                new WindowTestsBase.ActivityBuilder(mAtm).setOnTop(true)
                 // Set the component to be that of the test class in order
                 // to enable compat changes
-                .setComponent(ComponentName.createRelative(mAtm.mContext, TEST_COMPONENT_NAME))
-                .build();
-        pushActivity(activity);
+                .setComponent(ComponentName.createRelative(mAtm.mContext, TEST_COMPONENT_NAME));
+        if (!mTaskStack.isEmpty()) {
+            // We put the Activity in the current task if any.
+            activityBuilder.setTask(mTaskStack.top());
+        }
+        pushActivity(activityBuilder.build());
     }
 
     /**
@@ -438,14 +488,15 @@
     // We add the activity to the stack and spyOn() on its properties.
     private void pushActivity(@NonNull ActivityRecord activity) {
         mActivityStack.push(activity);
-        spyOn(activity);
+        onPostActivityCreation(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.mAppCompatController.getAppCompatResizeOverrides());
-        spyOn(activity.mLetterboxUiController);
+        spyOn(activity.mAppCompatController.getAppCompatReachabilityPolicy());
+        spyOn(activity.mAppCompatController.getAppCompatReachabilityOverrides());
     }
 
     private void pushTask(@NonNull Task task) {
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 6592f26..40a5347 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -19,6 +19,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import androidx.annotation.NonNull;
@@ -80,4 +83,34 @@
         doReturn(aspectRatio).when(mAppCompatConfiguration)
                 .getFixedOrientationLetterboxAspectRatio();
     }
+
+    void setThinLetterboxWidthPx(int thinWidthPx) {
+        doReturn(thinWidthPx).when(mAppCompatConfiguration)
+                .getThinLetterboxWidthPx();
+    }
+
+    void setThinLetterboxHeightPx(int thinHeightPx) {
+        doReturn(thinHeightPx).when(mAppCompatConfiguration)
+                .getThinLetterboxHeightPx();
+    }
+
+    void checkToNextLeftStop(boolean invoked) {
+        verify(mAppCompatConfiguration, times(invoked ? 1 : 0))
+                .movePositionForHorizontalReachabilityToNextLeftStop(anyBoolean());
+    }
+
+    void checkToNextRightStop(boolean invoked) {
+        verify(mAppCompatConfiguration, times(invoked ? 1 : 0))
+                .movePositionForHorizontalReachabilityToNextRightStop(anyBoolean());
+    }
+
+    void checkToNextBottomStop(boolean invoked) {
+        verify(mAppCompatConfiguration, times(invoked ? 1 : 0))
+                .movePositionForVerticalReachabilityToNextBottomStop(anyBoolean());
+    }
+
+    void checkToNextTopStop(boolean invoked) {
+        verify(mAppCompatConfiguration, times(invoked ? 1 : 0))
+                .movePositionForVerticalReachabilityToNextTopStop(anyBoolean());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
new file mode 100644
index 0000000..47f584a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
@@ -0,0 +1,227 @@
+/*
+ * 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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import com.android.window.flags.Flags;
+
+import junit.framework.Assert;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Test class for {@link AppCompatReachabilityOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatReachabilityOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatReachabilityOverridesTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    public void testIsThinLetterboxed_NegativePx_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponentWithoutTask();
+            robot.conf().setThinLetterboxHeightPx(/* thinHeightPx */ -1);
+            robot.checkIsVerticalThinLetterboxed(/* expected */ false);
+
+            robot.conf().setThinLetterboxWidthPx(/* thinHeightPx */ -1);
+            robot.checkIsHorizontalThinLetterboxed(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testIsThinLetterboxed_noTask_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponentWithoutTask();
+            robot.conf().setThinLetterboxHeightPx(/* thinHeightPx */ 10);
+            robot.checkIsVerticalThinLetterboxed(/* expected */ false);
+
+            robot.conf().setThinLetterboxWidthPx(/* thinHeightPx */ 10);
+            robot.checkIsHorizontalThinLetterboxed(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testIsVerticalThinLetterboxed() {
+        runTestScenario((robot) -> {
+            robot.conf().setThinLetterboxHeightPx(/* thinHeightPx */ 10);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.configureTaskBounds(new Rect(0, 0, 100, 100));
+
+                // (task.width() - act.width()) / 2  = 5 < 10
+                a.configureTopActivityBounds(new Rect(5, 5, 95, 95));
+                robot.checkIsVerticalThinLetterboxed(/* expected */ true);
+
+                // (task.width() - act.width()) / 2  = 10 = 10
+                a.configureTopActivityBounds(new Rect(10, 10, 90, 90));
+                robot.checkIsVerticalThinLetterboxed(/* expected */ true);
+
+                // (task.width() - act.width()) / 2  = 11 > 10
+                a.configureTopActivityBounds(new Rect(11, 11, 89, 89));
+                robot.checkIsVerticalThinLetterboxed(/* expected */ false);
+            });
+        });
+    }
+
+    @Test
+    public void testIsHorizontalThinLetterboxed() {
+        runTestScenario((robot) -> {
+            robot.conf().setThinLetterboxWidthPx(/* thinHeightPx */ 10);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.configureTaskBounds(new Rect(0, 0, 100, 100));
+
+                // (task.height() - act.height()) / 2  = 5 < 10
+                a.configureTopActivityBounds(new Rect(5, 5, 95, 95));
+                robot.checkIsHorizontalThinLetterboxed(/* expected */ true);
+
+                // (task.height() - act.height()) / 2  = 10 = 10
+                a.configureTopActivityBounds(new Rect(10, 10, 90, 90));
+                robot.checkIsHorizontalThinLetterboxed(/* expected */ true);
+
+                // (task.height() - act.height()) / 2  = 11 > 10
+                a.configureTopActivityBounds(new Rect(11, 11, 89, 89));
+                robot.checkIsHorizontalThinLetterboxed(/* expected */ false);
+            });
+        });
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
+    public void testAllowReachabilityForThinLetterboxWithFlagEnabled() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.configureIsVerticalThinLetterboxed(/* isThin */ true);
+            robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ false);
+            robot.configureIsHorizontalThinLetterboxed(/* isThin */ true);
+            robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ false);
+
+            robot.configureIsVerticalThinLetterboxed(/* isThin */ false);
+            robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true);
+            robot.configureIsHorizontalThinLetterboxed(/* isThin */ false);
+            robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true);
+        });
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
+    public void testAllowReachabilityForThinLetterboxWithFlagDisabled() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.configureIsVerticalThinLetterboxed(/* isThin */ true);
+            robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true);
+            robot.configureIsHorizontalThinLetterboxed(/* isThin */ true);
+            robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true);
+
+            robot.configureIsVerticalThinLetterboxed(/* isThin */ false);
+            robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true);
+            robot.configureIsHorizontalThinLetterboxed(/* isThin */ false);
+            robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<ReachabilityOverridesRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final ReachabilityOverridesRobotTest robot =
+                new ReachabilityOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class ReachabilityOverridesRobotTest extends AppCompatRobotBase {
+
+        private final Supplier<Rect> mLetterboxInnerBoundsSupplier = spy(Rect::new);
+
+        ReachabilityOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        @Override
+        void onPostActivityCreation(@NonNull ActivityRecord activity) {
+            super.onPostActivityCreation(activity);
+            activity.mAppCompatController.getAppCompatReachabilityPolicy()
+                    .setLetterboxInnerBoundsSupplier(mLetterboxInnerBoundsSupplier);
+        }
+
+        void configureIsVerticalThinLetterboxed(boolean isThin) {
+            doReturn(isThin).when(getAppCompatReachabilityOverrides())
+                    .isVerticalThinLetterboxed();
+        }
+
+        void configureIsHorizontalThinLetterboxed(boolean isThin) {
+            doReturn(isThin).when(getAppCompatReachabilityOverrides())
+                    .isHorizontalThinLetterboxed();
+        }
+
+        void checkIsVerticalThinLetterboxed(boolean expected) {
+            Assert.assertEquals(expected,
+                    getAppCompatReachabilityOverrides().isVerticalThinLetterboxed());
+        }
+
+        void checkIsHorizontalThinLetterboxed(boolean expected) {
+            Assert.assertEquals(expected,
+                    getAppCompatReachabilityOverrides().isHorizontalThinLetterboxed());
+        }
+
+        void checkAllowVerticalReachabilityForThinLetterbox(boolean expected) {
+            Assert.assertEquals(expected, getAppCompatReachabilityOverrides()
+                    .allowVerticalReachabilityForThinLetterbox());
+        }
+
+        void checkAllowHorizontalReachabilityForThinLetterbox(boolean expected) {
+            Assert.assertEquals(expected, getAppCompatReachabilityOverrides()
+                    .allowHorizontalReachabilityForThinLetterbox());
+        }
+
+        @NonNull
+        private AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
+            return activity().top().mAppCompatController.getAppCompatReachabilityOverrides();
+        }
+
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
new file mode 100644
index 0000000..84f89b5
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Test class for {@link AppCompatReachabilityPolicy}.
+ * <p/>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatReachabilityPolicyTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatReachabilityPolicyTest extends WindowTestsBase {
+
+    @Test
+    public void handleHorizontalDoubleTap_reachabilityDisabled_nothingHappen() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableHorizontalReachability(/* enabled */ false);
+            robot.activity().setTopActivityInTransition(/* inTransition */ true);
+            robot.doubleTapAt(100, 100);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false);
+        });
+    }
+
+    @Test
+    public void handleHorizontalDoubleTap_reachabilityEnabledInTransition_nothingHappen() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableHorizontalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ true);
+            robot.doubleTapAt(100, 100);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false);
+        });
+    }
+
+    @Test
+    public void handleHorizontalDoubleTap_reachabilityDisabledNotInTransition_nothingHappen() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableHorizontalReachability(/* enabled */ false);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+            robot.doubleTapAt(100, 100);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false);
+        });
+    }
+
+    @Test
+    public void handleHorizontalDoubleTap_leftInnerFrame_moveToLeft() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableHorizontalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+
+            robot.configureLetterboxInnerFrameWidth(/* left */ 100, /* right */ 200);
+            robot.doubleTapAt(99, 100);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true);
+            robot.applyOnConf((c) -> {
+                c.checkToNextLeftStop(/* invoked */ true);
+                c.checkToNextRightStop(/* invoked */ false);
+            });
+        });
+    }
+
+    @Test
+    public void handleHorizontalDoubleTap_rightInnerFrame_moveToRight() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableHorizontalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+
+            robot.configureLetterboxInnerFrameWidth(/* left */ 100, /* right */ 200);
+            robot.doubleTapAt(201, 100);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true);
+            robot.applyOnConf((c) -> {
+                c.checkToNextLeftStop(/* invoked */ false);
+                c.checkToNextRightStop(/* invoked */ true);
+            });
+        });
+    }
+
+    @Test
+    public void handleHorizontalDoubleTap_intoInnerFrame_noMove() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableHorizontalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+
+            robot.configureLetterboxInnerFrameWidth(/* left */ 100, /* right */ 200);
+            robot.doubleTapAt(150, 100);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true);
+            robot.applyOnConf((c) -> {
+                c.checkToNextLeftStop(/* invoked */ false);
+                c.checkToNextRightStop(/* invoked */ false);
+            });
+        });
+    }
+
+
+    @Test
+    public void handleVerticalDoubleTap_reachabilityDisabled_nothingHappen() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableVerticalReachability(/* enabled */ false);
+            robot.activity().setTopActivityInTransition(/* inTransition */ true);
+            robot.doubleTapAt(100, 100);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false);
+        });
+    }
+
+    @Test
+    public void handleVerticalDoubleTap_reachabilityEnabledInTransition_nothingHappen() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableVerticalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ true);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false);
+        });
+    }
+
+    @Test
+    public void handleVerticalDoubleTap_reachabilityDisabledNotInTransition_nothingHappen() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableVerticalReachability(/* enabled */ false);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false);
+        });
+    }
+
+    @Test
+    public void handleVerticalDoubleTap_topInnerFrame_moveToTop() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableVerticalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+
+            robot.configureLetterboxInnerFrameHeight(/* top */ 100, /* bottom */ 200);
+            robot.doubleTapAt(100, 99);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true);
+            robot.applyOnConf((c) -> {
+                c.checkToNextTopStop(/* invoked */ true);
+                c.checkToNextBottomStop(/* invoked */ false);
+            });
+        });
+    }
+
+    @Test
+    public void handleVerticalDoubleTap_bottomInnerFrame_moveToBottom() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableVerticalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+
+            robot.configureLetterboxInnerFrameHeight(/* top */ 100, /* bottom */ 200);
+            robot.doubleTapAt(100, 201);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true);
+            robot.applyOnConf((c) -> {
+                c.checkToNextTopStop(/* invoked */ false);
+                c.checkToNextBottomStop(/* invoked */ true);
+            });
+        });
+    }
+
+    @Test
+    public void handleVerticalDoubleTap_intoInnerFrame_noMove() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.enableVerticalReachability(/* enabled */ true);
+            robot.activity().setTopActivityInTransition(/* inTransition */ false);
+
+            robot.configureLetterboxInnerFrameHeight(/* top */ 100, /* bottom */ 200);
+            robot.doubleTapAt(100, 150);
+
+            robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true);
+            robot.applyOnConf((c) -> {
+                c.checkToNextTopStop(/* invoked */ false);
+                c.checkToNextBottomStop(/* invoked */ false);
+            });
+        });
+    }
+
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<ReachabilityPolicyRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final ReachabilityPolicyRobotTest robot =
+                new ReachabilityPolicyRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class ReachabilityPolicyRobotTest extends AppCompatRobotBase {
+
+        private final Supplier<Rect> mLetterboxInnerBoundsSupplier = spy(Rect::new);
+
+        ReachabilityPolicyRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        @Override
+        void onPostActivityCreation(@NonNull ActivityRecord activity) {
+            super.onPostActivityCreation(activity);
+            activity.mAppCompatController.getAppCompatReachabilityPolicy()
+                    .setLetterboxInnerBoundsSupplier(mLetterboxInnerBoundsSupplier);
+        }
+
+        void configureLetterboxInnerFrameWidth(int left, int right) {
+            doReturn(new Rect(left, /* top */ 0, right, /* bottom */ 100))
+                    .when(mLetterboxInnerBoundsSupplier).get();
+        }
+
+        void configureLetterboxInnerFrameHeight(int top, int bottom) {
+            doReturn(new Rect(/* left */ 0, top, /* right */ 100, bottom))
+                    .when(mLetterboxInnerBoundsSupplier).get();
+        }
+
+        void enableHorizontalReachability(boolean enabled) {
+            doReturn(enabled).when(getAppCompatReachabilityOverrides())
+                    .isHorizontalReachabilityEnabled();
+        }
+
+        void enableVerticalReachability(boolean enabled) {
+            doReturn(enabled).when(getAppCompatReachabilityOverrides())
+                    .isVerticalReachabilityEnabled();
+        }
+
+        void doubleTapAt(int x, int y) {
+            getAppCompatReachabilityPolicy().handleDoubleTap(x, y);
+        }
+
+        void checkLetterboxInnerFrameProvidedInvoked(boolean invoked) {
+            verify(mLetterboxInnerBoundsSupplier, times(invoked ? 1 : 0)).get();
+        }
+
+        @NonNull
+        private AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
+            return activity().top().mAppCompatController.getAppCompatReachabilityOverrides();
+        }
+
+        @NonNull
+        private AppCompatReachabilityPolicy getAppCompatReachabilityPolicy() {
+            return activity().top().mAppCompatController.getAppCompatReachabilityPolicy();
+        }
+
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
index 8fc1a77..cade213 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
@@ -39,7 +39,7 @@
 
 /**
  * Test class for {@link AppCompatResizeOverrides}.
- * <p>
+ * <p/>
  * Build/Install/Run:
  * atest WmTests:AppCompatResizeOverridesTest
  */
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
index 6939f97..57ff4f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
 
 import java.util.function.Consumer;
@@ -43,6 +44,7 @@
             int displayWidth, int displayHeight) {
         mActivityRobot = new AppCompatActivityRobot(wm, atm, supervisor,
                 displayWidth, displayHeight);
+        mActivityRobot.setOnPostActivityCreation(this::onPostActivityCreation);
         mConfigurationRobot =
                 new AppCompatConfigurationRobot(wm.mAppCompatConfiguration);
         mOptPropRobot = new AppCompatComponentPropRobot(wm);
@@ -54,6 +56,16 @@
         this(wm, atm, supervisor, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
     }
 
+    /**
+     * Specific Robots can override this method to add operation to run on a newly created
+     * {@link ActivityRecord}. Common case is to invoke spyOn().
+     *
+     * @param activity THe newly created {@link ActivityRecord}.
+     */
+    @CallSuper
+    void onPostActivityCreation(@NonNull ActivityRecord activity) {
+    }
+
     @NonNull
     AppCompatConfigurationRobot conf() {
         return mConfigurationRobot;
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 33df5d8..695068a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -23,11 +23,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -36,8 +34,6 @@
 import android.content.ComponentName;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -49,7 +45,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
-import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -296,106 +291,6 @@
     }
 
     @Test
-    public void testIsVerticalThinLetterboxed() {
-        // Vertical thin letterbox disabled
-        doReturn(-1).when(mActivity.mWmService.mAppCompatConfiguration)
-                .getThinLetterboxHeightPx();
-        final AppCompatReachabilityOverrides reachabilityOverrides = mActivity.mAppCompatController
-                .getAppCompatReachabilityOverrides();
-        assertFalse(reachabilityOverrides.isVerticalThinLetterboxed());
-        // Define a Task 100x100
-        final Task task = mock(Task.class);
-        doReturn(new Rect(0, 0, 100, 100)).when(task).getBounds();
-        doReturn(10).when(mActivity.mWmService.mAppCompatConfiguration)
-                .getThinLetterboxHeightPx();
-
-        // Vertical thin letterbox disabled without Task
-        doReturn(null).when(mActivity).getTask();
-        assertFalse(reachabilityOverrides.isVerticalThinLetterboxed());
-        // Assign a Task for the Activity
-        doReturn(task).when(mActivity).getTask();
-
-        // (task.width() - act.width()) / 2  = 5 < 10
-        doReturn(new Rect(5, 5, 95, 95)).when(mActivity).getBounds();
-        assertTrue(reachabilityOverrides.isVerticalThinLetterboxed());
-
-        // (task.width() - act.width()) / 2  = 10 = 10
-        doReturn(new Rect(10, 10, 90, 90)).when(mActivity).getBounds();
-        assertTrue(reachabilityOverrides.isVerticalThinLetterboxed());
-
-        // (task.width() - act.width()) / 2  = 11 > 10
-        doReturn(new Rect(11, 11, 89, 89)).when(mActivity).getBounds();
-        assertFalse(reachabilityOverrides.isVerticalThinLetterboxed());
-    }
-
-    @Test
-    public void testIsHorizontalThinLetterboxed() {
-        // Horizontal thin letterbox disabled
-        doReturn(-1).when(mActivity.mWmService.mAppCompatConfiguration)
-                .getThinLetterboxWidthPx();
-        final AppCompatReachabilityOverrides reachabilityOverrides = mActivity.mAppCompatController
-                .getAppCompatReachabilityOverrides();
-        assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed());
-        // Define a Task 100x100
-        final Task task = mock(Task.class);
-        doReturn(new Rect(0, 0, 100, 100)).when(task).getBounds();
-        doReturn(10).when(mActivity.mWmService.mAppCompatConfiguration)
-                .getThinLetterboxWidthPx();
-
-        // Vertical thin letterbox disabled without Task
-        doReturn(null).when(mActivity).getTask();
-        assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed());
-        // Assign a Task for the Activity
-        doReturn(task).when(mActivity).getTask();
-
-        // (task.height() - act.height()) / 2  = 5 < 10
-        doReturn(new Rect(5, 5, 95, 95)).when(mActivity).getBounds();
-        assertTrue(reachabilityOverrides.isHorizontalThinLetterboxed());
-
-        // (task.height() - act.height()) / 2  = 10 = 10
-        doReturn(new Rect(10, 10, 90, 90)).when(mActivity).getBounds();
-        assertTrue(reachabilityOverrides.isHorizontalThinLetterboxed());
-
-        // (task.height() - act.height()) / 2  = 11 > 10
-        doReturn(new Rect(11, 11, 89, 89)).when(mActivity).getBounds();
-        assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed());
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
-    public void testAllowReachabilityForThinLetterboxWithFlagEnabled() {
-        final AppCompatReachabilityOverrides reachabilityOverrides =
-                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
-        spyOn(reachabilityOverrides);
-        doReturn(true).when(reachabilityOverrides).isVerticalThinLetterboxed();
-        assertFalse(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
-        doReturn(true).when(reachabilityOverrides).isHorizontalThinLetterboxed();
-        assertFalse(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
-
-        doReturn(false).when(reachabilityOverrides).isVerticalThinLetterboxed();
-        assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
-        doReturn(false).when(reachabilityOverrides).isHorizontalThinLetterboxed();
-        assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
-    public void testAllowReachabilityForThinLetterboxWithFlagDisabled() {
-        final AppCompatReachabilityOverrides reachabilityOverrides =
-                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
-        spyOn(reachabilityOverrides);
-        doReturn(true).when(reachabilityOverrides).isVerticalThinLetterboxed();
-        assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
-        doReturn(true).when(reachabilityOverrides).isHorizontalThinLetterboxed();
-        assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
-
-        doReturn(false).when(reachabilityOverrides).isVerticalThinLetterboxed();
-        assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
-        doReturn(false).when(reachabilityOverrides).isHorizontalThinLetterboxed();
-        assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
-    }
-
-    @Test
     public void testIsLetterboxEducationEnabled() {
         mController.isLetterboxEducationEnabled();
         verify(mAppCompatConfiguration).getIsEducationEnabled();