diff options
10 files changed, 146 insertions, 18 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 52a79bae6324..52d61420b631 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -832,6 +832,14 @@ package android.hardware.soundtrigger { } +package android.inputmethodservice { + + public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService { + field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L + } + +} + package android.location { public final class GnssClock implements android.os.Parcelable { diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index e9de27456f97..0766917642e8 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -54,6 +54,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_VIEW_CLICKED = 115; private static final int DO_NOTIFY_IME_HIDDEN = 120; private static final int DO_REMOVE_IME_SURFACE = 130; + private static final int DO_FINISH_INPUT = 140; + @UnsupportedAppUsage HandlerCaller mCaller; @@ -141,6 +143,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.removeImeSurface(); return; } + case DO_FINISH_INPUT: { + mInputMethodSession.finishInput(); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -222,6 +228,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION)); } + @Override + public void finishInput() { + mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT)); + } private final class ImeInputEventReceiver extends InputEventReceiver implements InputMethodSession.EventCallback { private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ae260e16806f..4a5d831cd705 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -61,9 +61,12 @@ import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.annotation.UiContext; import android.app.ActivityManager; import android.app.Dialog; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; @@ -411,7 +414,29 @@ public class InputMethodService extends AbstractInputMethodService { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) int mTheme = 0; - + + /** + * Finish the {@link InputConnection} when the device becomes + * {@link android.os.PowerManager#isInteractive non-interactive}. + * + * <p> + * If enabled by the current {@link InputMethodService input method}, the current input + * connection will be {@link InputMethodService#onFinishInput finished} whenever the devices + * becomes non-interactive. + * + * <p> + * If not enabled, the current input connection will instead be silently deactivated when the + * devices becomes non-interactive, and an {@link InputMethodService#onFinishInput + * onFinishInput()} {@link InputMethodService#onStartInput onStartInput()} pair is dispatched + * when the device becomes interactive again. + * + * @hide + */ + @TestApi + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // This is a bug id. + LayoutInflater mInflater; TypedArray mThemeAttrs; @UnsupportedAppUsage @@ -2325,7 +2350,7 @@ public class InputMethodService extends AbstractInputMethodService { } void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { - if (!restarting) { + if (!restarting && mInputStarted) { doFinishInput(); } ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this); diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java index dbb669be1402..2db9ed1103fa 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java @@ -23,6 +23,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; import android.view.InputChannel; @@ -38,8 +39,8 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import com.android.internal.annotations.GuardedBy; -import com.android.internal.inputmethod.IMultiClientInputMethodSession; import com.android.internal.inputmethod.CancellationGroup; +import com.android.internal.inputmethod.IMultiClientInputMethodSession; import com.android.internal.os.SomeArgs; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.view.IInputContext; @@ -303,6 +304,12 @@ final class MultiClientInputMethodClientCallbackAdaptor { // no-op for multi-session reportNotSupported(); } + + @Override + public void finishInput() throws RemoteException { + // no-op for multi-session + reportNotSupported(); + } } private static final class MultiClientInputMethodSessionImpl diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index efc0bd2785f4..4a5c95f43d46 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -222,6 +222,25 @@ public final class ImeFocusController { } /** + * To handle the lifecycle of the input connection when the device interactivity state changed. + * (i.e. Calling IMS#onFinishInput when the device screen-off and Calling IMS#onStartInput + * when the device screen-on again). + */ + @UiThread + public void onInteractiveChanged(boolean interactive) { + final InputMethodManagerDelegate immDelegate = getImmDelegate(); + if (!immDelegate.isCurrentRootView(mViewRootImpl)) { + return; + } + if (interactive) { + final View focusedView = mViewRootImpl.mView.findFocus(); + onViewFocusChanged(focusedView, focusedView != null); + } else { + mDelegate.finishInputAndReportToIme(); + } + } + + /** * @param windowAttribute {@link WindowManager.LayoutParams} to be checked. * @return Whether the window is in local focus mode or not. */ @@ -256,6 +275,7 @@ public final class ImeFocusController { @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus); void finishInput(); + void finishInputAndReportToIme(); void closeCurrentIme(); void finishComposingText(); void setCurrentRootView(ViewRootImpl rootView); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 3c89a4bfad59..315d4461692c 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -599,6 +599,27 @@ public final class InputMethodManager { } /** + * Used by {@link ImeFocusController} to finish input connection and callback + * {@link InputMethodService#onFinishInput()}. + * + * This method is especially for when ImeFocusController received device screen-off event to + * ensure the entire finish input connection and the connection lifecycle callback to + * IME can be done for security concern. + */ + @Override + public void finishInputAndReportToIme() { + synchronized (mH) { + finishInputLocked(); + if (mCurMethod != null) { + try { + mCurMethod.finishInput(); + } catch (RemoteException e) { + } + } + } + } + + /** * Used by {@link ImeFocusController} to hide current input method editor. */ @Override @@ -849,12 +870,23 @@ public final class InputMethodManager { case MSG_SET_ACTIVE: { final boolean active = msg.arg1 != 0; final boolean fullscreen = msg.arg2 != 0; + final boolean reportToImeController = msg.obj != null && (boolean) msg.obj; if (DEBUG) { Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive); } synchronized (mH) { mActive = active; mFullscreenMode = fullscreen; + + // Report active state to ImeFocusController to handle IME input + // connection lifecycle callback when it allowed. + final ImeFocusController controller = getFocusController(); + final View rootView = mCurRootView != null ? mCurRootView.getView() : null; + if (controller != null && rootView != null && reportToImeController) { + rootView.post(() -> controller.onInteractiveChanged(active)); + return; + } + if (!active) { // Some other client has starting using the IME, so note // that this happened and make sure our own editor's @@ -1061,8 +1093,9 @@ public final class InputMethodManager { } @Override - public void setActive(boolean active, boolean fullscreen) { - mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0).sendToTarget(); + public void setActive(boolean active, boolean fullscreen, boolean reportToImeController) { + mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0, + reportToImeController).sendToTarget(); } @Override diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl index 1145f5183206..ec9a0a2f4801 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -25,7 +25,7 @@ import com.android.internal.view.InputBindResult; oneway interface IInputMethodClient { void onBindMethod(in InputBindResult res); void onUnbindMethod(int sequence, int unbindReason); - void setActive(boolean active, boolean fullscreen); + void setActive(boolean active, boolean fullscreen, boolean reportToImeController); void scheduleStartInputIfNecessary(boolean fullscreen); void reportFullscreenMode(boolean fullscreen); void applyImeVisibility(boolean setVisible); diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 0319e3637384..c6afd78ec04b 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -52,4 +52,6 @@ oneway interface IInputMethodSession { void notifyImeHidden(); void removeImeSurface(); + + void finishInput(); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index cfb5116c47f3..4331198daacf 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -15,6 +15,7 @@ package com.android.server.inputmethod; +import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -152,6 +153,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; +import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; @@ -709,6 +711,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ boolean mIsInteractive = true; + private IPlatformCompat mPlatformCompat; + int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** @@ -1670,6 +1674,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); + mPlatformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime); mIsLowRam = ActivityManager.isLowRamDeviceStatic(); @@ -2306,8 +2312,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO( - MSG_SET_ACTIVE, 0, 0, mCurClient)); + scheduleSetActiveToClient(mCurClient, false /* active */, false /* fullscreen */, + false /* reportToImeController */); executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO( MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client)); mCurClient.sessionRequested = false; @@ -2449,7 +2455,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // If the screen is on, inform the new client it is active if (mIsInteractive) { - executeOrSendMessage(cs.client, mCaller.obtainMessageIO(MSG_SET_ACTIVE, 1, cs)); + scheduleSetActiveToClient(cs, true /* active */, false /* fullscreen */, + false /* reportToImeController */); } } @@ -4452,15 +4459,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub args.recycle(); return true; } - case MSG_SET_ACTIVE: + case MSG_SET_ACTIVE: { + args = (SomeArgs) msg.obj; + final ClientState clientState = (ClientState) args.arg1; try { - ((ClientState)msg.obj).client.setActive(msg.arg1 != 0, msg.arg2 != 0); + clientState.client.setActive(args.argi1 != 0 /* active */, + args.argi2 != 0 /* fullScreen */, + args.argi3 != 0 /* reportToImeController */); } catch (RemoteException e) { Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid " - + ((ClientState)msg.obj).pid + " uid " - + ((ClientState)msg.obj).uid); + + clientState.pid + " uid " + clientState.uid); } + args.recycle(); return true; + } case MSG_SET_INTERACTIVE: handleSetInteractive(msg.arg1 != 0); return true; @@ -4546,13 +4558,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Inform the current client of the change in active status if (mCurClient != null && mCurClient.client != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO( - MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0, - mCurClient)); + boolean reportToImeController = false; + try { + reportToImeController = mPlatformCompat.isChangeEnabledByUid( + FINISH_INPUT_NO_FALLBACK_CONNECTION, mCurMethodUid); + } catch (RemoteException e) { + } + scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode, + reportToImeController); } } } + private void scheduleSetActiveToClient(ClientState state, boolean active, boolean fullscreen, + boolean reportToImeController) { + executeOrSendMessage(state.client, mCaller.obtainMessageIIIIO(MSG_SET_ACTIVE, + active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, 0, state)); + } + private boolean chooseNewDefaultIMELocked() { final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME( mSettings.getEnabledInputMethodListLocked()); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 7af27ca46f68..a3fb13593209 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1338,7 +1338,7 @@ public final class MultiClientInputMethodManagerService { switch (clientInfo.mState) { case InputMethodClientState.WAITING_FOR_IME_SESSION: try { - clientInfo.mClient.setActive(true, false); + clientInfo.mClient.setActive(true, false, false); } catch (RemoteException e) { // TODO(yukawa): Remove this client. return; @@ -1400,7 +1400,7 @@ public final class MultiClientInputMethodManagerService { return; } try { - clientInfo.mClient.setActive(active, false /* fullscreen */); + clientInfo.mClient.setActive(active, false /* fullscreen */, false); } catch (RemoteException e) { return; } |