Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5aefade7 authored by Cosmin Băieș's avatar Cosmin Băieș
Browse files

Restore initial config and await secure setting

Some tests in InputMethodServiceTest manually modify the IME's
configuration to setup various hardware keyboard scenarios. However,
the configuration is not reset at the end of the test, and the values
persist across test runs in this class. This wraps the tests in
try-finally blocks and restore the initial config.

Aditionally, some tests rely on setting the show_ime_with_hard_keyboard
value, but don't wait for this to be applied. This adds a check that
eventually the new value takes effect. To make this more efficient, a
test API is added to IME to directly read the value of the secure
setting as cached by the IME.

Flag: EXEMPT testfix
Bug: 394328311
Test: atest InputMethodServiceTest
Change-Id: Id97df01a5603ed8ed1201da4d7ecde6ffb44650a
parent eeccf514
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -1725,9 +1725,21 @@ public class InputMethodService extends AbstractInputMethodService {
            return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard  + "}";
        }
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private SettingsObserver mSettingsObserver;

    /**
     * Checks whether the IME should be shown when a hardware keyboard is connected, as configured
     * through {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}, for testing purposes only.
     *
     * @hide
     */
    @VisibleForTesting
    public final boolean getShouldShowImeWithHardKeyboardForTesting() {
        return mSettingsObserver.shouldShowImeWithHardKeyboard();
    }

    /**
     * You can call this to customize the theme used by your IME's window.
     * This theme should typically be one that derives from
@@ -4454,7 +4466,7 @@ public class InputMethodService extends AbstractInputMethodService {
     *
     * @hide
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @VisibleForTesting
    public final boolean isImeNavigationBarShownForTesting() {
        return mNavigationBarController.isShown();
    }
+216 −174
Original line number Diff line number Diff line
@@ -134,19 +134,18 @@ public class InputMethodServiceTest {
                    .that(mInputMethodService.getCurrentInputViewStarted()).isFalse();
        });
        // Save the original value of show_ime_with_hard_keyboard from Settings.
        mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt(
                mInputMethodService.getContentResolver(),
                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
        mShowImeWithHardKeyboardEnabled =
                mInputMethodService.getShouldShowImeWithHardKeyboardForTesting();
    }

    @After
    public void tearDown() throws Exception {
        mUiDevice.unfreezeRotation();
        executeShellCommand("ime disable " + mInputMethodId);
        // Change back the original value of show_ime_with_hard_keyboard in Settings.
        executeShellCommand(mShowImeWithHardKeyboardEnabled
                ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
                : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
        executeShellCommand("ime disable " + mInputMethodId);
    }

    /**
@@ -321,26 +320,30 @@ public class InputMethodServiceTest {
    public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() {
        setShowImeWithHardKeyboard(true /* enabled */);

        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_NO;
        eventually(() -> assertWithMessage("InputView should show with visible hardware keyboard")
        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
            eventually(() ->
                    assertWithMessage("InputView should show with visible hardware keyboard")
                            .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());

        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_NOKEYS;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_NO;
        eventually(() -> assertWithMessage("InputView should show without hardware keyboard")
            config.keyboard = Configuration.KEYBOARD_NOKEYS;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
            eventually(() ->
                    assertWithMessage("InputView should show without hardware keyboard")
                            .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());

        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
        eventually(() -> assertWithMessage("InputView should show with hidden hardware keyboard")
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
            eventually(() ->
                    assertWithMessage("InputView should show with hidden hardware keyboard")
                         .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -351,27 +354,30 @@ public class InputMethodServiceTest {
    public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() {
        setShowImeWithHardKeyboard(false /* enabled */);

        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_NO;
        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
            eventually(() ->
                    assertWithMessage("InputView should not show with visible hardware keyboard")
                            .that(mInputMethodService.onEvaluateInputViewShown()).isFalse());

        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_NOKEYS;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_NO;
        eventually(() -> assertWithMessage("InputView should show without hardware keyboard")
            config.keyboard = Configuration.KEYBOARD_NOKEYS;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
            eventually(() ->
                    assertWithMessage("InputView should show without hardware keyboard")
                            .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());

        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
        eventually(() -> assertWithMessage("InputView should show with hidden hardware keyboard")
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
            eventually(() ->
                    assertWithMessage("InputView should show with hidden hardware keyboard")
                            .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -382,27 +388,34 @@ public class InputMethodServiceTest {
    public void testShowSoftInput_disableShowImeWithHardKeyboard() {
        setShowImeWithHardKeyboard(false /* enabled */);

        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            // Simulate connecting a hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_NO;
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;

        // When InputMethodService#onEvaluateInputViewShown() returns false, the IME should not be
        // shown no matter what the show flag is.
            // When InputMethodService#onEvaluateInputViewShown() returns false, the IME should
            // not be shown no matter what the show flag is.
            verifyInputViewStatusOnMainSync(() -> assertThat(
                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
                            mActivity.showImeWithInputMethodManager(
                                    InputMethodManager.SHOW_IMPLICIT)).isTrue(),
                    false /* expected */,
                    false /* inputViewStarted */);
            assertWithMessage("IME is not shown after SHOW_IMPLICIT")
                    .that(mInputMethodService.isInputViewShown()).isFalse();

            verifyInputViewStatusOnMainSync(
                () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
                    () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */))
                            .isTrue(),
                    false /* expected */,
                    false /* inputViewStarted */);
            assertWithMessage("IME is not shown after SHOW_EXPLICIT")
                    .that(mInputMethodService.isInputViewShown()).isFalse();
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -509,17 +522,22 @@ public class InputMethodServiceTest {
    public void testShowSoftInputExplicitly_withHardKeyboard() {
        setShowImeWithHardKeyboard(false /* enabled */);

        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            // Simulate connecting a hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;

            verifyInputViewStatusOnMainSync(() -> assertThat(
                            mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
                    true /* expected */,
                    true /* inputViewStarted */);
            assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue();
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -535,18 +553,24 @@ public class InputMethodServiceTest {
    public void testShowSoftInputImplicitly_withHardKeyboard() {
        setShowImeWithHardKeyboard(false /* enabled */);

        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            // Simulate connecting a hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;

            verifyInputViewStatusOnMainSync(() ->assertThat(
                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
                    mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT))
                            .isTrue(),
                    false /* expected */,
                    false /* inputViewStarted */);
            assertWithMessage("IME is not shown")
                    .that(mInputMethodService.isInputViewShown()).isFalse();
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -557,34 +581,37 @@ public class InputMethodServiceTest {
    public void testShowSoftInputExplicitly_thenConfigurationChanged() {
        setShowImeWithHardKeyboard(false /* enabled */);

        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            // Start with no hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_NOKEYS;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
            config.keyboard = Configuration.KEYBOARD_NOKEYS;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;

            verifyInputViewStatusOnMainSync(
                () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
                    () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */))
                            .isTrue(),
                    true /* expected */,
                    true /* inputViewStarted */);
            assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue();

            // Simulate connecting a hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;

        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
        mInputMethodService.getResources().getConfiguration().orientation =
                Configuration.ORIENTATION_LANDSCAPE;
            // Simulate a fake configuration change to avoid the recreation of TestActivity.
            config.orientation = Configuration.ORIENTATION_LANDSCAPE;

        verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
                mInputMethodService.getResources().getConfiguration()),
            verifyInputViewStatusOnMainSync(
                    () -> mInputMethodService.onConfigurationChanged(config),
                    true /* expected */,
                    true /* inputViewStarted */);
            assertWithMessage("IME is still shown after a configuration change")
                    .that(mInputMethodService.isInputViewShown()).isTrue();
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -601,39 +628,43 @@ public class InputMethodServiceTest {
    public void testShowSoftInputImplicitly_thenConfigurationChanged() {
        setShowImeWithHardKeyboard(false /* enabled */);

        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            // Start with no hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_NOKEYS;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
            config.keyboard = Configuration.KEYBOARD_NOKEYS;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;

            verifyInputViewStatusOnMainSync(() -> assertThat(
                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
                            mActivity.showImeWithInputMethodManager(
                                    InputMethodManager.SHOW_IMPLICIT)).isTrue(),
                    true /* expected */,
                    true /* inputViewStarted */);
            assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue();

            // Simulate connecting a hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.HARDKEYBOARDHIDDEN_YES;

        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
        mInputMethodService.getResources().getConfiguration().orientation =
                Configuration.ORIENTATION_LANDSCAPE;

        // Normally, IMS#onFinishInputView will be called when finishing the input view by the user.
        // But if IMS#hideWindow is called when receiving a new configuration change, we don't
        // expect that it's user-driven to finish the lifecycle of input view with
        // IMS#onFinishInputView, because the input view will be re-initialized according to the
        // last #mShowInputRequested state. So in this case we treat the input view as still alive.
        verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
                mInputMethodService.getResources().getConfiguration()),
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;

            // Simulate a fake configuration change to avoid the recreation of TestActivity.
            config.orientation = Configuration.ORIENTATION_LANDSCAPE;

            // Normally, IMS#onFinishInputView will be called when finishing the input view by
            // the user. But if IMS#hideWindow is called when receiving a new configuration change,
            // we don't expect that it's user-driven to finish the lifecycle of input view with
            // IMS#onFinishInputView, because the input view will be re-initialized according
            // to the last #mShowInputRequested state. So in this case we treat the input view as
            // still alive.
            verifyInputViewStatusOnMainSync(
                    () -> mInputMethodService.onConfigurationChanged(config),
                    true /* expected */,
                    true /* inputViewStarted */);
            assertWithMessage("IME is not shown after a configuration change")
                    .that(mInputMethodService.isInputViewShown()).isFalse();
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -645,11 +676,12 @@ public class InputMethodServiceTest {
    public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard() {
        setShowImeWithHardKeyboard(false /* enabled */);

        final var config = mInputMethodService.getResources().getConfiguration();
        final var initialConfig = new Configuration(config);
        try {
            // Simulate connecting a hardware keyboard.
        mInputMethodService.getResources().getConfiguration().keyboard =
                Configuration.KEYBOARD_QWERTY;
        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                Configuration.HARDKEYBOARDHIDDEN_YES;
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;

            // Explicit show request.
            verifyInputViewStatusOnMainSync(() -> assertThat(
@@ -660,21 +692,26 @@ public class InputMethodServiceTest {

            // Implicit show request.
            verifyInputViewStatusOnMainSync(() -> assertThat(
                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
                            mActivity.showImeWithInputMethodManager(
                                    InputMethodManager.SHOW_IMPLICIT)).isTrue(),
                    false /* expected */,
                    true /* inputViewStarted */);
            assertWithMessage("IME is still shown")
                    .that(mInputMethodService.isInputViewShown()).isTrue();

        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
            // Simulate a fake configuration change to avoid the recreation of TestActivity.
            // This should now consider the implicit show request, but keep the state from the
            // explicit show request, and thus not hide the IME.
        verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
                        mInputMethodService.getResources().getConfiguration()),
            verifyInputViewStatusOnMainSync(
                    () -> mInputMethodService.onConfigurationChanged(config),
                    true /* expected */,
                    true /* inputViewStarted */);
            assertWithMessage("IME is still shown after a configuration change")
                    .that(mInputMethodService.isInputViewShown()).isTrue();
        } finally {
            mInputMethodService.getResources()
                    .updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
        }
    }

    /**
@@ -1112,16 +1149,21 @@ public class InputMethodServiceTest {
    }

    /**
     * Sets the value of show_ime_with_hard_keyboard, only if it is different to the default value.
     * Sets the value of {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}, only if it is
     * different to the current value, and waits for the update to take effect.
     *
     * @param enabled the value to be set.
     * @param enable the value to be set.
     */
    private void setShowImeWithHardKeyboard(boolean enabled) {
        if (mShowImeWithHardKeyboardEnabled != enabled) {
            executeShellCommand(enabled
    private void setShowImeWithHardKeyboard(boolean enable) {
        final boolean currentEnabled =
                mInputMethodService.getShouldShowImeWithHardKeyboardForTesting();
        if (currentEnabled != enable) {
            executeShellCommand(enable
                    ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
                    : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
            mInstrumentation.waitForIdleSync();
            eventually(() -> assertWithMessage("showImeWithHardKeyboard updated")
                    .that(mInputMethodService.getShouldShowImeWithHardKeyboardForTesting())
                    .isEqualTo(enable));
        }
    }