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

Commit 65b8a795 authored by Antonio Kantek's avatar Antonio Kantek
Browse files

Move mImeWindowVis and mBackDisposition to binding controller

Make mImeWindowVis and mBackDisposition per user by moving it to
binding controller.

This code change is an internal refactoring and should not cause
any noticeable issues on surfaces with non-concurrent multi-user IME
enabled.

Bug: 325515685
Test: atest FrameworksInputMethodSystemServerTests
Test: atest CtsInputMethodTestCases
Test: atest FrameworksServicesTests
Test: atest --host FrameworksInputMethodSystemServerTestsRavenwood
Flag: EXEMPT refactor
Change-Id: I11a7905cd4fd08a21e5d29da0ee9ded17f081abd
parent 9d1ee9be
Loading
Loading
Loading
Loading
+47 −1
Original line number Diff line number Diff line
@@ -96,6 +96,32 @@ final class InputMethodBindingController {
    @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY;
    @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;

    /**
     * A set of status bits regarding the active IME.
     *
     * <p>This value is a combination of following two bits:</p>
     * <dl>
     * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
     * <dd>
     *   If this bit is ON, connected IME is ready to accept touch/key events.
     * </dd>
     * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
     * <dd>
     *   If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
     * </dd>
     * <dt>{@link InputMethodService#IME_INVISIBLE}</dt>
     * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
     *    currently invisible.
     * </dd>
     * </dl>
     * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
     * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
     */
    @GuardedBy("ImfLock.class") private int mImeWindowVis;

    @GuardedBy("ImfLock.class")
    private int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;

    @Nullable private CountDownLatch mLatchForTesting;

    /**
@@ -473,7 +499,7 @@ final class InputMethodBindingController {

        if (getCurToken() != null) {
            removeCurrentToken();
            mService.resetSystemUiLocked();
            mService.resetSystemUiLocked(this);
            mAutofillController.onResetSystemUi();
        }

@@ -662,4 +688,24 @@ final class InputMethodBindingController {
    int getUserId() {
        return mUserId;
    }

    @GuardedBy("ImfLock.class")
    void setImeWindowVis(int imeWindowVis) {
        mImeWindowVis = imeWindowVis;
    }

    @GuardedBy("ImfLock.class")
    int getImeWindowVis() {
        return mImeWindowVis;
    }

    @GuardedBy("ImfLock.class")
    int getBackDisposition() {
        return mBackDisposition;
    }

    @GuardedBy("ImfLock.class")
    void setBackDisposition(int backDisposition) {
        mBackDisposition = backDisposition;
    }
}
+47 −61
Original line number Diff line number Diff line
@@ -552,33 +552,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
    @MultiUserUnawareField
    boolean mIsInteractive = true;

    @MultiUserUnawareField
    int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;

    /**
     * A set of status bits regarding the active IME.
     *
     * <p>This value is a combination of following two bits:</p>
     * <dl>
     * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
     * <dd>
     *   If this bit is ON, connected IME is ready to accept touch/key events.
     * </dd>
     * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
     * <dd>
     *   If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
     * </dd>
     * <dt>{@link InputMethodService#IME_INVISIBLE}</dt>
     * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
     *    currently invisible.
     * </dd>
     * </dl>
     * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
     * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
     */
    @MultiUserUnawareField
    int mImeWindowVis;

    @SharedByAllUsersField
    private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();

@@ -936,7 +909,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                            // Uh oh, current input method is no longer around!
                            // Pick another one...
                            Slog.i(TAG, "Current input method removed: " + curInputMethodId);
                            updateSystemUiLocked(0 /* vis */, mBackDisposition);
                            final var bindingController = getInputMethodBindingController(userId);
                            updateSystemUiLocked(0 /* vis */,
                                    bindingController.getBackDisposition(), userId);
                            if (!chooseNewDefaultIMELocked()) {
                                changed = true;
                                curIm = null;
@@ -1412,7 +1387,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                mStatusBarManagerInternal =
                        LocalServices.getService(StatusBarManagerInternal.class);
                hideStatusBarIconLocked();
                updateSystemUiLocked(mImeWindowVis, mBackDisposition);
                final var bindingController = getInputMethodBindingController(currentUserId);
                updateSystemUiLocked(bindingController.getImeWindowVis(),
                        bindingController.getBackDisposition(), currentUserId);
                mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
                        com.android.internal.R.bool.show_ongoing_ime_switcher);
                if (mShowOngoingImeSwitcherForPhones) {
@@ -2426,11 +2403,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
    }

    @GuardedBy("ImfLock.class")
    void resetSystemUiLocked() {
    void resetSystemUiLocked(InputMethodBindingController bindingController) {
        // Set IME window status as invisible when unbinding current method.
        mImeWindowVis = 0;
        mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
        updateSystemUiLocked(mImeWindowVis, mBackDisposition);
        final int imeWindowVis = 0;
        final int backDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
        bindingController.setImeWindowVis(imeWindowVis);
        bindingController.setBackDisposition(backDisposition);
        updateSystemUiLocked(imeWindowVis, backDisposition, bindingController.getUserId());
    }

    @GuardedBy("ImfLock.class")
@@ -2544,7 +2523,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                    sessionState.mSession.finishSession();
                } catch (RemoteException e) {
                    Slog.w(TAG, "Session failed to close due to remote exception", e);
                    updateSystemUiLocked(0 /* vis */, mBackDisposition);
                    // TODO(b/350386877): Propagate userId from the caller or infer it from
                    //  sessionState
                    final int userId = mCurrentUserId;
                    final var bindingController = getInputMethodBindingController(userId);
                    updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(),
                            userId);
                }
                sessionState.mSession = null;
            }
@@ -2764,8 +2748,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
            if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) {
                return;
            }
            mImeWindowVis = vis;
            mBackDisposition = backDisposition;
            bindingController.setImeWindowVis(vis);
            bindingController.setBackDisposition(backDisposition);
            updateSystemUiLocked(vis, backDisposition, userId);
        }

@@ -2802,23 +2786,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.

    private void updateImeWindowStatus(boolean disableImeIcon) {
        synchronized (ImfLock.class) {
            // TODO(b/350386877): Propagate userId from the caller.
            final int userId = mCurrentUserId;
            if (disableImeIcon) {
                updateSystemUiLocked(0, mBackDisposition);
                final var bindingController = getInputMethodBindingController(userId);
                updateSystemUiLocked(0, bindingController.getBackDisposition(), userId);
            } else {
                updateSystemUiLocked();
                updateSystemUiLocked(userId);
            }
        }
    }

    @GuardedBy("ImfLock.class")
    void updateSystemUiLocked() {
        updateSystemUiLocked(mImeWindowVis, mBackDisposition);
    }

    // Caution! This method is called in this class. Handle multi-user carefully
    @GuardedBy("ImfLock.class")
    private void updateSystemUiLocked(int vis, int backDisposition) {
        updateSystemUiLocked(vis, backDisposition, mCurrentUserId);
    void updateSystemUiLocked(@UserIdInt int userId) {
        final var bindingController = getInputMethodBindingController(userId);
        updateSystemUiLocked(bindingController.getImeWindowVis(),
                bindingController.getBackDisposition(), userId);
    }

    @GuardedBy("ImfLock.class")
@@ -3068,7 +3052,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true, userId);
                IInputMethodInvoker curMethod = bindingController.getCurMethod();
                if (curMethod != null) {
                    updateSystemUiLocked(mImeWindowVis, mBackDisposition);
                    updateSystemUiLocked(bindingController.getImeWindowVis(),
                            bindingController.getBackDisposition(), userId);
                    curMethod.changeInputMethodSubtype(newSubtype);
                }
            }
@@ -3455,7 +3440,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                    return;
                }
                mFocusedWindowPerceptible.put(windowToken, windowPerceptible);
                updateSystemUiLocked();
                updateSystemUiLocked(mCurrentUserId);
            }
        });
    }
@@ -3621,29 +3606,30 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
    boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
            @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver,
            @SoftInputShowHideReason int reason, @UserIdInt int userId) {
        final var bindingController = getInputMethodBindingController(userId);
        if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) {
            return false;
        }

        // There is a chance that IMM#hideSoftInput() is called in a transient state where
        // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
        // to be updated with the new value sent from IME process.  Even in such a transient state
        // historically we have accepted an incoming call of IMM#hideSoftInput() from the
        // IMMS#InputShown is already updated to be true whereas the user's ImeWindowVis is still
        // waiting to be updated with the new value sent from IME process.  Even in such a transient
        // state historically we have accepted an incoming call of IMM#hideSoftInput() from the
        // application process as a valid request, and have even promised such a behavior with CTS
        // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
        // IMMS#InputShown indicates that the software keyboard is shown.
        // TODO(b/246309664): Clean up IMMS#mImeWindowVis
        final var bindingController = getInputMethodBindingController(userId);
        final var userData = getUserData(userId);
        IInputMethodInvoker curMethod = bindingController.getCurMethod();
        final boolean shouldHideSoftInput = curMethod != null
                && (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
                && (isInputShownLocked()
                || (bindingController.getImeWindowVis() & InputMethodService.IME_ACTIVE) != 0);

        mVisibilityStateComputer.requestImeVisibility(windowToken, false);
        if (shouldHideSoftInput) {
            // The IME will report its visible state again after the following message finally
            // delivered to the IME process as an IPC.  Hence the inconsistency between
            // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
            // IMMS#mInputShown and the user's ImeWindowVis should be resolved spontaneously in
            // the final state.
            ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE);
            mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason,
@@ -4633,8 +4619,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
            proto.write(HAVE_CONNECTION, bindingController.hasMainConnection());
            proto.write(BOUND_TO_METHOD, userData.mBoundToMethod);
            proto.write(IS_INTERACTIVE, mIsInteractive);
            proto.write(BACK_DISPOSITION, mBackDisposition);
            proto.write(IME_WINDOW_VISIBILITY, mImeWindowVis);
            proto.write(BACK_DISPOSITION, bindingController.getBackDisposition());
            proto.write(IME_WINDOW_VISIBILITY, bindingController.getImeWindowVis());
            proto.write(SHOW_IME_WITH_HARD_KEYBOARD, mMenuController.getShowImeWithHardKeyboard());
            proto.end(token);
        }
@@ -5143,18 +5129,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.

    private void handleSetInteractive(final boolean interactive) {
        synchronized (ImfLock.class) {
            mIsInteractive = interactive;
            updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);

            // TODO(b/305849394): Support multiple IMEs.
            final var userId = mCurrentUserId;
            final int userId = mCurrentUserId;
            final var bindingController = getInputMethodBindingController(userId);
            mIsInteractive = interactive;
            updateSystemUiLocked(
                    interactive ? bindingController.getImeWindowVis() : 0,
                    bindingController.getBackDisposition(), userId);
            final var userData = getUserData(userId);
            // Inform the current client of the change in active status
            if (userData.mCurClient == null || userData.mCurClient.mClient == null) {
                return;
            }
            // TODO(b/325515685): user data must be retrieved by a userId parameter
            final var bindingController = getInputMethodBindingController(mCurrentUserId);
            if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(
                    bindingController.getCurMethodUid())) {
                // Handle IME visibility when interactive changed before finishing the input to
+4 −2
Original line number Diff line number Diff line
@@ -201,7 +201,7 @@ final class InputMethodMenuController {
        attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
        attrs.setTitle("Select input method");
        w.setAttributes(attrs);
        mService.updateSystemUiLocked();
        mService.updateSystemUiLocked(userId);
        mService.sendOnNavButtonFlagsChangedLocked();
        mSwitchingDialog.show();
    }
@@ -239,7 +239,9 @@ final class InputMethodMenuController {
            mSwitchingDialog = null;
            mSwitchingDialogTitleView = null;

            mService.updateSystemUiLocked();
            // TODO(b/305849394): Make InputMethodMenuController multi-user aware
            final int userId = mService.getCurrentImeUserIdLocked();
            mService.updateSystemUiLocked(userId);
            mService.sendOnNavButtonFlagsChangedLocked();
            mDialogBuilder = null;
            mIms = null;
+7 −2
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import static java.util.Objects.requireNonNull;

@@ -134,8 +135,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe

    @Test
    public void testApplyImeVisibility_hideImeExplicit() throws Exception {
        mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
        synchronized (ImfLock.class) {
            final var bindingController =
                    mInputMethodManagerService.getInputMethodBindingController(mUserId);
            when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE);
            mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
                    STATE_HIDE_IME_EXPLICIT, eq(SoftInputShowHideReason.NOT_SET), mUserId);
        }
@@ -144,8 +147,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe

    @Test
    public void testApplyImeVisibility_hideNotAlways() throws Exception {
        mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
        synchronized (ImfLock.class) {
            final var bindingController =
                    mInputMethodManagerService.getInputMethodBindingController(mUserId);
            when(bindingController.getImeWindowVis()).thenReturn(IME_ACTIVE);
            mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
                    STATE_HIDE_IME_NOT_ALWAYS, eq(SoftInputShowHideReason.NOT_SET), mUserId);
        }