Extend InputMethodStressTest to add more IME show/hide e2e test cases.

Add more IME show/hide test cases to cover different flag settings(window flags, soft input mode flags), show/hide methods (from auto-show/IMM/WIC/Click), call times and user actions.

Merge the two TestActivitys in AutoShowTest and ImeOpenCloseStressTest into one in ImeStressTestUtil.

Modify test type in TEST_MAPPING to "presubmit-large" to avoid timeout.

See go/imf-test-cases for clearer test descriptions and results.

Bug: 242838873
Bug: 240359838

Test: atest com.android.inputmethod.stresstest.AutoShowTest
atest com.android.inputmethod.stresstest.ImeOpenCloseStressTest

Change-Id: Ibff3a85fa5b66c692a2fe8cd69fb1ca521b1b8d8
diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml
index f5fe8f2..2d183bc 100644
--- a/tests/InputMethodStressTest/AndroidManifest.xml
+++ b/tests/InputMethodStressTest/AndroidManifest.xml
@@ -16,11 +16,11 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.inputmethod.stresstest">
+          package="com.android.inputmethod.stresstest">
 
     <application>
-        <activity android:name=".AutoShowTest$TestActivity"/>
-        <activity android:name=".ImeOpenCloseStressTest$TestActivity"/>
+        <activity android:name=".ImeStressTestUtil$TestActivity"
+                  android:configChanges="orientation|screenSize"/>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/InputMethodStressTest/TEST_MAPPING b/tests/InputMethodStressTest/TEST_MAPPING
index ad07205..06e2ce8 100644
--- a/tests/InputMethodStressTest/TEST_MAPPING
+++ b/tests/InputMethodStressTest/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "InputMethodStressTest"
     }
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
index 92ea029..8e4ecf1 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
@@ -16,24 +16,32 @@
 
 package com.android.inputmethod.stresstest;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.UNFOCUSABLE_VIEW;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
-import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
 
-import android.app.Activity;
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.Instrumentation;
 import android.content.Intent;
-import android.os.Bundle;
+import android.os.SystemClock;
 import android.platform.test.annotations.RootPermissionTest;
 import android.platform.test.rule.UnlockScreenRule;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
 import android.view.WindowManager;
 import android.widget.EditText;
-import android.widget.LinearLayout;
 
-import androidx.annotation.Nullable;
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Rule;
@@ -41,135 +49,428 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
- * Tests to verify the "auto show" behavior in {@code InputMethodManagerService} when the window
+ * Tests to verify the "auto-show" behavior in {@code InputMethodManagerService} when the window
  * gaining the focus to start the input.
  */
 @RootPermissionTest
 @RunWith(Parameterized.class)
 public final class AutoShowTest {
 
-    @Rule
-    public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+    @Rule public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
 
     @Rule
     public ScreenCaptureRule mScreenCaptureRule =
             new ScreenCaptureRule("/sdcard/InputMethodStressTest");
 
-    private static final int[] SOFT_INPUT_VISIBILITY_FLAGS =
-            new int[] {
-                WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
-                WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN,
-                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN,
-                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE,
-                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE,
-            };
-
-    private static final int[] SOFT_INPUT_ADJUST_FLAGS =
-            new int[] {
-                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED,
-                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
-                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN,
-                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
-            };
-
     // TODO(b/240359838): add test case {@code Configuration.SCREENLAYOUT_SIZE_LARGE}.
     @Parameterized.Parameters(
-            name =
-                    "softInputVisibility={0}, softInputAdjustment={1},"
-                            + " softInputModeIsForwardNavigation={2}")
-    public static List<Object[]> softInputModeConfigs() {
-        ArrayList<Object[]> params = new ArrayList<>();
-        for (int softInputVisibility : SOFT_INPUT_VISIBILITY_FLAGS) {
-            for (int softInputAdjust : SOFT_INPUT_ADJUST_FLAGS) {
-                params.add(new Object[] {softInputVisibility, softInputAdjust, true});
-                params.add(new Object[] {softInputVisibility, softInputAdjust, false});
-            }
-        }
-        return params;
+            name = "windowFocusFlags={0}, softInputVisibility={1}, softInputAdjustment={2}")
+    public static List<Object[]> windowAndSoftInputFlagParameters() {
+        return getWindowAndSoftInputFlagParameters();
     }
 
-    private static final String SOFT_INPUT_FLAGS = "soft_input_flags";
+    private final int mSoftInputFlags;
+    private final int mWindowFocusFlags;
+    private final Instrumentation mInstrumentation;
 
-    private final int mSoftInputVisibility;
-    private final int mSoftInputAdjustment;
-    private final boolean mSoftInputIsForwardNavigation;
+    public AutoShowTest(int windowFocusFlags, int softInputVisibility, int softInputAdjustment) {
+        mSoftInputFlags = softInputVisibility | softInputAdjustment;
+        mWindowFocusFlags = windowFocusFlags;
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
 
-    public AutoShowTest(
-            int softInputVisibility,
-            int softInputAdjustment,
-            boolean softInputIsForwardNavigation) {
-        mSoftInputVisibility = softInputVisibility;
-        mSoftInputAdjustment = softInputAdjustment;
-        mSoftInputIsForwardNavigation = softInputIsForwardNavigation;
+    /**
+     * Test auto-show IME behavior when the {@link EditText} is focusable ({@link
+     * EditText#isFocusableInTouchMode} is {@code true}) and has called {@link
+     * EditText#requestFocus}.
+     */
+    @Test
+    public void autoShow_hasFocusedView_requestFocus() {
+        // request focus at onCreate()
+        Intent intent =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+        TestActivity activity = TestActivity.start(intent);
+
+        verifyAutoShowBehavior_forwardWithKeyboardOff(activity);
+    }
+
+    /**
+     * Test auto-show IME behavior when the {@link EditText} is focusable ({@link
+     * EditText#isFocusableInTouchMode} is {@code true}) and {@link EditText#requestFocus} is not
+     * called. The IME should never be shown because there is no focused editor in the window.
+     */
+    @Test
+    public void autoShow_hasFocusedView_notRequestFocus() {
+        // request focus not set
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        EditText editText = activity.getEditText();
+
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus.
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+        } else {
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+        }
+        // IME is always hidden because there is no view focus.
+        verifyImeIsAlwaysHidden(editText);
+    }
+
+    /**
+     * Test auto-show IME behavior when the {@link EditText} is not focusable ({@link
+     * EditText#isFocusableInTouchMode} is {@code false}) and {@link EditText#requestFocus} is not
+     * called. The IME should never be shown because there is no focusable editor in the window.
+     */
+    @Test
+    public void autoShow_notFocusedView_notRequestFocus() {
+        // Unfocusable view, request focus not set
+        Intent intent =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Collections.singletonList(UNFOCUSABLE_VIEW));
+        TestActivity activity = TestActivity.start(intent);
+        EditText editText = activity.getEditText();
+
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus.
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+        } else {
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+        }
+        // IME is always hidden because there is no focused view.
+        verifyImeIsAlwaysHidden(editText);
+    }
+
+    /**
+     * Test auto-show IME behavior when the activity is navigated forward from another activity with
+     * keyboard off.
+     */
+    @Test
+    public void autoShow_forwardWithKeyboardOff() {
+        // Create first activity with keyboard off
+        Intent intent1 =
+                createIntent(
+                        0x0 /* No window focus flags */,
+                        WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+                                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+                        Collections.emptyList());
+        TestActivity firstActivity = TestActivity.start(intent1);
+
+        // Create second activity with parameterized flags:
+        Intent intent2 =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+        TestActivity secondActivity = firstActivity.startSecondTestActivity(intent2);
+
+        // The auto-show behavior should be the same as opening the app
+        verifyAutoShowBehavior_forwardWithKeyboardOff(secondActivity);
+    }
+
+    /**
+     * Test auto-show IME behavior when the activity is navigated forward from another activity with
+     * keyboard on.
+     */
+    @Test
+    public void autoShow_forwardWithKeyboardOn() {
+        // Create first activity with keyboard on
+        Intent intent1 =
+                createIntent(
+                        0x0 /* No window focus flags */,
+                        WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+                                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+                        Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+        TestActivity firstActivity = TestActivity.start(intent1);
+        // Show Ime with InputMethodManager to ensure the keyboard is on.
+        boolean succ = callOnMainSync(firstActivity::showImeWithInputMethodManager);
+        assertThat(succ).isTrue();
+        SystemClock.sleep(1000);
+        mInstrumentation.waitForIdleSync();
+
+        // Create second activity with parameterized flags:
+        Intent intent2 =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+        TestActivity secondActivity = firstActivity.startSecondTestActivity(intent2);
+
+        // The auto-show behavior should be the same as open app
+        verifyAutoShowBehavior_forwardWithKeyboardOn(secondActivity);
+    }
+
+    /**
+     * Test auto-show IME behavior when the activity is navigated back from another activity with
+     * keyboard off.
+     */
+    @Test
+    public void autoShow_backwardWithKeyboardOff() {
+        // Not request focus at onCreate() to avoid triggering auto-show behavior
+        Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity firstActivity = TestActivity.start(intent1);
+        // Request view focus after app starts
+        mInstrumentation.runOnMainSync(firstActivity::requestFocus);
+
+        Intent intent2 =
+                createIntent(
+                        0x0 /* No window focus flags */,
+                        WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+                                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+                        Collections.emptyList());
+        TestActivity secondActivity = firstActivity.startSecondTestActivity(intent2);
+        secondActivity.finish();
+        mInstrumentation.waitForIdleSync();
+
+        // When activity is navigated back from another activity with keyboard off, the keyboard
+        // will not show except when soft input visibility flag is SOFT_INPUT_STATE_ALWAYS_VISIBLE.
+        verifyAutoShowBehavior_backwardWithKeyboardOff(firstActivity);
+    }
+
+    /**
+     * Test auto-show IME behavior when the activity is navigated back from another activity with
+     * keyboard on.
+     */
+    @Test
+    public void autoShow_backwardWithKeyboardOn() {
+        // Not request focus at onCreate() to avoid triggering auto-show behavior
+        Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent1);
+        // Request view focus after app starts
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+
+        // Create second TestActivity
+        Intent intent2 =
+                createIntent(
+                        0x0 /* No window focus flags */,
+                        WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+                                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+                        Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+        ImeStressTestUtil.TestActivity secondActivity = activity.startSecondTestActivity(intent2);
+        // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity
+        boolean succ = callOnMainSync(secondActivity::showImeWithInputMethodManager);
+        assertThat(succ).isTrue();
+        SystemClock.sleep(1000);
+        mInstrumentation.waitForIdleSync();
+        // Close the second activity
+        secondActivity.finish();
+        SystemClock.sleep(1000);
+        mInstrumentation.waitForIdleSync();
+        // When activity is navigated back from another activity with keyboard on, the keyboard
+        // will not hide except when soft input visibility flag is SOFT_INPUT_STATE_ALWAYS_HIDDEN.
+        verifyAutoShowBehavior_backwardWithKeyboardOn(activity);
     }
 
     @Test
-    public void autoShow() {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        int flags = mSoftInputVisibility | mSoftInputAdjustment;
-        if (mSoftInputIsForwardNavigation) {
-            flags |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+    public void clickFocusableView_requestFocus() {
+        if ((mWindowFocusFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            // UiAutomator cannot get UiObject if FLAG_NOT_FOCUSABLE is set
+            return;
+        }
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request view focus after app starts
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+
+        // Find the editText and click it
+        UiObject2 editTextUiObject =
+                UiDevice.getInstance(mInstrumentation)
+                        .wait(Until.findObject(By.clazz(EditText.class)), 5000);
+        assertThat(editTextUiObject).isNotNull();
+        editTextUiObject.click();
+
+        // Ime will show unless window flag is set
+        verifyClickBehavior(activity);
+    }
+
+    @Test
+    public void clickFocusableView_notRequestFocus() {
+        if ((mWindowFocusFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            // UiAutomator cannot get UiObject if FLAG_NOT_FOCUSABLE is set
+            return;
+        }
+        // Not request focus
+        Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent1);
+
+        // Find the editText and click it
+        UiObject2 editTextUiObject =
+                UiDevice.getInstance(mInstrumentation)
+                        .wait(Until.findObject(By.clazz(EditText.class)), 5000);
+        assertThat(editTextUiObject).isNotNull();
+        editTextUiObject.click();
+
+        // Ime will show unless window flag is set
+        verifyClickBehavior(activity);
+    }
+
+    public static void verifyAutoShowBehavior_forwardWithKeyboardOff(TestActivity activity) {
+        // public: also used by ImeOpenCloseStressTest
+        if (hasUnfocusableWindowFlags(activity)) {
+            verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+            return;
         }
 
-        Intent intent =
-                new Intent()
-                        .setAction(Intent.ACTION_MAIN)
-                        .setClass(instrumentation.getContext(), TestActivity.class)
-                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
-                        .putExtra(SOFT_INPUT_FLAGS, flags);
-        TestActivity activity = (TestActivity) instrumentation.startActivitySync(intent);
+        int softInputMode = activity.getWindow().getAttributes().softInputMode;
+        int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+        int softInputAdjustment = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
         EditText editText = activity.getEditText();
-        waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
 
-        if (mSoftInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
-                || mSoftInputVisibility
-                        == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) {
-            // IME will be auto-shown if softInputMode is set with flag:
-            // SOFT_INPUT_STATE_VISIBLE or SOFT_INPUT_STATE_ALWAYS_VISIBLE
-            waitOnMainUntilImeIsShown(editText);
-        } else if (mSoftInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
-                || mSoftInputVisibility
-                        == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
-            // IME will be not be shown if softInputMode is set with flag:
-            // SOFT_INPUT_STATE_HIDDEN or SOFT_INPUT_STATE_ALWAYS_HIDDEN
-            verifyImeIsAlwaysHidden(editText);
-        } else {
-            // The current system behavior will choose to show IME automatically when navigating
-            // forward to an app that has no visibility state specified  (i.e.
-            // SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE flag.
-            if (mSoftInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
-                    && mSoftInputAdjustment == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
-                    && mSoftInputIsForwardNavigation) {
+        verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+        switch (softInputVisibility) {
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: {
+                // IME will be auto-shown if softInputMode is set with flag:
+                // SOFT_INPUT_STATE_VISIBLE or SOFT_INPUT_STATE_ALWAYS_VISIBLE
                 waitOnMainUntilImeIsShown(editText);
+                break;
             }
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: {
+                // IME will be not be auto-shown if softInputMode is set with flag:
+                // SOFT_INPUT_STATE_HIDDEN or SOFT_INPUT_STATE_ALWAYS_HIDDEN,
+                // or stay unchanged if set SOFT_INPUT_STATE_UNCHANGED
+                verifyImeIsAlwaysHidden(editText);
+                break;
+            }
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: {
+                if (softInputAdjustment
+                        == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
+                    // The current system behavior will choose to show IME automatically when
+                    // navigating forward to an app that has no visibility state specified
+                    // (i.e. SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE
+                    // flag.
+                    waitOnMainUntilImeIsShown(editText);
+                } else {
+                    verifyImeIsAlwaysHidden(editText);
+                }
+                break;
+            }
+            default:
+                break;
         }
     }
 
-    public static class TestActivity extends Activity {
-        private EditText mEditText;
+    private static void verifyAutoShowBehavior_forwardWithKeyboardOn(TestActivity activity) {
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        int softInputMode = activity.getWindow().getAttributes().softInputMode;
+        int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+        int softInputAdjustment = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+        EditText editText = activity.getEditText();
 
-        @Override
-        protected void onCreate(@Nullable Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            int flags = getIntent().getIntExtra(SOFT_INPUT_FLAGS, 0);
-            getWindow().setSoftInputMode(flags);
-            LinearLayout rootView = new LinearLayout(this);
-            rootView.setOrientation(LinearLayout.VERTICAL);
-            mEditText = new EditText(this);
-            rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
-            setContentView(rootView);
-            // Ensure the focused view is a text editor (View#onCheckIsTextEditor() returns true) to
-            // automatically display a soft input window.
-            mEditText.requestFocus();
+        if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus. The IME
+            // will always be hidden even though the view can get focus itself.
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ true);
+            // TODO(b/252192121): Ime should be hidden but is shown.
+            // waitOnMainUntilImeIsHidden(editText);
+            return;
+        } else if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+                || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+            // When FLAG_ALT_FOCUSABLE_IM or FLAG_LOCAL_FOCUS_MODE is set, the view can gain both
+            // window focus and view focus but not IME focus. The IME will always be hidden.
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+            // TODO(b/252192121): Ime should be hidden but is shown.
+            // waitOnMainUntilImeIsHidden(editText);
+            return;
         }
+        verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+        switch (softInputVisibility) {
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: {
+                // IME will be auto-shown if softInputMode is set with flag:
+                // SOFT_INPUT_STATE_VISIBLE or SOFT_INPUT_STATE_ALWAYS_VISIBLE
+                waitOnMainUntilImeIsShown(editText);
+                break;
+            }
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: {
+                // IME will be not be auto-shown if softInputMode is set with flag:
+                // SOFT_INPUT_STATE_HIDDEN or SOFT_INPUT_STATE_ALWAYS_HIDDEN
+                // or stay unchanged if set SOFT_INPUT_STATE_UNCHANGED
+                verifyImeIsAlwaysHidden(editText);
+                break;
+            }
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: {
+                if (softInputAdjustment
+                        == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
+                    // The current system behavior will choose to show IME automatically when
+                    // navigating
+                    // forward to an app that has no visibility state specified  (i.e.
+                    // SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE flag.
+                    waitOnMainUntilImeIsShown(editText);
+                } else {
+                    verifyImeIsAlwaysHidden(editText);
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
 
-        public EditText getEditText() {
-            return mEditText;
+    private static void verifyAutoShowBehavior_backwardWithKeyboardOff(TestActivity activity) {
+        if (hasUnfocusableWindowFlags(activity)) {
+            verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+            return;
+        }
+        int softInputMode = activity.getWindow().getAttributes().softInputMode;
+        int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+        EditText editText = activity.getEditText();
+
+        verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+        if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) {
+            waitOnMainUntilImeIsShown(editText);
+        } else {
+            verifyImeIsAlwaysHidden(editText);
+        }
+    }
+
+    private static void verifyAutoShowBehavior_backwardWithKeyboardOn(TestActivity activity) {
+        if (hasUnfocusableWindowFlags(activity)) {
+            verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+            return;
+        }
+        int softInputMode = activity.getWindow().getAttributes().softInputMode;
+        int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+        EditText editText = activity.getEditText();
+
+        verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+        if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
+            verifyImeIsAlwaysHidden(editText);
+        } else {
+            waitOnMainUntilImeIsShown(editText);
+        }
+    }
+
+    private static void verifyClickBehavior(TestActivity activity) {
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        EditText editText = activity.getEditText();
+
+        verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+        if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+                || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+            verifyImeIsAlwaysHidden(editText);
+        } else {
+            waitOnMainUntilImeIsShown(editText);
         }
     }
 }
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
index 8419276..82acfb6 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -16,224 +16,535 @@
 
 package com.android.inputmethod.stresstest;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.INPUT_METHOD_MANAGER_HIDE_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.INPUT_METHOD_MANAGER_SHOW_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.isImeShown;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
 
-import android.app.Activity;
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.Instrumentation;
 import android.content.Intent;
-import android.os.Bundle;
+import android.os.Build;
 import android.os.SystemClock;
 import android.platform.test.annotations.RootPermissionTest;
 import android.platform.test.rule.UnlockScreenRule;
+import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
-import android.view.WindowInsets;
-import android.view.WindowInsetsAnimation;
-import android.view.inputmethod.InputMethodManager;
+import android.view.WindowManager;
 import android.widget.EditText;
-import android.widget.LinearLayout;
 
-import androidx.annotation.Nullable;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 @RootPermissionTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 public final class ImeOpenCloseStressTest {
 
     private static final String TAG = "ImeOpenCloseStressTest";
     private static final int NUM_TEST_ITERATIONS = 10;
 
-    @Rule
-    public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+    @Rule public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
 
     @Rule
     public ScreenCaptureRule mScreenCaptureRule =
             new ScreenCaptureRule("/sdcard/InputMethodStressTest");
-    private Instrumentation mInstrumentation;
 
-    @Before
-    public void setUp() {
+    private final Instrumentation mInstrumentation;
+    private final int mSoftInputFlags;
+    private final int mWindowFocusFlags;
+
+    @Parameterized.Parameters(
+            name = "windowFocusFlags={0}, softInputVisibility={1}, softInputAdjustment={2}")
+    public static List<Object[]> windowAndSoftInputFlagParameters() {
+        return getWindowAndSoftInputFlagParameters();
+    }
+
+    public ImeOpenCloseStressTest(
+            int windowFocusFlags, int softInputVisibility, int softInputAdjustment) {
+        mSoftInputFlags = softInputVisibility | softInputAdjustment;
+        mWindowFocusFlags = windowFocusFlags;
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     }
 
     @Test
-    public void testShowHide_waitingVisibilityChange() {
-        TestActivity activity = TestActivity.start();
-        EditText editText = activity.getEditText();
-        waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
-        for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+    public void testShowHideWithInputMethodManager_waitingVisibilityChange() {
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+        // Test only once if window flags set to save time.
+        int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
+        for (int i = 0; i < iterNum; i++) {
             String msgPrefix = "Iteration #" + i + " ";
             Log.i(TAG, msgPrefix + "start");
-            mInstrumentation.runOnMainSync(activity::showIme);
-            waitOnMainUntil(msgPrefix + "IME should be visible", () -> isImeShown(editText));
-            mInstrumentation.runOnMainSync(activity::hideIme);
-            waitOnMainUntil(msgPrefix + "IME should be hidden", () -> !isImeShown(editText));
+            boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager);
+            assertThat(showResult).isEqualTo(!(hasUnfocusableWindowFlags(activity)));
+            verifyShowBehavior(activity);
+
+            boolean hideResult = callOnMainSync(activity::hideImeWithInputMethodManager);
+            assertThat(hideResult).isEqualTo(!(hasUnfocusableWindowFlags(activity)));
+
+            verifyHideBehavior(activity);
         }
     }
 
     @Test
-    public void testShowHide_waitingAnimationEnd() {
-        TestActivity activity = TestActivity.start();
+    public void testShowHideWithInputMethodManager_waitingAnimationEnd() {
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+
+        if (hasUnfocusableWindowFlags(activity)) {
+            return; // Skip to save time.
+        }
         activity.enableAnimationMonitoring();
         EditText editText = activity.getEditText();
-        waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
         for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
             String msgPrefix = "Iteration #" + i + " ";
             Log.i(TAG, msgPrefix + "start");
-            mInstrumentation.runOnMainSync(activity::showIme);
-            waitOnMainUntil(msgPrefix + "IME should be visible",
+            boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager);
+            assertThat(showResult).isTrue();
+            waitOnMainUntil(
+                    msgPrefix + "IME should be visible",
                     () -> !activity.isAnimating() && isImeShown(editText));
-            mInstrumentation.runOnMainSync(activity::hideIme);
-            waitOnMainUntil(msgPrefix + "IME should be hidden",
+
+            boolean hideResult = callOnMainSync(activity::hideImeWithInputMethodManager);
+            assertThat(hideResult).isTrue();
+            waitOnMainUntil(
+                    msgPrefix + "IME should be hidden",
                     () -> !activity.isAnimating() && !isImeShown(editText));
         }
     }
 
     @Test
-    public void testShowHide_intervalAfterHide() {
+    public void testShowHideWithInputMethodManager_intervalAfterHide() {
         // Regression test for b/221483132
-        TestActivity activity = TestActivity.start();
-        EditText editText = activity.getEditText();
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+        if (hasUnfocusableWindowFlags(activity)) {
+            return; // Skip to save time.
+        }
         // Intervals = 10, 20, 30, ..., 100, 150, 200, ...
         List<Integer> intervals = new ArrayList<>();
         for (int i = 10; i < 100; i += 10) intervals.add(i);
         for (int i = 100; i < 1000; i += 50) intervals.add(i);
-        waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
+        boolean firstHide = false;
         for (int intervalMillis : intervals) {
             String msgPrefix = "Interval = " + intervalMillis + " ";
             Log.i(TAG, msgPrefix + " start");
-            mInstrumentation.runOnMainSync(activity::hideIme);
+            boolean hideResult = callOnMainSync(activity::hideImeWithInputMethodManager);
+            assertThat(hideResult).isEqualTo(firstHide);
+            firstHide = true;
             SystemClock.sleep(intervalMillis);
-            mInstrumentation.runOnMainSync(activity::showIme);
-            waitOnMainUntil(msgPrefix + "IME should be visible",
-                    () -> isImeShown(editText));
+
+            boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager);
+            assertThat(showResult).isTrue();
+            verifyShowBehavior(activity);
         }
     }
 
     @Test
-    public void testShowHideInSameFrame() {
-        TestActivity activity = TestActivity.start();
-        activity.enableAnimationMonitoring();
-        EditText editText = activity.getEditText();
-        waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
+    public void testShowHideWithInputMethodManager_inSameFrame() {
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
 
+        if (hasUnfocusableWindowFlags(activity)) {
+            return; // Skip to save time.
+        }
         // hidden -> show -> hide
-        mInstrumentation.runOnMainSync(() -> {
-            Log.i(TAG, "Calling showIme() and hideIme()");
-            activity.showIme();
-            activity.hideIme();
-        });
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Log.i(TAG, "Calling showIme() and hideIme()");
+                    activity.showImeWithInputMethodManager();
+                    activity.hideImeWithInputMethodManager();
+                });
         // Wait until IMMS / IMS handles messages.
         SystemClock.sleep(1000);
         mInstrumentation.waitForIdleSync();
-        waitOnMainUntil("IME should be invisible after show/hide", () -> !isImeShown(editText));
+        verifyHideBehavior(activity);
 
-        mInstrumentation.runOnMainSync(activity::showIme);
-        waitOnMainUntil("IME should be visible",
-                () -> !activity.isAnimating() && isImeShown(editText));
+        mInstrumentation.runOnMainSync(activity::showImeWithInputMethodManager);
+        verifyShowBehavior(activity);
         mInstrumentation.waitForIdleSync();
 
         // shown -> hide -> show
-        mInstrumentation.runOnMainSync(() -> {
-            Log.i(TAG, "Calling hideIme() and showIme()");
-            activity.hideIme();
-            activity.showIme();
-        });
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Log.i(TAG, "Calling hideIme() and showIme()");
+                    activity.hideImeWithInputMethodManager();
+                    activity.showImeWithInputMethodManager();
+                });
         // Wait until IMMS / IMS handles messages.
         SystemClock.sleep(1000);
         mInstrumentation.waitForIdleSync();
-        waitOnMainUntil("IME should be visible after hide/show",
-                () -> !activity.isAnimating() && isImeShown(editText));
+        verifyShowBehavior(activity);
     }
 
-    public static class TestActivity extends Activity {
+    @Test
+    public void testShowHideWithInputMethodManager_onCreate() {
+        // Show and hide with InputMethodManager at onCreate()
+        Intent intent =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Arrays.asList(
+                                REQUEST_FOCUS_ON_CREATE,
+                                INPUT_METHOD_MANAGER_SHOW_ON_CREATE,
+                                INPUT_METHOD_MANAGER_HIDE_ON_CREATE));
+        TestActivity activity = TestActivity.start(intent);
 
-        private EditText mEditText;
-        private boolean mIsAnimating;
+        // TODO: The Ime is expected to show first and then hide. But show or hide
+        // with InputMethodManager at onCreate() would always fail because the window
+        // has not gained focus, so the actual behavior will be the same as auto-show.
+        // verifyHideBehavior(activity);
+    }
 
-        private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
-                new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
-                    @Override
-                    public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation,
-                            WindowInsetsAnimation.Bounds bounds) {
-                        mIsAnimating = true;
-                        return super.onStart(animation, bounds);
-                    }
+    @Test
+    public void testShowWithInputMethodManager_notRequestFocus() {
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
 
-                    @Override
-                    public void onEnd(WindowInsetsAnimation animation) {
-                        super.onEnd(animation);
-                        mIsAnimating = false;
-                    }
+        // Show InputMethodManager without requesting focus
+        boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager);
+        assertThat(showResult).isFalse();
 
-                    @Override
-                    public WindowInsets onProgress(WindowInsets insets,
-                            List<WindowInsetsAnimation> runningAnimations) {
-                        return insets;
-                    }
-                };
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        EditText editText = activity.getEditText();
+        if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+        } else {
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+        }
+        // The Ime should always be hidden because view never gains focus.
+        verifyImeIsAlwaysHidden(editText);
+    }
 
-        public static TestActivity start() {
-            Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-            Intent intent = new Intent()
-                    .setAction(Intent.ACTION_MAIN)
-                    .setClass(instrumentation.getContext(), TestActivity.class)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-            return (TestActivity) instrumentation.startActivitySync(intent);
+    @Test
+    public void testShowHideWithWindowInsetsController_waitingVisibilityChange() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+        // Test only once if window flags set to save time.
+        int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
+        for (int i = 0; i < iterNum; i++) {
+            String msgPrefix = "Iteration #" + i + " ";
+            Log.i(TAG, msgPrefix + "start");
+            mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+            verifyShowBehavior(activity);
+            mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
+            verifyHideBehavior(activity);
+        }
+    }
+
+    @Test
+    public void testShowHideWithWindowInsetsController_waitingAnimationEnd() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+
+        if (hasUnfocusableWindowFlags(activity)) {
+            return; // Skip to save time.
+        }
+        activity.enableAnimationMonitoring();
+        EditText editText = activity.getEditText();
+        for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+            String msgPrefix = "Iteration #" + i + " ";
+            Log.i(TAG, msgPrefix + "start");
+            mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+            waitOnMainUntil(
+                    msgPrefix + "IME should be visible",
+                    () -> !activity.isAnimating() && isImeShown(editText));
+
+            mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
+            waitOnMainUntil(
+                    msgPrefix + "IME should be hidden",
+                    () -> !activity.isAnimating() && !isImeShown(editText));
+        }
+    }
+
+    @Test
+    public void testShowHideWithWindowInsetsController_intervalAfterHide() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+
+        if (hasUnfocusableWindowFlags(activity)) {
+            return; // Skip to save time.
+        }
+        // Intervals = 10, 20, 30, ..., 100, 150, 200, ...
+        List<Integer> intervals = new ArrayList<>();
+        for (int i = 10; i < 100; i += 10) intervals.add(i);
+        for (int i = 100; i < 1000; i += 50) intervals.add(i);
+        for (int intervalMillis : intervals) {
+            String msgPrefix = "Interval = " + intervalMillis + " ";
+            Log.i(TAG, msgPrefix + " start");
+            mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
+            SystemClock.sleep(intervalMillis);
+
+            mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+            verifyShowBehavior(activity);
+        }
+    }
+
+    @Test
+    public void testShowHideWithWindowInsetsController_inSameFrame() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        // Request focus after app starts to avoid triggering auto-show behavior.
+        mInstrumentation.runOnMainSync(activity::requestFocus);
+
+        if (hasUnfocusableWindowFlags(activity)) {
+            return; // Skip to save time.
+        }
+        // hidden -> show -> hide
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Log.i(TAG, "Calling showIme() and hideIme()");
+                    activity.showImeWithWindowInsetsController();
+                    activity.hideImeWithWindowInsetsController();
+                });
+        // Wait until IMMS / IMS handles messages.
+        SystemClock.sleep(1000);
+        mInstrumentation.waitForIdleSync();
+        // TODO(b/248456059): Ime should be hidden but is shown.
+        // verifyHideBehavior(activity);
+
+        mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+        verifyShowBehavior(activity);
+        mInstrumentation.waitForIdleSync();
+
+        // shown -> hide -> show
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Log.i(TAG, "Calling hideIme() and showIme()");
+                    activity.hideImeWithWindowInsetsController();
+                    activity.showImeWithWindowInsetsController();
+                });
+        // Wait until IMMS / IMS handles messages.
+        SystemClock.sleep(1000);
+        mInstrumentation.waitForIdleSync();
+        verifyShowBehavior(activity);
+    }
+
+    @Test
+    public void testShowWithWindowInsetsController_onCreate_requestFocus() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        // Show with InputMethodManager at onCreate()
+        Intent intent =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Arrays.asList(
+                                REQUEST_FOCUS_ON_CREATE, WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE));
+        TestActivity activity = TestActivity.start(intent);
+
+        verifyShowBehavior(activity);
+    }
+
+    @Test
+    public void testShowWithWindowInsetsController_onCreate_notRequestFocus() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        // Show and hide with InputMethodManager at onCreate()
+        Intent intent =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Collections.singletonList(WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE));
+        TestActivity activity = TestActivity.start(intent);
+
+        // Ime is shown but with a fallback InputConnection
+        verifyShowBehaviorNotRequestFocus(activity);
+    }
+
+    @Test
+    public void testShowWithWindowInsetsController_afterStart_notRequestFocus() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        // Show and hide with InputMethodManager at onCreate()
+        Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+        TestActivity activity = TestActivity.start(intent);
+        mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+
+        // Ime is shown but with a fallback InputConnection
+        verifyShowBehaviorNotRequestFocus(activity);
+    }
+
+    @Test
+    public void testHideWithWindowInsetsController_onCreate_requestFocus() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            return;
+        }
+        // Show and hide with InputMethodManager at onCreate()
+        Intent intent =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Arrays.asList(
+                                REQUEST_FOCUS_ON_CREATE,
+                                WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE,
+                                WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE));
+        TestActivity activity = TestActivity.start(intent);
+
+        // TODO(b/248456059): Ime should be hidden but is shown.
+        //verifyHideBehavior(activity);
+    }
+
+    @Test
+    public void testScreenOffOn() throws Exception {
+        Intent intent1 =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+        TestActivity activity = TestActivity.start(intent1);
+        // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity
+        boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager);
+        assertThat(showResult).isEqualTo(!(hasUnfocusableWindowFlags(activity)));
+
+        Thread.sleep(1000);
+        verifyShowBehavior(activity);
+
+        UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
+
+        if (uiDevice.isScreenOn()) {
+            uiDevice.sleep();
+        }
+        Thread.sleep(1000);
+        if (!uiDevice.isScreenOn()) {
+            uiDevice.wakeUp();
         }
 
-        @Override
-        protected void onCreate(@Nullable Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            LinearLayout rootView = new LinearLayout(this);
-            rootView.setOrientation(LinearLayout.VERTICAL);
-            mEditText = new EditText(this);
-            rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
-            setContentView(rootView);
-        }
+        verifyShowBehavior(activity);
+    }
 
-        public EditText getEditText() {
-            return mEditText;
-        }
+    @Test
+    public void testRotateScreenWithKeyboardOn() throws Exception {
+        // TODO(b/256739702): Keyboard disappears after rotating screen to landscape mode if
+        // android:configChanges="orientation|screenSize" is not set
+        Intent intent =
+                createIntent(
+                        mWindowFocusFlags,
+                        mSoftInputFlags,
+                        Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+        TestActivity activity = TestActivity.start(intent);
+        // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity
+        boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager);
+        assertThat(showResult).isEqualTo(!(hasUnfocusableWindowFlags(activity)));
+        Thread.sleep(2000);
+        verifyShowBehavior(activity);
 
-        public void showIme() {
-            Log.i(TAG, "TestActivity.showIme");
-            mEditText.requestFocus();
-            InputMethodManager imm = getSystemService(InputMethodManager.class);
-            imm.showSoftInput(mEditText, 0);
-        }
+        UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
 
-        public void hideIme() {
-            Log.i(TAG, "TestActivity.hideIme");
-            InputMethodManager imm = getSystemService(InputMethodManager.class);
-            imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
-        }
+        uiDevice.freezeRotation();
+        uiDevice.setOrientationRight();
+        uiDevice.waitForIdle();
+        Thread.sleep(1000);
+        Log.i(TAG, "Rotate screen right");
+        assertThat(uiDevice.isNaturalOrientation()).isFalse();
+        verifyShowBehavior(activity);
 
-        public void enableAnimationMonitoring() {
-            // Enable WindowInsetsAnimation.
-            // Note that this has a side effect of disabling InsetsAnimationThreadControlRunner.
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-                getWindow().setDecorFitsSystemWindows(false);
-                mEditText.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
-            });
-        }
+        uiDevice.setOrientationLeft();
+        uiDevice.waitForIdle();
+        Thread.sleep(1000);
+        Log.i(TAG, "Rotate screen left");
+        assertThat(uiDevice.isNaturalOrientation()).isFalse();
+        verifyShowBehavior(activity);
 
-        public boolean isAnimating() {
-            return mIsAnimating;
+        uiDevice.setOrientationNatural();
+        uiDevice.waitForIdle();
+        uiDevice.unfreezeRotation();
+    }
+
+    private static void verifyShowBehavior(TestActivity activity) {
+        if (hasUnfocusableWindowFlags(activity)) {
+            verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+            return;
+        }
+        EditText editText = activity.getEditText();
+
+        verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+        waitOnMainUntilImeIsShown(editText);
+    }
+
+    private static void verifyHideBehavior(TestActivity activity) {
+        if (hasUnfocusableWindowFlags(activity)) {
+            verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+            return;
+        }
+        EditText editText = activity.getEditText();
+
+        verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+        waitOnMainUntilImeIsHidden(editText);
+    }
+
+    private static void verifyShowBehaviorNotRequestFocus(TestActivity activity) {
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        EditText editText = activity.getEditText();
+
+        if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+            verifyImeIsAlwaysHidden(editText);
+        } else if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+                || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+            verifyImeIsAlwaysHidden(editText);
+        } else {
+            verifyWindowAndViewFocus(
+                    editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+            // Ime is shown but with a fallback InputConnection
+            waitOnMainUntilImeIsShown(editText);
         }
     }
 }
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index b6d462c..e16c915 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -16,17 +16,37 @@
 
 package com.android.inputmethod.stresstest;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
 import static com.android.compatibility.common.util.SystemUtil.eventually;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
 import android.view.View;
 import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
 
+import androidx.annotation.Nullable;
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.ThrowingRunnable;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -34,28 +54,96 @@
 /** Utility methods for IME stress test. */
 public final class ImeStressTestUtil {
 
-    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
-    private static final long VERIFY_DURATION = TimeUnit.SECONDS.toMillis(2);
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(3);
 
-    private ImeStressTestUtil() {
+    private ImeStressTestUtil() {}
+
+    private static final int[] WINDOW_FOCUS_FLAGS =
+            new int[] {
+                LayoutParams.FLAG_NOT_FOCUSABLE,
+                LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                LayoutParams.FLAG_LOCAL_FOCUS_MODE
+            };
+
+    private static final int[] SOFT_INPUT_VISIBILITY_FLAGS =
+            new int[] {
+                LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
+                LayoutParams.SOFT_INPUT_STATE_UNCHANGED,
+                LayoutParams.SOFT_INPUT_STATE_HIDDEN,
+                LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN,
+                LayoutParams.SOFT_INPUT_STATE_VISIBLE,
+                LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE,
+            };
+
+    private static final int[] SOFT_INPUT_ADJUST_FLAGS =
+            new int[] {
+                LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED,
+                LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+                LayoutParams.SOFT_INPUT_ADJUST_PAN,
+                LayoutParams.SOFT_INPUT_ADJUST_NOTHING
+            };
+
+    public static final String SOFT_INPUT_FLAGS = "soft_input_flags";
+    public static final String WINDOW_FLAGS = "window_flags";
+    public static final String UNFOCUSABLE_VIEW = "unfocusable_view";
+    public static final String REQUEST_FOCUS_ON_CREATE = "request_focus_on_create";
+    public static final String INPUT_METHOD_MANAGER_SHOW_ON_CREATE =
+            "input_method_manager_show_on_create";
+    public static final String INPUT_METHOD_MANAGER_HIDE_ON_CREATE =
+            "input_method_manager_hide_on_create";
+    public static final String WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE =
+            "window_insets_controller_show_on_create";
+    public static final String WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE =
+            "window_insets_controller_hide_on_create";
+
+    /** Parameters for show/hide ime parameterized tests. */
+    public static ArrayList<Object[]> getWindowAndSoftInputFlagParameters() {
+        ArrayList<Object[]> params = new ArrayList<>();
+
+        // Set different window focus flags and keep soft input flags as default values (4 cases)
+        for (int windowFocusFlags : WINDOW_FOCUS_FLAGS) {
+            params.add(
+                    new Object[] {
+                        windowFocusFlags,
+                        LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
+                        LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+                    });
+        }
+        // Set the combinations of different softInputVisibility, softInputAdjustment flags,
+        // keep the window focus flag as default value ( 6 * 4 = 24 cases)
+        for (int softInputVisibility : SOFT_INPUT_VISIBILITY_FLAGS) {
+            for (int softInputAdjust : SOFT_INPUT_ADJUST_FLAGS) {
+                params.add(
+                        new Object[] {
+                            0x0 /* No window focus flags */, softInputVisibility, softInputAdjust
+                        });
+            }
+        }
+        return params;
     }
 
     /** Checks if the IME is shown on the window that the given view belongs to. */
     public static boolean isImeShown(View view) {
         WindowInsets insets = view.getRootWindowInsets();
+        if (insets == null) {
+            return false;
+        }
         return insets.isVisible(WindowInsets.Type.ime());
     }
 
     /** Calls the callable on the main thread and returns the result. */
     public static <V> V callOnMainSync(Callable<V> callable) {
         AtomicReference<V> result = new AtomicReference<>();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            try {
-                result.set(callable.call());
-            } catch (Exception e) {
-                throw new RuntimeException("Exception was thrown", e);
-            }
-        });
+        InstrumentationRegistry.getInstrumentation()
+                .runOnMainSync(
+                        () -> {
+                            try {
+                                result.set(callable.call());
+                            } catch (Exception e) {
+                                throw new RuntimeException("Exception was thrown", e);
+                            }
+                        });
         return result.get();
     }
 
@@ -70,15 +158,42 @@
 
     /** Waits until IME is shown, or throws on timeout. */
     public static void waitOnMainUntilImeIsShown(View view) {
-        eventually(() -> assertWithMessage("IME should be shown").that(
-                callOnMainSync(() -> isImeShown(view))).isTrue(), TIMEOUT);
+        eventually(
+                () ->
+                        assertWithMessage("IME should be shown")
+                                .that(callOnMainSync(() -> isImeShown(view)))
+                                .isTrue(),
+                TIMEOUT);
     }
 
     /** Waits until IME is hidden, or throws on timeout. */
     public static void waitOnMainUntilImeIsHidden(View view) {
-        //eventually(() -> assertThat(callOnMainSync(() -> isImeShown(view))).isFalse(), TIMEOUT);
-        eventually(() -> assertWithMessage("IME should be hidden").that(
-                callOnMainSync(() -> isImeShown(view))).isFalse(), TIMEOUT);
+        eventually(
+                () ->
+                        assertWithMessage("IME should be hidden")
+                                .that(callOnMainSync(() -> isImeShown(view)))
+                                .isFalse(),
+                TIMEOUT);
+    }
+
+    /** Waits until window get focus, or throws on timeout. */
+    public static void waitOnMainUntilWindowGainsFocus(View view) {
+        eventually(
+                () ->
+                        assertWithMessage("Window should gain focus")
+                                .that(callOnMainSync(view::hasWindowFocus))
+                                .isTrue(),
+                TIMEOUT);
+    }
+
+    /** Waits until view get focus, or throws on timeout. */
+    public static void waitOnMainUntilViewGainsFocus(View view) {
+        eventually(
+                () ->
+                        assertWithMessage("View should gain focus")
+                                .that(callOnMainSync(view::hasFocus))
+                                .isTrue(),
+                TIMEOUT);
     }
 
     /** Verify IME is always hidden within the given time duration. */
@@ -88,7 +203,27 @@
                         assertWithMessage("IME should be hidden")
                                 .that(callOnMainSync(() -> isImeShown(view)))
                                 .isFalse(),
-                VERIFY_DURATION);
+                TIMEOUT);
+    }
+
+    /** Verify the window never gains focus within the given time duration. */
+    public static void verifyWindowNeverGainsFocus(View view) {
+        always(
+                () ->
+                        assertWithMessage("window should never gain focus")
+                                .that(callOnMainSync(view::hasWindowFocus))
+                                .isFalse(),
+                TIMEOUT);
+    }
+
+    /** Verify the view never gains focus within the given time duration. */
+    public static void verifyViewNeverGainsFocus(View view) {
+        always(
+                () ->
+                        assertWithMessage("view should never gain ime focus")
+                                .that(callOnMainSync(view::hasFocus))
+                                .isFalse(),
+                TIMEOUT);
     }
 
     /**
@@ -117,4 +252,232 @@
             }
         }
     }
+
+    public static boolean hasUnfocusableWindowFlags(Activity activity) {
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        return (windowFlags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0
+                || (windowFlags & LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+                || (windowFlags & LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
+    }
+
+    public static void verifyWindowAndViewFocus(
+            View view, boolean expectWindowFocus, boolean expectViewFocus) {
+        if (expectWindowFocus) {
+            waitOnMainUntilWindowGainsFocus(view);
+        } else {
+            verifyWindowNeverGainsFocus(view);
+        }
+        if (expectViewFocus) {
+            waitOnMainUntilViewGainsFocus(view);
+        } else {
+            verifyViewNeverGainsFocus(view);
+        }
+    }
+
+    public static void verifyImeAlwaysHiddenWithWindowFlagSet(TestActivity activity) {
+        int windowFlags = activity.getWindow().getAttributes().flags;
+        View view = activity.getEditText();
+        if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+            // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus. The IME
+            // will always be hidden even though the view can get focus itself.
+            verifyWindowAndViewFocus(view, /*expectWindowFocus*/ false, /*expectViewFocus*/ true);
+            verifyImeIsAlwaysHidden(view);
+        } else if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+                || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+            // When FLAG_ALT_FOCUSABLE_IM or FLAG_LOCAL_FOCUS_MODE is set, the view can gain both
+            // window focus and view focus but not IME focus. The IME will always be hidden.
+            verifyWindowAndViewFocus(view, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+            verifyImeIsAlwaysHidden(view);
+        }
+    }
+
+    /** Activity to help test show/hide behavior of IME. */
+    public static class TestActivity extends Activity {
+        private static final String TAG = "ImeStressTestUtil.TestActivity";
+        private EditText mEditText;
+        private boolean mIsAnimating;
+
+        private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
+                new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+                    @Override
+                    public WindowInsetsAnimation.Bounds onStart(
+                            WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+                        mIsAnimating = true;
+                        return super.onStart(animation, bounds);
+                    }
+
+                    @Override
+                    public void onEnd(WindowInsetsAnimation animation) {
+                        super.onEnd(animation);
+                        mIsAnimating = false;
+                    }
+
+                    @Override
+                    public WindowInsets onProgress(
+                            WindowInsets insets, List<WindowInsetsAnimation> runningAnimations) {
+                        return insets;
+                    }
+                };
+
+        /** Create intent with extras. */
+        public static Intent createIntent(
+                int windowFlags, int softInputFlags, List<String> extras) {
+            Intent intent =
+                    new Intent()
+                            .putExtra(WINDOW_FLAGS, windowFlags)
+                            .putExtra(SOFT_INPUT_FLAGS, softInputFlags);
+            for (String extra : extras) {
+                intent.putExtra(extra, true);
+            }
+            return intent;
+        }
+
+        @Override
+        protected void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            Log.i(TAG, "onCreate()");
+            boolean isUnfocusableView = getIntent().getBooleanExtra(UNFOCUSABLE_VIEW, false);
+            boolean requestFocus = getIntent().getBooleanExtra(REQUEST_FOCUS_ON_CREATE, false);
+            int softInputFlags = getIntent().getIntExtra(SOFT_INPUT_FLAGS, 0);
+            int windowFlags = getIntent().getIntExtra(WINDOW_FLAGS, 0);
+            boolean showWithInputMethodManagerOnCreate =
+                    getIntent().getBooleanExtra(INPUT_METHOD_MANAGER_SHOW_ON_CREATE, false);
+            boolean hideWithInputMethodManagerOnCreate =
+                    getIntent().getBooleanExtra(INPUT_METHOD_MANAGER_HIDE_ON_CREATE, false);
+            boolean showWithWindowInsetsControllerOnCreate =
+                    getIntent().getBooleanExtra(WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE, false);
+            boolean hideWithWindowInsetsControllerOnCreate =
+                    getIntent().getBooleanExtra(WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE, false);
+
+            getWindow().addFlags(windowFlags);
+            getWindow().setSoftInputMode(softInputFlags);
+
+            LinearLayout rootView = new LinearLayout(this);
+            rootView.setOrientation(LinearLayout.VERTICAL);
+            mEditText = new EditText(this);
+            if (isUnfocusableView) {
+                mEditText.setFocusableInTouchMode(false);
+            }
+            rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+            setContentView(rootView);
+
+            if (requestFocus) {
+                requestFocus();
+            }
+            if (showWithInputMethodManagerOnCreate) {
+                showImeWithInputMethodManager();
+            }
+            if (hideWithInputMethodManagerOnCreate) {
+                hideImeWithInputMethodManager();
+            }
+            if (showWithWindowInsetsControllerOnCreate) {
+                showImeWithWindowInsetsController();
+            }
+            if (hideWithWindowInsetsControllerOnCreate) {
+                hideImeWithWindowInsetsController();
+            }
+        }
+
+        /** Show IME with InputMethodManager. */
+        public boolean showImeWithInputMethodManager() {
+            boolean showResult =
+                    getInputMethodManager()
+                            .showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
+            if (showResult) {
+                Log.i(TAG, "IMM#showSoftInput successfully");
+            } else {
+                Log.i(TAG, "IMM#showSoftInput failed");
+            }
+            return showResult;
+        }
+
+        /** Hide IME with InputMethodManager. */
+        public boolean hideImeWithInputMethodManager() {
+            boolean hideResult =
+                    getInputMethodManager().hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+            if (hideResult) {
+                Log.i(TAG, "IMM#hideSoftInput successfully");
+            } else {
+                Log.i(TAG, "IMM#hideSoftInput failed");
+            }
+            return hideResult;
+        }
+
+        /** Show IME with WindowInsetsController */
+        public void showImeWithWindowInsetsController() {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+                return;
+            }
+            Log.i(TAG, "showImeWithWIC()");
+            WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
+            assertWithMessage("WindowInsetsController shouldn't be null.")
+                    .that(windowInsetsController)
+                    .isNotNull();
+            windowInsetsController.show(WindowInsets.Type.ime());
+        }
+
+        /** Hide IME with WindowInsetsController. */
+        public void hideImeWithWindowInsetsController() {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+                return;
+            }
+            Log.i(TAG, "hideImeWithWIC()");
+            WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
+            assertWithMessage("WindowInsetsController shouldn't be null.")
+                    .that(windowInsetsController)
+                    .isNotNull();
+            windowInsetsController.hide(WindowInsets.Type.ime());
+        }
+
+        private InputMethodManager getInputMethodManager() {
+            return getSystemService(InputMethodManager.class);
+        }
+
+        public EditText getEditText() {
+            return mEditText;
+        }
+
+        /** Start TestActivity with intent. */
+        public static TestActivity start(Intent intent) {
+            Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+            intent.setAction(Intent.ACTION_MAIN)
+                    .setClass(instrumentation.getContext(), TestActivity.class)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            return (TestActivity) instrumentation.startActivitySync(intent);
+        }
+
+        /** Start the second TestActivity with intent. */
+        public TestActivity startSecondTestActivity(Intent intent) {
+            Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+            intent.setClass(TestActivity.this, TestActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return (TestActivity) instrumentation.startActivitySync(intent);
+        }
+
+        public void enableAnimationMonitoring() {
+            // Enable WindowInsetsAnimation.
+            // Note that this has a side effect of disabling InsetsAnimationThreadControlRunner.
+            InstrumentationRegistry.getInstrumentation()
+                    .runOnMainSync(
+                            () -> {
+                                getWindow().setDecorFitsSystemWindows(false);
+                                mEditText.setWindowInsetsAnimationCallback(
+                                        mWindowInsetsAnimationCallback);
+                            });
+        }
+
+        public boolean isAnimating() {
+            return mIsAnimating;
+        }
+
+        public void requestFocus() {
+            boolean requestFocusResult = getEditText().requestFocus();
+            if (requestFocusResult) {
+                Log.i(TAG, "Request focus successfully");
+            } else {
+                Log.i(TAG, "Request focus failed");
+            }
+        }
+    }
 }