Loading core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java +37 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ final class IInputMethodManagerGlobalInvoker { @Nullable private static volatile IImeTracker sTrackerServiceCache = null; private static int sCurStartInputSeq = 0; /** * @return {@code true} if {@link IInputMethodManager} is available. Loading Loading @@ -327,6 +328,7 @@ final class IInputMethodManagerGlobalInvoker { } } // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled. @AnyThread @NonNull @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) Loading @@ -353,6 +355,41 @@ final class IInputMethodManagerGlobalInvoker { } } /** * Returns a sequence number for startInput. */ @AnyThread @NonNull @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) static int startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason, @NonNull IInputMethodClient client, @Nullable IBinder windowToken, @StartInputFlags int startInputFlags, @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo, @Nullable IRemoteInputConnection remoteInputConnection, @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, int unverifiedTargetSdkVersion, @UserIdInt int userId, @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { final IInputMethodManager service = getService(); if (service == null) { return -1; } try { service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken, startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection, remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId, imeDispatcher, advanceAngGetStartInputSequenceNumber()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return sCurStartInputSeq; } private static int advanceAngGetStartInputSequenceNumber() { return ++sCurStartInputSeq; } @AnyThread static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client, int auxiliarySubtypeMode) { Loading core/java/android/view/inputmethod/InputMethodManager.java +125 −8 Original line number Diff line number Diff line Loading @@ -320,6 +320,22 @@ public final class InputMethodManager { } }; /** * A runnable that reports {@link InputConnection} opened event for calls to * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}. */ private abstract static class ReportInputConnectionOpenedRunner implements Runnable { /** * Sequence number to track startInput requests to * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync} */ int mSequenceNum; ReportInputConnectionOpenedRunner(int sequenceNum) { this.mSequenceNum = sequenceNum; } } private ReportInputConnectionOpenedRunner mReportInputConnectionOpenedRunner; /** * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly * or indirectly relied on {@link #sInstance} via reflection or something like that. Loading Loading @@ -691,6 +707,7 @@ public final class InputMethodManager { private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12; private static final int MSG_SET_INTERACTIVE = 13; private static final int MSG_ON_SHOW_REQUESTED = 31; private static final int MSG_START_INPUT_RESULT = 40; /** * Calling this will invalidate Local stylus handwriting availability Cache which Loading Loading @@ -1071,6 +1088,60 @@ public final class InputMethodManager { startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0); return; } case MSG_START_INPUT_RESULT: { final InputBindResult res = (InputBindResult) msg.obj; final int startInputSeq = msg.arg1; if (res == null) { // IMMS logs .wtf already. return; } if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); synchronized (mH) { if (res.id != null) { updateInputChannelLocked(res.channel); mCurMethod = res.method; // for @UnsupportedAppUsage mCurBindState = new BindState(res); mAccessibilityInputMethodSession.clear(); if (res.accessibilitySessions != null) { for (int i = 0; i < res.accessibilitySessions.size(); i++) { IAccessibilityInputMethodSessionInvoker wrapper = IAccessibilityInputMethodSessionInvoker.createOrNull( res.accessibilitySessions.valueAt(i)); if (wrapper != null) { mAccessibilityInputMethodSession.append( res.accessibilitySessions.keyAt(i), wrapper); } } } mCurId = res.id; // for @UnsupportedAppUsage } else if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); } switch (res.result) { case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW: mRestartOnNextWindowFocus = true; mServedView = null; break; } if (mCompletions != null) { if (isImeSessionAvailableLocked()) { mCurBindState.mImeSession.displayCompletions(mCompletions); } } if (res != null && res.method != null && mServedView != null && mReportInputConnectionOpenedRunner != null && mReportInputConnectionOpenedRunner.mSequenceNum == startInputSeq) { mReportInputConnectionOpenedRunner.run(); } mReportInputConnectionOpenedRunner = null; } return; } case MSG_UNBIND: { final int sequence = msg.arg1; @UnbindReason Loading Loading @@ -1321,6 +1392,12 @@ public final class InputMethodManager { mH.obtainMessage(MSG_BIND, res).sendToTarget(); } @Override public void onStartInputResult(InputBindResult res, int startInputSeq) { mH.obtainMessage(MSG_START_INPUT_RESULT, startInputSeq, -1 /* unused */, res) .sendToTarget(); } @Override public void onBindAccessibilityService(InputBindResult res, int id) { mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget(); Loading Loading @@ -2010,6 +2087,7 @@ public final class InputMethodManager { mServedConnecting = false; clearConnectionLocked(); } mReportInputConnectionOpenedRunner = null; // Clear the back callbacks held by the ime dispatcher to avoid memory leaks. mImeDispatcher.clear(); } Loading Loading @@ -3080,6 +3158,18 @@ public final class InputMethodManager { final int targetUserId = editorInfo.targetInputMethodUser != null ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus"); int startInputSeq = -1; if (Flags.useZeroJankProxy()) { // async result delivered via MSG_START_INPUT_RESULT. startInputSeq = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocusAsync( startInputReason, mClient, windowGainingFocus, startInputFlags, softInputMode, windowFlags, editorInfo, servedInputConnection, servedInputConnection == null ? null : servedInputConnection.asIRemoteAccessibilityInputConnection(), view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, mImeDispatcher); } else { res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus( startInputReason, mClient, windowGainingFocus, startInputFlags, softInputMode, windowFlags, editorInfo, servedInputConnection, Loading @@ -3087,7 +3177,33 @@ public final class InputMethodManager { : servedInputConnection.asIRemoteAccessibilityInputConnection(), view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, mImeDispatcher); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (Flags.useZeroJankProxy()) { // Create a runnable for delayed notification to the app that the InputConnection is // initialized and ready for use. if (ic != null) { final int seqId = startInputSeq; mReportInputConnectionOpenedRunner = new ReportInputConnectionOpenedRunner(startInputSeq) { @Override public void run() { if (DEBUG) { Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view + ", ic=" + ic + ", editorInfo=" + editorInfo + ", handler=" + icHandler + ", startInputSeq=" + seqId); } reportInputConnectionOpened(ic, editorInfo, icHandler, view); } }; } else { mReportInputConnectionOpenedRunner = null; } return true; } if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res == null) { Log.wtf(TAG, "startInputOrWindowGainedFocus must not return" Loading Loading @@ -3118,6 +3234,7 @@ public final class InputMethodManager { } else if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); } switch (res.result) { case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW: mRestartOnNextWindowFocus = true; Loading core/java/com/android/internal/inputmethod/IInputMethodClient.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.internal.inputmethod.InputBindResult; */ oneway interface IInputMethodClient { void onBindMethod(in InputBindResult res); void onStartInputResult(in InputBindResult res, int startInputSeq); void onBindAccessibilityService(in InputBindResult res, int id); void onUnbindMethod(int sequence, int unbindReason); void onUnbindAccessibilityService(int sequence, int id); Loading core/java/com/android/internal/inputmethod/InputBindResult.java +1 −0 Original line number Diff line number Diff line Loading @@ -271,6 +271,7 @@ public final class InputBindResult implements Parcelable { public String toString() { return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id + " sequence=" + sequence + " result=" + result + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker + "}"; } Loading core/java/com/android/internal/view/IInputMethodManager.aidl +18 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,8 @@ interface IInputMethodManager { boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, in @nullable ImeTracker.Token statsToken, int flags, in @nullable ResultReceiver resultReceiver, int reason); // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled. // If windowToken is null, this just does startInput(). Otherwise this reports that a window // has gained focus, and if 'editorInfo' is non-null then also does startInput. // @NonNull Loading @@ -85,6 +87,21 @@ interface IInputMethodManager { int unverifiedTargetSdkVersion, int userId, in ImeOnBackInvokedDispatcher imeDispatcher); // If windowToken is null, this just does startInput(). Otherwise this reports that a window // has gained focus, and if 'editorInfo' is non-null then also does startInput. @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)") void startInputOrWindowGainedFocusAsync( /* @StartInputReason */ int startInputReason, in IInputMethodClient client, in @nullable IBinder windowToken, /* @StartInputFlags */ int startInputFlags, /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags, in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection, in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, int unverifiedTargetSdkVersion, int userId, in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq); void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode); Loading Loading @@ -156,6 +173,7 @@ interface IInputMethodManager { in String delegatePackageName, in String delegatorPackageName); // TODO(b/293640003): introduce a new API method to provide async way to return boolean. /** Accepts and starts a stylus handwriting session for the delegate view **/ boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId, in String delegatePackageName, in String delegatorPackageName, int flags); Loading Loading
core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java +37 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ final class IInputMethodManagerGlobalInvoker { @Nullable private static volatile IImeTracker sTrackerServiceCache = null; private static int sCurStartInputSeq = 0; /** * @return {@code true} if {@link IInputMethodManager} is available. Loading Loading @@ -327,6 +328,7 @@ final class IInputMethodManagerGlobalInvoker { } } // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled. @AnyThread @NonNull @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) Loading @@ -353,6 +355,41 @@ final class IInputMethodManagerGlobalInvoker { } } /** * Returns a sequence number for startInput. */ @AnyThread @NonNull @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) static int startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason, @NonNull IInputMethodClient client, @Nullable IBinder windowToken, @StartInputFlags int startInputFlags, @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo, @Nullable IRemoteInputConnection remoteInputConnection, @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, int unverifiedTargetSdkVersion, @UserIdInt int userId, @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { final IInputMethodManager service = getService(); if (service == null) { return -1; } try { service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken, startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection, remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId, imeDispatcher, advanceAngGetStartInputSequenceNumber()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return sCurStartInputSeq; } private static int advanceAngGetStartInputSequenceNumber() { return ++sCurStartInputSeq; } @AnyThread static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client, int auxiliarySubtypeMode) { Loading
core/java/android/view/inputmethod/InputMethodManager.java +125 −8 Original line number Diff line number Diff line Loading @@ -320,6 +320,22 @@ public final class InputMethodManager { } }; /** * A runnable that reports {@link InputConnection} opened event for calls to * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}. */ private abstract static class ReportInputConnectionOpenedRunner implements Runnable { /** * Sequence number to track startInput requests to * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync} */ int mSequenceNum; ReportInputConnectionOpenedRunner(int sequenceNum) { this.mSequenceNum = sequenceNum; } } private ReportInputConnectionOpenedRunner mReportInputConnectionOpenedRunner; /** * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly * or indirectly relied on {@link #sInstance} via reflection or something like that. Loading Loading @@ -691,6 +707,7 @@ public final class InputMethodManager { private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12; private static final int MSG_SET_INTERACTIVE = 13; private static final int MSG_ON_SHOW_REQUESTED = 31; private static final int MSG_START_INPUT_RESULT = 40; /** * Calling this will invalidate Local stylus handwriting availability Cache which Loading Loading @@ -1071,6 +1088,60 @@ public final class InputMethodManager { startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0); return; } case MSG_START_INPUT_RESULT: { final InputBindResult res = (InputBindResult) msg.obj; final int startInputSeq = msg.arg1; if (res == null) { // IMMS logs .wtf already. return; } if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); synchronized (mH) { if (res.id != null) { updateInputChannelLocked(res.channel); mCurMethod = res.method; // for @UnsupportedAppUsage mCurBindState = new BindState(res); mAccessibilityInputMethodSession.clear(); if (res.accessibilitySessions != null) { for (int i = 0; i < res.accessibilitySessions.size(); i++) { IAccessibilityInputMethodSessionInvoker wrapper = IAccessibilityInputMethodSessionInvoker.createOrNull( res.accessibilitySessions.valueAt(i)); if (wrapper != null) { mAccessibilityInputMethodSession.append( res.accessibilitySessions.keyAt(i), wrapper); } } } mCurId = res.id; // for @UnsupportedAppUsage } else if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); } switch (res.result) { case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW: mRestartOnNextWindowFocus = true; mServedView = null; break; } if (mCompletions != null) { if (isImeSessionAvailableLocked()) { mCurBindState.mImeSession.displayCompletions(mCompletions); } } if (res != null && res.method != null && mServedView != null && mReportInputConnectionOpenedRunner != null && mReportInputConnectionOpenedRunner.mSequenceNum == startInputSeq) { mReportInputConnectionOpenedRunner.run(); } mReportInputConnectionOpenedRunner = null; } return; } case MSG_UNBIND: { final int sequence = msg.arg1; @UnbindReason Loading Loading @@ -1321,6 +1392,12 @@ public final class InputMethodManager { mH.obtainMessage(MSG_BIND, res).sendToTarget(); } @Override public void onStartInputResult(InputBindResult res, int startInputSeq) { mH.obtainMessage(MSG_START_INPUT_RESULT, startInputSeq, -1 /* unused */, res) .sendToTarget(); } @Override public void onBindAccessibilityService(InputBindResult res, int id) { mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget(); Loading Loading @@ -2010,6 +2087,7 @@ public final class InputMethodManager { mServedConnecting = false; clearConnectionLocked(); } mReportInputConnectionOpenedRunner = null; // Clear the back callbacks held by the ime dispatcher to avoid memory leaks. mImeDispatcher.clear(); } Loading Loading @@ -3080,6 +3158,18 @@ public final class InputMethodManager { final int targetUserId = editorInfo.targetInputMethodUser != null ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus"); int startInputSeq = -1; if (Flags.useZeroJankProxy()) { // async result delivered via MSG_START_INPUT_RESULT. startInputSeq = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocusAsync( startInputReason, mClient, windowGainingFocus, startInputFlags, softInputMode, windowFlags, editorInfo, servedInputConnection, servedInputConnection == null ? null : servedInputConnection.asIRemoteAccessibilityInputConnection(), view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, mImeDispatcher); } else { res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus( startInputReason, mClient, windowGainingFocus, startInputFlags, softInputMode, windowFlags, editorInfo, servedInputConnection, Loading @@ -3087,7 +3177,33 @@ public final class InputMethodManager { : servedInputConnection.asIRemoteAccessibilityInputConnection(), view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, mImeDispatcher); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (Flags.useZeroJankProxy()) { // Create a runnable for delayed notification to the app that the InputConnection is // initialized and ready for use. if (ic != null) { final int seqId = startInputSeq; mReportInputConnectionOpenedRunner = new ReportInputConnectionOpenedRunner(startInputSeq) { @Override public void run() { if (DEBUG) { Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view + ", ic=" + ic + ", editorInfo=" + editorInfo + ", handler=" + icHandler + ", startInputSeq=" + seqId); } reportInputConnectionOpened(ic, editorInfo, icHandler, view); } }; } else { mReportInputConnectionOpenedRunner = null; } return true; } if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res == null) { Log.wtf(TAG, "startInputOrWindowGainedFocus must not return" Loading Loading @@ -3118,6 +3234,7 @@ public final class InputMethodManager { } else if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); } switch (res.result) { case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW: mRestartOnNextWindowFocus = true; Loading
core/java/com/android/internal/inputmethod/IInputMethodClient.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.internal.inputmethod.InputBindResult; */ oneway interface IInputMethodClient { void onBindMethod(in InputBindResult res); void onStartInputResult(in InputBindResult res, int startInputSeq); void onBindAccessibilityService(in InputBindResult res, int id); void onUnbindMethod(int sequence, int unbindReason); void onUnbindAccessibilityService(int sequence, int id); Loading
core/java/com/android/internal/inputmethod/InputBindResult.java +1 −0 Original line number Diff line number Diff line Loading @@ -271,6 +271,7 @@ public final class InputBindResult implements Parcelable { public String toString() { return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id + " sequence=" + sequence + " result=" + result + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker + "}"; } Loading
core/java/com/android/internal/view/IInputMethodManager.aidl +18 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,8 @@ interface IInputMethodManager { boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, in @nullable ImeTracker.Token statsToken, int flags, in @nullable ResultReceiver resultReceiver, int reason); // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled. // If windowToken is null, this just does startInput(). Otherwise this reports that a window // has gained focus, and if 'editorInfo' is non-null then also does startInput. // @NonNull Loading @@ -85,6 +87,21 @@ interface IInputMethodManager { int unverifiedTargetSdkVersion, int userId, in ImeOnBackInvokedDispatcher imeDispatcher); // If windowToken is null, this just does startInput(). Otherwise this reports that a window // has gained focus, and if 'editorInfo' is non-null then also does startInput. @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)") void startInputOrWindowGainedFocusAsync( /* @StartInputReason */ int startInputReason, in IInputMethodClient client, in @nullable IBinder windowToken, /* @StartInputFlags */ int startInputFlags, /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags, in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection, in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, int unverifiedTargetSdkVersion, int userId, in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq); void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode); Loading Loading @@ -156,6 +173,7 @@ interface IInputMethodManager { in String delegatePackageName, in String delegatorPackageName); // TODO(b/293640003): introduce a new API method to provide async way to return boolean. /** Accepts and starts a stylus handwriting session for the delegate view **/ boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId, in String delegatePackageName, in String delegatorPackageName, int flags); Loading