From fa6e0a80e20a4917b403fd330e01a9edd021aabc Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Thu, 23 Jul 2015 15:08:59 -0700 Subject: [PATCH] Don't rely on broadcast intent for waking up input method. Basically this is a copy of Iabef96921dd554ce3768fb18619cefc for InputMethodManagerService. As described in JavaDoc of Intent#ACTION_SCREEN_OFF and Intent#ACTION_SCREEN_ON, one can use those Intents to be notified when the device becomes non-interactive and interactive. IMMS has relied on them to enable and disable InputConnection between the IME and the application so as not to allow IMEs to update text when the user does not present. This is actually our design goal as documented in JavaDoc of InputMethodManager. An IME can never interact with an InputConnection while the screen is off. This is enforced by making all clients inactive while the screen is off, and prevents bad IMEs from driving the UI when the user can not be aware of its behavior. The goal of this CL is to improve the timeliness of above mechianism by introducing a direct communication channel from PowerManagerService to InputMethodManagerService via Notifier. Actually this is what InputManager has been doing since Iabef96921dd554ce3768fb18619cefc3230b5fb0. Reasons behind this change are: 1. There are several bugreports that imply those Intents can dispatch tens of seconds after it is enqueued. This is indeed problematic because the user cannot type password to unlock their devices until queued Intent#ACTION_SCREEN_ON is dispatched. This CL addresses such an issue without waiting for figuring out the root cause of the delay. 2. Intent#ACTION_SCREEN_OFF and Intent#ACTION_SCREEN_ON are sent as a ordered broadcast, which may not be suitable for tasks that require a certain level of timeliness, and what IMMS wants is to enable users to start typing immediately after the system. This CL was originally authored by Seigo Nonaka. Bug: 22423200 Bug: 22555778 Change-Id: I747c37ff6dd8f233faef43f2b5713a4320e848eb --- .../InputMethodManagerInternal.java | 30 ++++++++ .../server/InputMethodManagerService.java | 69 +++++++++++-------- .../com/android/server/power/Notifier.java | 4 ++ 3 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 core/java/android/view/inputmethod/InputMethodManagerInternal.java diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java new file mode 100644 index 0000000000000..c22127b65e2bf --- /dev/null +++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +/** + * Input method manager local system service interface. + * + * @hide Only for use within the system server. + */ +public interface InputMethodManagerInternal { + /** + * Called by the power manager to tell the input method manager whether it + * should start watching for wake events. + */ + public void setInteractive(boolean interactive); +} diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index dbe878183e929..64ee5f1a1037c 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -114,6 +114,7 @@ import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodManagerInternal; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import android.widget.ArrayAdapter; @@ -166,6 +167,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_UNBIND_METHOD = 3000; static final int MSG_BIND_METHOD = 3010; static final int MSG_SET_ACTIVE = 3020; + static final int MSG_SET_INTERACTIVE = 3030; static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040; static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000; @@ -394,9 +396,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub SessionState mEnabledSession; /** - * True if the screen is on. The value is true initially. + * True if the device is currently interactive with user. The value is true initially. */ - boolean mScreenOn = true; + boolean mIsInteractive = true; int mCurUserActionNotificationSequenceNumber = 0; @@ -492,30 +494,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } class ImmsBroadcastReceiver extends android.content.BroadcastReceiver { - private void updateActive() { - // Inform the current client of the change in active status - if (mCurClient != null && mCurClient.client != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient)); - } - } - @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - if (Intent.ACTION_SCREEN_ON.equals(action)) { - mScreenOn = true; - updateSystemUi(mCurToken, mImeWindowVis, mBackDisposition); - updateActive(); - return; - } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { - mScreenOn = false; - updateSystemUi(mCurToken, 0 /* vis */, mBackDisposition); - updateActive(); - return; - } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { hideInputMethodMenu(); - // No need to updateActive + // No need to update mIsInteractive return; } else if (Intent.ACTION_USER_ADDED.equals(action) || Intent.ACTION_USER_REMOVED.equals(action)) { @@ -817,8 +801,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mShowOngoingImeSwitcherForPhones = false; final IntentFilter broadcastFilter = new IntentFilter(); - broadcastFilter.addAction(Intent.ACTION_SCREEN_ON); - broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF); broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); broadcastFilter.addAction(Intent.ACTION_USER_ADDED); broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); @@ -938,6 +920,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } }, filter); + LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl(mHandler)); } private void resetDefaultImeLocked(Context context) { @@ -1379,9 +1362,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard); // If the screen is on, inform the new client it is active - if (mScreenOn) { + if (mIsInteractive) { executeOrSendMessage(cs.client, mCaller.obtainMessageIO( - MSG_SET_ACTIVE, mScreenOn ? 1 : 0, cs)); + MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, cs)); } } @@ -2851,6 +2834,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + ((ClientState)msg.obj).uid); } return true; + case MSG_SET_INTERACTIVE: + handleSetInteractive(msg.arg1 != 0); + return true; case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: { final int sequenceNumber = msg.arg1; final ClientState clientState = (ClientState)msg.obj; @@ -2874,6 +2860,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return false; } + private void handleSetInteractive(final boolean interactive) { + synchronized (mMethodMap) { + mIsInteractive = interactive; + updateSystemUiLocked(mCurToken, interactive ? mImeWindowVis : 0, mBackDisposition); + + // Inform the current client of the change in active status + if (mCurClient != null && mCurClient.client != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( + MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mCurClient)); + } + } + } + private boolean chooseNewDefaultIMELocked() { final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME( mSettings.getEnabledInputMethodListLocked()); @@ -3734,6 +3733,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private static final class LocalServiceImpl implements InputMethodManagerInternal { + @NonNull + private final Handler mHandler; + + LocalServiceImpl(@NonNull final Handler handler) { + mHandler = handler; + } + + @Override + public void setInteractive(boolean interactive) { + // Do everything in handler so as not to block the caller. + mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE, + interactive ? 1 : 0, 0)); + } + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -3784,7 +3799,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " mInputShown=" + mInputShown); p.println(" mCurUserActionNotificationSequenceNumber=" + mCurUserActionNotificationSequenceNumber); - p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn); + p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive); p.println(" mSettingsObserver=" + mSettingsObserver); p.println(" mSwitchingController:"); mSwitchingController.dump(p); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index c5ad7fed047d5..7108f4afb192e 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -48,6 +48,7 @@ import android.provider.Settings; import android.util.EventLog; import android.util.Slog; import android.view.WindowManagerPolicy; +import android.view.inputmethod.InputMethodManagerInternal; /** * Sends broadcasts about important power state changes. @@ -89,6 +90,7 @@ final class Notifier { private final WindowManagerPolicy mPolicy; private final ActivityManagerInternal mActivityManagerInternal; private final InputManagerInternal mInputManagerInternal; + private final InputMethodManagerInternal mInputMethodManagerInternal; private final NotifierHandler mHandler; private final Intent mScreenOnIntent; @@ -133,6 +135,7 @@ final class Notifier { mPolicy = policy; mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); + mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); mHandler = new NotifierHandler(looper); mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); @@ -314,6 +317,7 @@ final class Notifier { // Start input as soon as we start waking up or going to sleep. mInputManagerInternal.setInteractive(interactive); + mInputMethodManagerInternal.setInteractive(interactive); // Notify battery stats. try {