diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 8b586ccb5f695..765672c00bb2e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -16,18 +16,28 @@ package com.android.server.inputmethod; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; + +import static com.android.server.inputmethod.InputMethodManagerService.MSG_INITIALIZE_IME; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManagerInternal; import android.os.IBinder; import android.os.Process; +import android.os.SystemClock; +import android.os.Trace; import android.util.ArrayMap; +import android.util.Slog; import android.view.inputmethod.InputMethodInfo; +import com.android.internal.inputmethod.UnbindReason; import com.android.internal.view.IInputMethod; +import com.android.server.inputmethod.InputMethodManagerService.ClientState; /** * A controller managing the state of the input method binding. @@ -39,6 +49,8 @@ final class InputMethodBindingController { @NonNull private final InputMethodManagerService mService; @NonNull private final Context mContext; @NonNull private final ArrayMap mMethodMap; + @NonNull private final InputMethodUtils.InputMethodSettings mSettings; + @NonNull private final PackageManagerInternal mPackageManagerInternal; private long mLastBindTime; private boolean mHasConnection; @@ -56,6 +68,8 @@ final class InputMethodBindingController { mService = service; mContext = mService.mContext; mMethodMap = mService.mMethodMap; + mSettings = mService.mSettings; + mPackageManagerInternal = mService.mPackageManagerInternal; } /** @@ -224,4 +238,84 @@ final class InputMethodBindingController { } }; + /** + * Used to bind the IME while it is not currently being shown. + */ + @NonNull + ServiceConnection getMainConnection() { + return mMainConnection; + } + + private final ServiceConnection mMainConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected"); + synchronized (mMethodMap) { + if (getCurIntent() != null && name.equals(getCurIntent().getComponent())) { + setCurMethod(IInputMethod.Stub.asInterface(service)); + final String curMethodPackage = + getCurIntent().getComponent().getPackageName(); + final int curMethodUid = mPackageManagerInternal.getPackageUid( + curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId()); + if (curMethodUid < 0) { + Slog.e(TAG, "Failed to get UID for package=" + curMethodPackage); + setCurMethodUid(Process.INVALID_UID); + } else { + setCurMethodUid(curMethodUid); + } + if (getCurToken() == null) { + Slog.w(TAG, "Service connected without a token!"); + mService.unbindCurrentMethodLocked(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + return; + } + if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + getCurToken()); + // Dispatch display id for InputMethodService to update context display. + mService.executeOrSendMessage(getCurMethod(), + mService.mCaller.obtainMessageIOO(MSG_INITIALIZE_IME, + mMethodMap.get(getSelectedMethodId()).getConfigChanges(), + getCurMethod(), getCurToken())); + mService.scheduleNotifyImeUidToAudioService(getCurMethodUid()); + ClientState curClient = mService.getCurClient(); + if (curClient != null) { + mService.clearClientSessionLocked(curClient); + mService.requestClientSessionLocked(curClient); + } + } + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + // Note that mContext.unbindService(this) does not trigger this. Hence if we are + // here the + // disconnection is not intended by IMMS (e.g. triggered because the current IMS + // crashed), + // which is irregular but can eventually happen for everyone just by continuing + // using the + // device. Thus it is important to make sure that all the internal states are + // properly + // refreshed when this method is called back. Running + // adb install -r + // would be a good way to trigger such a situation. + synchronized (mMethodMap) { + if (DEBUG) { + Slog.v(TAG, "Service disconnected: " + name + + " mCurIntent=" + getCurIntent()); + } + if (getCurMethod() != null && getCurIntent() != null + && name.equals(getCurIntent().getComponent())) { + mService.clearCurMethodLocked(); + // We consider this to be a new bind attempt, since the system + // should now try to restart the service for us. + setLastBindTime(SystemClock.uptimeMillis()); + mService.setShowRequested(mService.isInputShown()); + mService.setInputShown(false); + mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME); + } + } + } + }; + } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index a35687d579b33..05aebcc7b0a86 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -72,7 +72,6 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; @@ -374,7 +373,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ @NonNull private ServiceConnection getMainConnection() { - return mMainConnection; + return mBindingController.getMainConnection(); } // Ongoing notification @@ -2641,83 +2640,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @AnyThread - private void scheduleNotifyImeUidToAudioService(int uid) { + void scheduleNotifyImeUidToAudioService(int uid) { mCaller.removeMessages(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE); mCaller.obtainMessageI(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE, uid).sendToTarget(); } - private final ServiceConnection mMainConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected"); - synchronized (mMethodMap) { - if (getCurIntent() != null && name.equals(getCurIntent().getComponent())) { - setCurMethod(IInputMethod.Stub.asInterface(service)); - final String curMethodPackage = - getCurIntent().getComponent().getPackageName(); - final int curMethodUid = mPackageManagerInternal.getPackageUid( - curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId()); - if (curMethodUid < 0) { - Slog.e(TAG, "Failed to get UID for package=" + curMethodPackage); - setCurMethodUid(Process.INVALID_UID); - } else { - setCurMethodUid(curMethodUid); - } - if (getCurToken() == null) { - Slog.w(TAG, "Service connected without a token!"); - unbindCurrentMethodLocked(); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - return; - } - if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + getCurToken()); - // Dispatch display id for InputMethodService to update context display. - executeOrSendMessage(getCurMethod(), - mCaller.obtainMessageIOO(MSG_INITIALIZE_IME, - mMethodMap.get(getSelectedMethodId()).getConfigChanges(), - getCurMethod(), getCurToken())); - scheduleNotifyImeUidToAudioService(getCurMethodUid()); - if (mCurClient != null) { - clearClientSessionLocked(mCurClient); - requestClientSessionLocked(mCurClient); - } - } - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - // Note that mContext.unbindService(this) does not trigger this. Hence if we are - // here the - // disconnection is not intended by IMMS (e.g. triggered because the current IMS - // crashed), - // which is irregular but can eventually happen for everyone just by continuing - // using the - // device. Thus it is important to make sure that all the internal states are - // properly - // refreshed when this method is called back. Running - // adb install -r - // would be a good way to trigger such a situation. - synchronized (mMethodMap) { - if (DEBUG) { - Slog.v(TAG, "Service disconnected: " + name - + " mCurIntent=" + getCurIntent()); - } - if (getCurMethod() != null && getCurIntent() != null - && name.equals(getCurIntent().getComponent())) { - clearCurMethodLocked(); - // We consider this to be a new bind attempt, since the system - // should now try to restart the service for us. - setLastBindTime(SystemClock.uptimeMillis()); - mShowRequested = mInputShown; - mInputShown = false; - unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME); - } - } - } - - }; - void onSessionCreated(IInputMethod method, IInputMethodSession session, InputChannel channel) { synchronized (mMethodMap) {