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

Commit 56fa6466 authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Fix IME insets not apply hidden when switching focus in diffrent users

Normally, the focus window will apply the IME visibility state to
WindowManager when the IME has applied it. But it would be too late
when switching IMEs in between different users. (Since the focused IME
will first unbind the service to switch to bind the next user of the
IME service, that wouldn't make the attached IME token validity check
in time)

As a result, we have to notify WM to apply IME visibility before
clearing the binding states in the first place.

Fix: 285821212
Test: manual as issue steps
  1. Launch youtube with personal profile
  2. Launch Gmail with work profile
  3. Split youtube with personal profile and gmail with work profile
   (The Work Profile app is split and shown upper)
  4. Typing something with personal profile app
  5. Click work profile app at blank space. (Non input box)
  6. Expects IME insets should be hidden without seeing the black area
Test: atest DefaultImeVisibilityApplierTest#\
              testApplyImeVisibility_hideImeWhenUnbinding
Change-Id: I0873ec065722e9e0b22dd5010d3415d3704cfd2d
parent d6d7f44c
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;

import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
import static com.android.server.inputmethod.InputMethodUtils.isSoftInputModeStateVisibleAllowed;

@@ -2360,6 +2361,28 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        }
    }

    /**
     * Called when {@link #resetCurrentMethodAndClientLocked(int)} invoked for clean-up states
     * before unbinding the current method.
     */
    @GuardedBy("ImfLock.class")
    void onUnbindCurrentMethodByReset() {
        final ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull(
                mCurFocusedWindow);
        if (winState != null && !winState.isRequestedImeVisible()
                && !mVisibilityStateComputer.isInputShown()) {
            // Normally, the focus window will apply the IME visibility state to
            // WindowManager when the IME has applied it. But it would be too late when
            // switching IMEs in between different users. (Since the focused IME will
            // first unbind the service to switch to bind the next user of the IME
            // service, that wouldn't make the attached IME token validity check in time)
            // As a result, we have to notify WM to apply IME visibility before clearing the
            // binding states in the first place.
            mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, mCurStatsToken,
                    STATE_HIDE_IME);
        }
    }

    /** {@code true} when a {@link ClientState} has attached from starting the input connection. */
    @GuardedBy("ImfLock.class")
    boolean hasAttachedClient() {
@@ -2831,6 +2854,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    @GuardedBy("ImfLock.class")
    void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
        setSelectedMethodIdLocked(null);
        // Callback before clean-up binding states.
        onUnbindCurrentMethodByReset();
        mBindingController.unbindCurrentMethod();
        unbindCurrentClientLocked(unbindClientReason);
    }
+25 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.inputmethodservice.InputMethodService.IME_ACTIVE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;

import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SOFT_INPUT;
import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SWITCH_USER;
import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_SOFT_INPUT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
@@ -43,6 +44,7 @@ import android.view.inputmethod.InputMethodManager;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
@@ -165,6 +167,29 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
        verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY));
    }

    @Test
    public void testApplyImeVisibility_hideImeWhenUnbinding() {
        mInputMethodManagerService.setAttachedClientForTesting(null);
        startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        ExtendedMockito.spyOn(mVisibilityApplier);

        synchronized (ImfLock.class) {
            // Simulate the system hides the IME when switching IME services in different users.
            // (e.g. unbinding the IME from the current user to the profile user)
            final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
            mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, null, 0, null,
                    HIDE_SWITCH_USER);
            mInputMethodManagerService.onUnbindCurrentMethodByReset();

            // Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing
            // the IME hidden state.
            verify(mVisibilityApplier).applyImeVisibility(eq(mWindowToken), any(),
                    eq(STATE_HIDE_IME));
            verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
                    eq(mWindowToken), eq(displayIdToShowIme), eq(null));
        }
    }

    private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) {
        return mInputMethodManagerService.startInputOrWindowGainedFocus(
                StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,