From 982a94c5e3cce47d6c7ae4cdf7a4dbcdcc75431f Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Thu, 11 Aug 2016 19:16:02 -0700 Subject: [PATCH] Fix a race to detemine IME switcher visibilityde When the current user is switching to a user who requires password to unlock, there is a chance that InputMethodManagerService has not completed InputMethodManagerService#onSwitchUser() when the keyguard is calling InputMethodManager#getEnabledInputMethodList() to detemine whether the IME switcher icon should be shown or not. If this occurs, and if the previous user enables only one IME but the new user enables multiple IMEs, it's possible that the IME switcher icon is not shown when it should be. Although the user is still able to work around this by switch back to the previous user then the current user again until the icon shows up, or can rely on a way to switch to next IME (e.g. globe key) if it's available, the lack of the IME switcher is still concerning since it can prevent the user from unlocking the device. As a quick workaround for this situation, this CL introduces a delayed task to re-evaluate if we need to show the IME switcher icon on the keyguard or not (currently the delay is set to 500msec) until this race condition is fundamentally fixed. Bug: 30640917 Change-Id: I93ea71d73540c31fbbe1cc4bd6747871f957dcc6 --- .../com/android/keyguard/KeyguardPasswordView.java | 72 ++++++++++++++-------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java index b75f52987a92..ddccc14a60f0 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java @@ -42,16 +42,21 @@ import java.util.List; * Displays an alphanumeric (latin-1) key entry for the user to enter * an unlock password */ - public class KeyguardPasswordView extends KeyguardAbsKeyInputView implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { private final boolean mShowImeAtScreenOn; private final int mDisappearYTranslation; + // A delay constant to be used in a workaround for the situation where InputMethodManagerService + // is not switched to the new user yet. + // TODO: Remove this by ensuring such a race condition never happens. + private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms + InputMethodManager mImm; private TextView mPasswordEntry; private TextViewInputDisabler mPasswordEntryDisabler; + private View mSwitchImeButton; private Interpolator mLinearOutSlowInInterpolator; private Interpolator mFastOutLinearInInterpolator; @@ -141,12 +146,31 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mPasswordEntry.requestFocus(); } + private void updateSwitchImeButton() { + // If there's more than one IME, enable the IME switcher button + final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; + final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false); + if (wasVisible != shouldBeVisible) { + mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); + } + + // TODO: Check if we still need this hack. + // If no icon is visible, reset the start margin on the password field so the text is + // still centered. + if (mSwitchImeButton.getVisibility() != View.VISIBLE) { + android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); + if (params instanceof MarginLayoutParams) { + final MarginLayoutParams mlp = (MarginLayoutParams) params; + mlp.setMarginStart(0); + mPasswordEntry.setLayoutParams(params); + } + } + } + @Override protected void onFinishInflate() { super.onFinishInflate(); - boolean imeOrDeleteButtonVisible = false; - mImm = (InputMethodManager) getContext().getSystemService( Context.INPUT_METHOD_SERVICE); @@ -171,31 +195,29 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mPasswordEntry.requestFocus(); + mSwitchImeButton = findViewById(R.id.switch_ime_button); + mSwitchImeButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mCallback.userActivity(); // Leave the screen on a bit longer + // Do not show auxiliary subtypes in password lock screen. + mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */); + } + }); + // If there's more than one IME, enable the IME switcher button - View switchImeButton = findViewById(R.id.switch_ime_button); - if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) { - switchImeButton.setVisibility(View.VISIBLE); - imeOrDeleteButtonVisible = true; - switchImeButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mCallback.userActivity(); // Leave the screen on a bit longer - // Do not show auxiliary subtypes in password lock screen. - mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */); - } - }); - } + updateSwitchImeButton(); - // If no icon is visible, reset the start margin on the password field so the text is - // still centered. - if (!imeOrDeleteButtonVisible) { - android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); - if (params instanceof MarginLayoutParams) { - final MarginLayoutParams mlp = (MarginLayoutParams) params; - mlp.setMarginStart(0); - mPasswordEntry.setLayoutParams(params); + // When we the current user is switching, InputMethodManagerService sometimes has not + // switched internal state yet here. As a quick workaround, we check the keyboard state + // again. + // TODO: Remove this workaround by ensuring such a race condition never happens. + postDelayed(new Runnable() { + @Override + public void run() { + updateSwitchImeButton(); } - } + }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); } @Override -- 2.11.0