diff options
| author | 2023-10-05 15:03:11 +0200 | |
|---|---|---|
| committer | 2024-02-28 16:37:05 +0100 | |
| commit | df62c4faa80f6bc0d5ddae808bb93c24b31b540c (patch) | |
| tree | 441c2836c3d8c1cdc1e5a1fa04e81fb638f273e2 | |
| parent | 2af839965ea12abda5429ca947e93af4c5c5a322 (diff) | |
Handle ImeTracker.Token missing flows
Created an empty ImeTracker.Token instance for testing,
and added more reasons to handle IME flows previously
missed from tracking.
Test: atest InputMethodStatsTest
ImeInsetsSourceConsumerTest
InputMethodManagerServiceWindowGainedFocusTest
DefaultImeVisibilityApplierTest
ImeVisibilityStateComputerTest#testOnApplyImeVisibilityFromComputer
Bug: 271426908
Change-Id: Idfa8dabf16d0bcd43ff0c0d64affb2e5f923f44b
43 files changed, 918 insertions, 538 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index b8c32a4aa7ef..a9966516965e 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3982,6 +3982,7 @@ package android.view.inputmethod { method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int); method public boolean hasActiveInputConnection(@Nullable android.view.View); method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests(); + method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest(); method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View); method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown(); method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle); diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index f5b58b920efb..9dc8c5d9f2e9 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -453,7 +453,7 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override - public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, + public void showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER); mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT, @@ -462,7 +462,7 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override - public void hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, + public void hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER); mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_HIDE_SOFT_INPUT, diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 2c7ca27e6b07..4dbdd91d5fc7 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -701,7 +701,13 @@ public class InputMethodService extends AbstractInputMethodService { */ private IBinder mCurHideInputToken; - /** The token tracking the current IME request or {@code null} otherwise. */ + /** + * The token tracking the current IME request. + * + * <p> This exists as a workaround to changing the signatures of public methods. It will get + * set to a {@code non-null} value before every call that uses it, stored locally inside the + * callee, and immediately after reset to {@code null} from the callee. + */ @Nullable private ImeTracker.Token mCurStatsToken; @@ -907,14 +913,13 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver, - IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) { + IBinder hideInputToken, @NonNull ImeTracker.Token statsToken) { mSystemCallingHideSoftInput = true; mCurHideInputToken = hideInputToken; mCurStatsToken = statsToken; try { hideSoftInput(flags, resultReceiver); } finally { - mCurStatsToken = null; mCurHideInputToken = null; mSystemCallingHideSoftInput = false; } @@ -926,23 +931,33 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void hideSoftInput(int flags, ResultReceiver resultReceiver) { - ImeTracker.forLogging().onProgress( - mCurStatsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT); if (DEBUG) Log.v(TAG, "hideSoftInput()"); + + final var statsToken = mCurStatsToken != null ? mCurStatsToken + : createStatsToken(false /* show */, + SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT, + ImeTracker.isFromUser(mRootView)); + mCurStatsToken = null; + + // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods. if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R && !mSystemCallingHideSoftInput) { Log.e(TAG, "IME shouldn't call hideSoftInput on itself." + " Use requestHideSelf(int) itself"); + ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT); return; } + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT); + + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput"); ImeTracing.getInstance().triggerServiceDump( "InputMethodService.InputMethodImpl#hideSoftInput", mDumper, null /* icProto */); final boolean wasVisible = isInputViewShown(); - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput"); mShowInputFlags = 0; mShowInputRequested = false; + mCurStatsToken = statsToken; hideWindow(); final boolean isVisible = isInputViewShown(); final boolean visibilityChanged = isVisible != wasVisible; @@ -963,14 +978,13 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void showSoftInputWithToken(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver, IBinder showInputToken, - @Nullable ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken) { mSystemCallingShowSoftInput = true; mCurShowInputToken = showInputToken; mCurStatsToken = statsToken; try { showSoftInput(flags, resultReceiver); } finally { - mCurStatsToken = null; mCurShowInputToken = null; mSystemCallingShowSoftInput = false; } @@ -982,16 +996,23 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { - ImeTracker.forLogging().onProgress( - mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT); if (DEBUG) Log.v(TAG, "showSoftInput()"); + + final var statsToken = mCurStatsToken != null ? mCurStatsToken + : createStatsToken(true /* show */, + SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT, + ImeTracker.isFromUser(mRootView)); + mCurStatsToken = null; + // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods. if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R && !mSystemCallingShowSoftInput) { - Log.e(TAG," IME shouldn't call showSoftInput on itself." + Log.e(TAG, "IME shouldn't call showSoftInput on itself." + " Use requestShowSelf(int) itself"); + ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT); return; } + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput"); ImeTracing.getInstance().triggerServiceDump( @@ -999,11 +1020,12 @@ public class InputMethodService extends AbstractInputMethodService { null /* icProto */); final boolean wasVisible = isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { - ImeTracker.forLogging().onProgress(mCurStatsToken, + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE); - showWindow(true); + mCurStatsToken = statsToken; + showWindow(true /* showInput */); } else { - ImeTracker.forLogging().onFailed(mCurStatsToken, + ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE); } setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); @@ -1895,21 +1917,23 @@ public class InputMethodService extends AbstractInputMethodService { if (showingInput) { // If we were last showing the soft keyboard, try to do so again. if (dispatchOnShowInputRequested(showFlags, true)) { - showWindow(true); + showWindowWithToken(true /* showInput */, + SoftInputShowHideReason.RESET_NEW_CONFIGURATION); if (completions != null) { mCurCompletions = completions; onDisplayCompletions(completions); } } else { - hideWindow(); + hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION); } } else if (mCandidatesVisibility == View.VISIBLE) { // If the candidates are currently visible, make sure the // window is shown for them. - showWindow(false); + showWindowWithToken(false /* showInput */, + SoftInputShowHideReason.RESET_NEW_CONFIGURATION); } else { // Otherwise hide the window. - hideWindow(); + hideWindowWithToken(SoftInputShowHideReason.RESET_NEW_CONFIGURATION); } // If user uses hard keyboard, IME button should always be shown. boolean showing = onEvaluateInputViewShown(); @@ -2368,13 +2392,15 @@ public class InputMethodService extends AbstractInputMethodService { // has not asked for the input view to be shown, then we need // to update whether the window is shown. if (shown) { - showWindow(false); + showWindowWithToken(false /* showInput */, + SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY); } else { - hideWindow(); + hideWindowWithToken( + SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY); } } } - + void updateCandidatesVisibility(boolean shown) { int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility(); if (mCandidatesVisibility != vis) { @@ -3009,6 +3035,19 @@ public class InputMethodService extends AbstractInputMethodService { return result; } + /** + * Utility function that creates an IME request tracking token before + * calling {@link #showWindow}. + * + * @param showInput whether the input window should be shown. + * @param reason the reason why the IME request was created. + */ + private void showWindowWithToken(boolean showInput, @SoftInputShowHideReason int reason) { + mCurStatsToken = createStatsToken(true /* show */, reason, + ImeTracker.isFromUser(mRootView)); + showWindow(showInput); + } + public void showWindow(boolean showInput) { if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput + " mShowInputRequested=" + mShowInputRequested @@ -3018,11 +3057,20 @@ public class InputMethodService extends AbstractInputMethodService { + " mInputStarted=" + mInputStarted + " mShowInputFlags=" + mShowInputFlags); + final var statsToken = mCurStatsToken != null ? mCurStatsToken + : createStatsToken(true /* show */, + SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT, + ImeTracker.isFromUser(mRootView)); + mCurStatsToken = null; + if (mInShowWindow) { Log.w(TAG, "Re-entrance in to showWindow"); + ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW); return; } + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper, null /* icProto */); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); @@ -3046,7 +3094,7 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "showWindow: draw decorView!"); mWindow.show(); mDecorViewWasVisible = true; - applyVisibilityInInsetsConsumerIfNecessary(true); + applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */, statsToken); cancelImeSurfaceRemoval(); mInShowWindow = false; Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -3137,13 +3185,15 @@ public class InputMethodService extends AbstractInputMethodService { * Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}. * * @param setVisible {@code true} to make it visible, false to hide it. + * @param statsToken the token tracking the current IME request. */ - private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) { + private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible, + @NonNull ImeTracker.Token statsToken) { ImeTracing.getInstance().triggerServiceDump( "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper, null /* icProto */); mPrivOps.applyImeVisibilityAsync(setVisible - ? mCurShowInputToken : mCurHideInputToken, setVisible, mCurStatsToken); + ? mCurShowInputToken : mCurHideInputToken, setVisible, statsToken); } private void finishViews(boolean finishingInput) { @@ -3159,12 +3209,35 @@ public class InputMethodService extends AbstractInputMethodService { mCandidatesViewStarted = false; } + /** + * Utility function that creates an IME request tracking token before + * calling {@link #hideWindow}. + * + * @param reason the reason why the IME request was created. + */ + private void hideWindowWithToken(@SoftInputShowHideReason int reason) { + // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it + // to work with onClickListeners + final boolean isFromUser = ImeTracker.isFromUser(mRootView) + || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY; + mCurStatsToken = createStatsToken(false /* show */, reason, isFromUser); + hideWindow(); + } + public void hideWindow() { if (DEBUG) Log.v(TAG, "CALL: hideWindow"); + + final var statsToken = mCurStatsToken != null ? mCurStatsToken + : createStatsToken(false /* show */, + SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT, + ImeTracker.isFromUser(mRootView)); + mCurStatsToken = null; + + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW); ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper, null /* icProto */); setImeWindowStatus(0, mBackDisposition); - applyVisibilityInInsetsConsumerIfNecessary(false); + applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */, statsToken); mWindowVisible = false; finishViews(false /* finishingInput */); if (mDecorViewVisible) { @@ -3440,9 +3513,14 @@ public class InputMethodService extends AbstractInputMethodService { private void requestHideSelf(@InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) { + // TODO(b/303041796): this should be handled by ImeTracker.isFromUser after fixing it + // to work with onClickListeners + final boolean isFromUser = ImeTracker.isFromUser(mRootView) + || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY; + final var statsToken = createStatsToken(false /* show */, reason, isFromUser); ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper, null /* icProto */); - mPrivOps.hideMySoftInput(flags, reason); + mPrivOps.hideMySoftInput(statsToken, flags, reason); } /** @@ -3450,9 +3528,16 @@ public class InputMethodService extends AbstractInputMethodService { * interact with it. */ public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) { + requestShowSelf(flags, SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME); + } + + private void requestShowSelf(@InputMethodManager.ShowFlags int flags, + @SoftInputShowHideReason int reason) { + final var statsToken = createStatsToken(true /* show */, reason, + ImeTracker.isFromUser(mRootView)); ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper, null /* icProto */); - mPrivOps.showMySoftInput(flags); + mPrivOps.showMySoftInput(statsToken, flags, reason); } private boolean handleBack(boolean doIt) { @@ -3472,7 +3557,7 @@ public class InputMethodService extends AbstractInputMethodService { // If we have the window visible for some other reason -- // most likely to show candidates -- then just get rid // of it. This really shouldn't happen, but just in case... - if (doIt) hideWindow(); + if (doIt) hideWindowWithToken(SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY); } return true; } @@ -3627,10 +3712,11 @@ public class InputMethodService extends AbstractInputMethodService { @InputMethodManager.HideFlags int hideFlags) { if (DEBUG) Log.v(TAG, "toggleSoftInput()"); if (isInputViewShown()) { - requestHideSelf( - hideFlags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT); + requestHideSelf(hideFlags, + SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT); } else { - requestShowSelf(showFlags); + requestShowSelf(showFlags, + SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT); } } @@ -4272,6 +4358,20 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Creates an IME request tracking token. + * + * @param show whether this is a show or a hide request. + * @param reason the reason why the IME request was created. + * @param isFromUser whether this request was created directly from user interaction. + */ + @NonNull + private ImeTracker.Token createStatsToken(boolean show, @SoftInputShowHideReason int reason, + boolean isFromUser) { + return ImeTracker.forLogging().onStart(show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_IME, reason, isFromUser); + } + + /** * Performs a dump of the InputMethodService's internal state. Override * to add your own information to the dump. */ diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 11180aef4479..5ee526e0343d 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -73,7 +73,7 @@ oneway interface IWindow { * * @param types internal insets types (WindowInsets.Type.InsetsType) to show * @param fromIme true if this request originated from IME (InputMethodService). - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ void showInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken); @@ -82,7 +82,7 @@ oneway interface IWindow { * * @param types internal insets types (WindowInsets.Type.InsetsType) to hide * @param fromIme true if this request originated from IME (InputMethodService). - * @param statsToken the token tracking the current IME hide request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ void hideInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken); diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index de809c8489fd..821e13d85370 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -21,9 +21,9 @@ import static android.view.ImeInsetsSourceConsumerProto.HAS_PENDING_REQUEST; import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER; import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; -import android.os.Process; import android.os.Trace; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl.Transaction; @@ -70,7 +70,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { if (!isShowRequested()) { mIsRequestedVisibleAwaitingControl = false; if (!running && !mHasPendingRequest) { - notifyHidden(null /* statsToken */); + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED, + mController.getHost().isHandlingPointerEvent() /* fromUser */); + notifyHidden(statsToken); removeSurface(); } } @@ -144,9 +148,17 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) { if (!fromIme) { + // Create a new token to track the hide request when we have control, + // as we use the passed in token for the insets animation already. + final var notifyStatsToken = getControl() != null + ? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL, + mController.getHost().isHandlingPointerEvent() /* fromUser */) + : statsToken; // The insets might be controlled by a remote target. Let the server know we are // requested to hide. - notifyHidden(statsToken); + notifyHidden(notifyStatsToken); } if (mAnimationState == ANIMATION_STATE_SHOW) { mHasPendingRequest = true; @@ -157,21 +169,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { * Notify {@link com.android.server.inputmethod.InputMethodManagerService} that * IME insets are hidden. * - * @param statsToken the token tracking the current IME hide request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ - private void notifyHidden(@Nullable ImeTracker.Token statsToken) { - // Create a new stats token to track the hide request when: - // - we do not already have one, or - // - we do already have one, but we have control and use the passed in token - // for the insets animation already. - if (statsToken == null || getControl() != null) { - statsToken = ImeTracker.forLogging().onRequestHide(null /* component */, - Process.myUid(), - ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, - mController.getHost().isHandlingPointerEvent() /* fromUser */); - } - + private void notifyHidden(@NonNull ImeTracker.Token statsToken) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN); diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 7f1e037e92d4..85c779bc8c79 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -43,7 +43,6 @@ import static android.view.WindowInsets.Type.ime; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY; -import static android.view.inputmethod.ImeTracker.TOKEN_NONE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; @@ -165,9 +164,9 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro mStatsToken = statsToken; if (DEBUG_IME_VISIBILITY && (types & ime()) != 0) { EventLog.writeEvent(IMF_IME_ANIM_START, - mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType, - mCurrentAlpha, "Current:" + mCurrentInsets, "Shown:" + mShownInsets, - "Hidden:" + mHiddenInsets); + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, + mAnimationType, mCurrentAlpha, "Current:" + mCurrentInsets, + "Shown:" + mShownInsets, "Hidden:" + mHiddenInsets); } mController.startAnimation(this, listener, types, mAnimation, new Bounds(mHiddenInsets, mShownInsets)); @@ -245,6 +244,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro } @Override + @Nullable public ImeTracker.Token getStatsToken() { return mStatsToken; } @@ -330,8 +330,8 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro mListener.onFinished(this); if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) { EventLog.writeEvent(IMF_IME_ANIM_FINISH, - mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType, - mCurrentAlpha, shown ? 1 : 0, Objects.toString(insets)); + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, + mAnimationType, mCurrentAlpha, shown ? 1 : 0, Objects.toString(insets)); } } @@ -355,8 +355,8 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro if (DEBUG) Log.d(TAG, "notify Control request cancelled for types: " + mTypes); if (DEBUG_IME_VISIBILITY && (mTypes & ime()) != 0) { EventLog.writeEvent(IMF_IME_ANIM_CANCEL, - mStatsToken != null ? mStatsToken.getTag() : TOKEN_NONE, mAnimationType, - Objects.toString(mPendingInsets)); + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, + mAnimationType, Objects.toString(mPendingInsets)); } releaseLeashes(); } diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 079991a81e77..92e20e09d8c4 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -137,6 +137,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro } @Override + @Nullable public ImeTracker.Token getStatsToken() { return mControl.getStatsToken(); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 1803a6ed237f..6cc4b20dcde9 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -28,7 +28,6 @@ import static android.view.WindowInsets.Type.LAST; import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.ime; -import static android.view.inputmethod.ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL; import android.animation.AnimationHandler; import android.animation.Animator; @@ -47,7 +46,6 @@ import android.graphics.Rect; import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; -import android.os.Process; import android.os.Trace; import android.text.TextUtils; import android.util.IntArray; @@ -659,6 +657,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final Runnable mAnimCallback; /** Pending control request that is waiting on IME to be ready to be shown */ + @Nullable private PendingControlRequest mPendingImeControlRequest; private int mWindowType; @@ -1043,12 +1042,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation hideTypes[0] &= ~animatingTypes; if (showTypes[0] != 0) { - applyAnimation(showTypes[0], true /* show */, false /* fromIme */, - null /* statsToken */); + final var statsToken = (showTypes[0] & ime()) == 0 ? null + : ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW, + ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED, + mHost.isHandlingPointerEvent() /* fromUser */); + applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken); } if (hideTypes[0] != 0) { - applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, - null /* statsToken */); + final var statsToken = (hideTypes[0] & ime()) == 0 ? null + : ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED, + mHost.isHandlingPointerEvent() /* fromUser */); + applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken); } if (mControllableTypes != controllableTypes) { @@ -1064,15 +1069,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void show(@InsetsType int types) { - ImeTracker.Token statsToken = null; - if ((types & ime()) != 0) { - statsToken = ImeTracker.forLogging().onRequestShow(null /* component */, - Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, - SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API, - mHost.isHandlingPointerEvent() /* fromUser */); - } - - show(types, false /* fromIme */, statsToken); + show(types, false /* fromIme */, null /* statsToken */); } @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @@ -1080,6 +1077,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Nullable ImeTracker.Token statsToken) { if ((types & ime()) != 0) { Log.d(TAG, "show(ime(), fromIme=" + fromIme + ")"); + + if (statsToken == null) { + statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW, + ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API, + mHost.isHandlingPointerEvent() /* fromUser */); + } } if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#show", @@ -1148,9 +1152,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } /** - * Handle the {@link #mPendingImeControlRequest} when - * - The IME insets is ready to show. - * - The IME insets has being requested invisible. + * Handle the {@link #mPendingImeControlRequest} when: + * <ul> + * <li> The IME insets is ready to show. + * <li> The IME insets has being requested invisible. + * </ul> */ private void handlePendingControlRequest(@Nullable ImeTracker.Token statsToken) { PendingControlRequest pendingRequest = mPendingImeControlRequest; @@ -1170,20 +1176,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void hide(@InsetsType int types) { - ImeTracker.Token statsToken = null; - if ((types & ime()) != 0) { - statsToken = ImeTracker.forLogging().onRequestHide(null /* component */, - Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, - mHost.isHandlingPointerEvent() /* fromUser */); - } - - hide(types, false /* fromIme */, statsToken); + hide(types, false /* fromIme */, null /* statsToken */); } @VisibleForTesting public void hide(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) { + if ((types & ime()) != 0) { + Log.d(TAG, "hide(ime(), fromIme=" + fromIme + ")"); + + if (statsToken == null) { + statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, + mHost.isHandlingPointerEvent() /* fromUser */); + } + } if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#hide", mHost.getInputMethodManager(), null /* icProto */); @@ -1307,10 +1315,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (monitoredAnimation && (types & Type.ime()) != 0) { if (animationType == ANIMATION_TYPE_SHOW) { ImeTracker.forLatency().onShowCancelled(statsToken, - PHASE_CLIENT_ANIMATION_CANCEL, ActivityThread::currentApplication); + ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL, + ActivityThread::currentApplication); } else { ImeTracker.forLatency().onHideCancelled(statsToken, - PHASE_CLIENT_ANIMATION_CANCEL, ActivityThread::currentApplication); + ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL, + ActivityThread::currentApplication); } ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION); @@ -1602,12 +1612,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) { if (invokeCallback) { ImeTracker.forLogging().onCancelled(control.getStatsToken(), - PHASE_CLIENT_ANIMATION_CANCEL); + ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL); control.cancel(); } else { // Succeeds if invokeCallback is false (i.e. when called from notifyFinished). ImeTracker.forLogging().onProgress(control.getStatsToken(), - PHASE_CLIENT_ANIMATION_CANCEL); + ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL); } if (DEBUG) { Log.d(TAG, TextUtils.formatSimple( diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java index bffaeea6a731..ebdddd537ae3 100644 --- a/core/java/android/view/InsetsResizeAnimationRunner.java +++ b/core/java/android/view/InsetsResizeAnimationRunner.java @@ -29,6 +29,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_RESIZE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Rect; import android.util.SparseArray; @@ -92,6 +93,7 @@ public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner } @Override + @Nullable public ImeTracker.Token getStatsToken() { // Return null as resizing the IME view is not explicitly tracked. return null; diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 0ce61bb17774..fdb2a6ee1791 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -314,7 +314,7 @@ public class InsetsSourceConsumer { * @param fromController {@code true} if request is coming from controller. * (e.g. in IME case, controller is * {@link android.inputmethodservice.InputMethodService}). - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. * * @implNote The {@code statsToken} is ignored here, and only handled in * {@link ImeInsetsSourceConsumer} for IME animations only. diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java index 491b0e349cde..cedf8d04ed99 100644 --- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java +++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java @@ -25,7 +25,6 @@ import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.content.Context; -import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; @@ -298,7 +297,7 @@ final class IInputMethodManagerGlobalInvoker { @AnyThread static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, + @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final IInputMethodManager service = getService(); @@ -315,7 +314,7 @@ final class IInputMethodManagerGlobalInvoker { @AnyThread static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, + @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final IInputMethodManager service = getService(); if (service == null) { @@ -331,6 +330,20 @@ final class IInputMethodManagerGlobalInvoker { // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled. @AnyThread + @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) + static void hideSoftInputFromServerForTest() { + final IInputMethodManager service = getService(); + if (service == null) { + return; + } + try { + service.hideSoftInputFromServerForTest(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @AnyThread @NonNull @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) static InputBindResult startInputOrWindowGainedFocus(@StartInputReason int startInputReason, @@ -654,35 +667,18 @@ final class IInputMethodManagerGlobalInvoker { } } - /** @see com.android.server.inputmethod.ImeTrackerService#onRequestShow */ - @AnyThread - @NonNull - static ImeTracker.Token onRequestShow(@NonNull String tag, int uid, - @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) { - final IImeTracker service = getImeTrackerService(); - if (service == null) { - // Create token with "fake" binder if the service was not found. - return new ImeTracker.Token(new Binder(), tag); - } - try { - return service.onRequestShow(tag, uid, origin, reason, fromUser); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** @see com.android.server.inputmethod.ImeTrackerService#onRequestHide */ + /** @see com.android.server.inputmethod.ImeTrackerService#onStart */ @AnyThread @NonNull - static ImeTracker.Token onRequestHide(@NonNull String tag, int uid, + static ImeTracker.Token onStart(@NonNull String tag, int uid, @ImeTracker.Type int type, @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) { - final IImeTracker service = getImeTrackerService(); + final var service = getImeTrackerService(); if (service == null) { - // Create token with "fake" binder if the service was not found. - return new ImeTracker.Token(new Binder(), tag); + // Create token with "empty" binder if the service was not found. + return ImeTracker.Token.empty(tag); } try { - return service.onRequestHide(tag, uid, origin, reason, fromUser); + return service.onStart(tag, uid, type, origin, reason, fromUser); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java index b1fdaa97ffe0..7f796611d217 100644 --- a/core/java/android/view/inputmethod/ImeTracker.java +++ b/core/java/android/view/inputmethod/ImeTracker.java @@ -28,17 +28,20 @@ import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_SHOWN; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; import android.content.Context; +import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.os.SystemProperties; import android.util.Log; import android.view.InsetsController.AnimationType; import android.view.SurfaceControl; import android.view.View; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.jank.InteractionJankMonitor; @@ -108,34 +111,32 @@ public interface ImeTracker { /** * The origin of the IME request * - * The name follows the format {@code PHASE_x_...} where {@code x} denotes - * where the origin is (i.e. {@code PHASE_SERVER_...} occurs in the server). + * <p> The name follows the format {@code ORIGIN_x_...} where {@code x} denotes + * where the origin is (i.e. {@code ORIGIN_SERVER} occurs in the server). */ @IntDef(prefix = { "ORIGIN_" }, value = { - ORIGIN_CLIENT_SHOW_SOFT_INPUT, - ORIGIN_CLIENT_HIDE_SOFT_INPUT, - ORIGIN_SERVER_START_INPUT, - ORIGIN_SERVER_HIDE_INPUT + ORIGIN_CLIENT, + ORIGIN_SERVER, + ORIGIN_IME }) @Retention(RetentionPolicy.SOURCE) @interface Origin {} - /** The IME show request originated in the client. */ - int ORIGIN_CLIENT_SHOW_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_SHOW_SOFT_INPUT; + /** The IME request originated in the client. */ + int ORIGIN_CLIENT = ImeProtoEnums.ORIGIN_CLIENT; - /** The IME hide request originated in the client. */ - int ORIGIN_CLIENT_HIDE_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_HIDE_SOFT_INPUT; + /** The IME request originated in the server. */ + int ORIGIN_SERVER = ImeProtoEnums.ORIGIN_SERVER; - /** The IME show request originated in the server. */ - int ORIGIN_SERVER_START_INPUT = ImeProtoEnums.ORIGIN_SERVER_START_INPUT; - - /** The IME hide request originated in the server. */ - int ORIGIN_SERVER_HIDE_INPUT = ImeProtoEnums.ORIGIN_SERVER_HIDE_INPUT; + /** The IME request originated in the IME. */ + int ORIGIN_IME = ImeProtoEnums.ORIGIN_IME; + /** The IME request originated in the WindowManager Shell. */ + int ORIGIN_WM_SHELL = ImeProtoEnums.ORIGIN_WM_SHELL; /** * The current phase of the IME request. * - * The name follows the format {@code PHASE_x_...} where {@code x} denotes + * <p> The name follows the format {@code PHASE_x_...} where {@code x} denotes * where the phase is (i.e. {@code PHASE_SERVER_...} occurs in the server). */ @IntDef(prefix = { "PHASE_" }, value = { @@ -155,7 +156,6 @@ public interface ImeTracker { PHASE_IME_SHOW_SOFT_INPUT, PHASE_IME_HIDE_SOFT_INPUT, PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE, - PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER, PHASE_SERVER_APPLY_IME_VISIBILITY, PHASE_WM_SHOW_IME_RUNNER, PHASE_WM_SHOW_IME_READY, @@ -182,6 +182,11 @@ public interface ImeTracker { PHASE_CLIENT_ANIMATION_FINISHED_SHOW, PHASE_CLIENT_ANIMATION_FINISHED_HIDE, PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT, + PHASE_CLIENT_ANIMATION_FINISHED_HIDE, + PHASE_IME_SHOW_WINDOW, + PHASE_IME_HIDE_WINDOW, + PHASE_IME_PRIVILEGED_OPERATIONS, + PHASE_SERVER_CURRENT_ACTIVE_IME, }) @Retention(RetentionPolicy.SOURCE) @interface Phase {} @@ -224,19 +229,15 @@ public interface ImeTracker { /** Dispatched from the IME wrapper to the IME. */ int PHASE_IME_WRAPPER_DISPATCH = ImeProtoEnums.PHASE_IME_WRAPPER_DISPATCH; - /** Reached the IME' showSoftInput method. */ + /** Reached the IME's showSoftInput method. */ int PHASE_IME_SHOW_SOFT_INPUT = ImeProtoEnums.PHASE_IME_SHOW_SOFT_INPUT; - /** Reached the IME' hideSoftInput method. */ + /** Reached the IME's hideSoftInput method. */ int PHASE_IME_HIDE_SOFT_INPUT = ImeProtoEnums.PHASE_IME_HIDE_SOFT_INPUT; /** The server decided the IME should be shown. */ int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = ImeProtoEnums.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE; - /** Requested applying the IME visibility in the insets source consumer. */ - int PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER = - ImeProtoEnums.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER; - /** Applied the IME visibility. */ int PHASE_SERVER_APPLY_IME_VISIBILITY = ImeProtoEnums.PHASE_SERVER_APPLY_IME_VISIBILITY; @@ -323,37 +324,49 @@ public interface ImeTracker { int PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT = ImeProtoEnums.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT; + /** Reached the IME's showWindow method. */ + int PHASE_IME_SHOW_WINDOW = ImeProtoEnums.PHASE_IME_SHOW_WINDOW; + + /** Reached the IME's hideWindow method. */ + int PHASE_IME_HIDE_WINDOW = ImeProtoEnums.PHASE_IME_HIDE_WINDOW; + + /** Reached the InputMethodPrivilegedOperations handler. */ + int PHASE_IME_PRIVILEGED_OPERATIONS = ImeProtoEnums.PHASE_IME_PRIVILEGED_OPERATIONS; + + /** Checked that the calling IME is the currently active IME. */ + int PHASE_SERVER_CURRENT_ACTIVE_IME = ImeProtoEnums.PHASE_SERVER_CURRENT_ACTIVE_IME; + /** - * Creates an IME show request tracking token. + * Called when an IME request is started. * - * @param component the name of the component that created the IME request, or {@code null} - * otherwise (defaulting to {@link ActivityThread#currentProcessName()}). - * @param uid the uid of the client that requested the IME. - * @param origin the origin of the IME show request. - * @param reason the reason why the IME show request was created. + * @param component the name of the component that started the request. + * @param uid the uid of the client that started the request. + * @param type the type of the request. + * @param origin the origin of the request. + * @param reason the reason for starting the request. * @param fromUser whether this request was created directly from user interaction. * - * @return An IME tracking token. + * @return An IME request tracking token. */ @NonNull - Token onRequestShow(@Nullable String component, int uid, @Origin int origin, + Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser); /** - * Creates an IME hide request tracking token. + * Called when an IME request is started for the current process. * - * @param component the name of the component that created the IME request, or {@code null} - * otherwise (defaulting to {@link ActivityThread#currentProcessName()}). - * @param uid the uid of the client that requested the IME. - * @param origin the origin of the IME hide request. - * @param reason the reason why the IME hide request was created. + * @param type the type of the request. + * @param origin the origin of the request. + * @param reason the reason for starting the request. * @param fromUser whether this request was created directly from user interaction. * - * @return An IME tracking token. + * @return An IME request tracking token. */ @NonNull - Token onRequestHide(@Nullable String component, int uid, @Origin int origin, - @SoftInputShowHideReason int reason, boolean fromUser); + default Token onStart(@Type int type, @Origin int origin, @SoftInputShowHideReason int reason, + boolean fromUser) { + return onStart(Process.myProcessName(), Process.myUid(), type, origin, reason, fromUser); + } /** * Called when an IME request progresses to a further phase. @@ -390,14 +403,14 @@ public interface ImeTracker { /** * Called when the IME show request is successful. * - * @param token the token tracking the current IME show request or {@code null} otherwise. + * @param token the token tracking the current IME request or {@code null} otherwise. */ void onShown(@Nullable Token token); /** * Called when the IME hide request is successful. * - * @param token the token tracking the current IME hide request or {@code null} otherwise. + * @param token the token tracking the current IME request or {@code null} otherwise. */ void onHidden(@Nullable Token token); @@ -479,33 +492,17 @@ public interface ImeTracker { @NonNull @Override - public Token onRequestShow(@Nullable String component, int uid, @Origin int origin, - @SoftInputShowHideReason int reason, boolean fromUser) { - final var tag = getTag(component); - final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin, - reason, fromUser); - - Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin) - + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason) - + " fromUser " + fromUser, - mLogStackTrace ? new Throwable() : null); - - return token; - } - - @NonNull - @Override - public Token onRequestHide(@Nullable String component, int uid, @Origin int origin, + public Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) { - final var tag = getTag(component); - final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin, - reason, fromUser); + final var tag = Token.createTag(component); + final var token = IInputMethodManagerGlobalInvoker.onStart(tag, uid, type, + origin, reason, fromUser); - Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin) + Log.i(TAG, token.mTag + ": onRequest" + (type == TYPE_SHOW ? "Show" : "Hide") + + " at " + Debug.originToString(origin) + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason) + " fromUser " + fromUser, mLogStackTrace ? new Throwable() : null); - return token; } @@ -556,20 +553,6 @@ public interface ImeTracker { Log.i(TAG, token.mTag + ": onHidden"); } - - /** - * Returns a logging tag using the given component name. - * - * @param component the name of the component that created the IME request, or {@code null} - * otherwise (defaulting to {@link ActivityThread#currentProcessName()}). - */ - @NonNull - private String getTag(@Nullable String component) { - if (component == null) { - component = ActivityThread.currentProcessName(); - } - return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt()); - } }; /** The singleton IME tracker instance for instrumenting jank metrics. */ @@ -581,6 +564,10 @@ public interface ImeTracker { /** A token that tracks the progress of an IME request. */ final class Token implements Parcelable { + /** Empty binder, lazily initialized, used for empty token instantiation. */ + @Nullable + private static IBinder sEmptyBinder; + /** The binder used to identify this token. */ @NonNull private final IBinder mBinder; @@ -599,16 +586,56 @@ public interface ImeTracker { mTag = in.readString8(); } + /** Returns the binder used to identify this token. */ @NonNull public IBinder getBinder() { return mBinder; } + /** Returns the logging tag of this token. */ @NonNull public String getTag() { return mTag; } + /** + * Creates a logging tag. + * + * @param component the name of the component that created the IME request. + */ + @NonNull + private static String createTag(@NonNull String component) { + return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt()); + } + + /** Returns a new token with an empty binder. */ + @NonNull + @VisibleForTesting(visibility = Visibility.PACKAGE) + public static Token empty() { + final var tag = createTag(Process.myProcessName()); + return empty(tag); + } + + /** Returns a new token with an empty binder and the given logging tag. */ + @NonNull + static Token empty(@NonNull String tag) { + return new Token(getEmptyBinder(), tag); + } + + /** Returns the empty binder instance for empty token creation, lazily initializing it. */ + @NonNull + private static IBinder getEmptyBinder() { + if (sEmptyBinder == null) { + sEmptyBinder = new Binder(); + } + return sEmptyBinder; + } + + @Override + public String toString() { + return super.toString() + "(tag: " + mTag + ")"; + } + /** For Parcelable, no special marshalled objects. */ @Override public int describeContents() { diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index 33f34c5697c4..88607fc80f69 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -281,7 +281,7 @@ public interface InputMethod { }) @Retention(RetentionPolicy.SOURCE) @interface ShowFlags {} - + /** * Flag for {@link #showSoftInput}: this show has been explicitly * requested by the user. If not set, the system has decided it may be @@ -314,18 +314,18 @@ public interface InputMethod { * @param showInputToken an opaque {@link android.os.Binder} token to identify which API call * of {@link InputMethodManager#showSoftInput(View, int)} is associated with * this callback. - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request. * @hide */ @MainThread public default void showSoftInputWithToken(@ShowFlags int flags, ResultReceiver resultReceiver, - IBinder showInputToken, @Nullable ImeTracker.Token statsToken) { + IBinder showInputToken, @NonNull ImeTracker.Token statsToken) { showSoftInput(flags, resultReceiver); } /** * Request that any soft input part of the input method be shown to the user. - * + * * @param resultReceiver The client requesting the show may wish to * be told the impact of their request, which should be supplied here. * The result code should be @@ -352,12 +352,12 @@ public interface InputMethod { * @param hideInputToken an opaque {@link android.os.Binder} token to identify which API call * of {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}} is associated * with this callback. - * @param statsToken the token tracking the current IME hide request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request. * @hide */ @MainThread public default void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver, - IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) { + IBinder hideInputToken, @NonNull ImeTracker.Token statsToken) { hideSoftInput(flags, resultReceiver); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index fcc8344cbcd9..fa80353650bb 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2262,21 +2262,22 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. */ public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) { - return showSoftInput(view, null /* statsToken */, flags, resultReceiver, - SoftInputShowHideReason.SHOW_SOFT_INPUT); + return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT); } - private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, - @ShowFlags int flags, ResultReceiver resultReceiver, + private boolean showSoftInput(View view, @ShowFlags int flags, + @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { + // TODO(b/303041796): handle tracking physical keyboard and DPAD as user interactions + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW, + ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(view)); + return showSoftInput(view, statsToken, flags, resultReceiver, reason); + } + + private boolean showSoftInput(View view, @NonNull ImeTracker.Token statsToken, + @ShowFlags int flags, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { - if (statsToken == null) { - // TODO(b/303041796): handle tracking physical keyboard and DPAD as user interactions - statsToken = ImeTracker.forLogging().onRequestShow(null /* component */, - Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason, - ImeTracker.isFromUser(view)); - } - ImeTracker.forLatency().onRequestShow(statsToken, ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, - reason, ActivityThread::currentApplication); + ImeTracker.forLatency().onRequestShow(statsToken, + ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication); ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this, null /* icProto */); // Re-dispatch if there is a context mismatch. @@ -2289,9 +2290,8 @@ public final class InputMethodManager { synchronized (mH) { if (!hasServedByInputMethodLocked(view)) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); - ImeTracker.forLatency().onShowFailed( - statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED, - ActivityThread::currentApplication); + ImeTracker.forLatency().onShowFailed(statsToken, + ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication); Log.w(TAG, "Ignoring showSoftInput() as view=" + view + " is not served."); return false; } @@ -2326,9 +2326,9 @@ public final class InputMethodManager { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499) public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) { synchronized (mH) { - final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow( - null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, - SoftInputShowHideReason.SHOW_SOFT_INPUT, false /* fromUser */); + final int reason = SoftInputShowHideReason.SHOW_SOFT_INPUT; + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW, + ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */); Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be" + " removed soon. If you are using androidx.appcompat.widget.SearchView," @@ -2352,7 +2352,7 @@ public final class InputMethodManager { flags, mCurRootView.getLastClickToolType(), resultReceiver, - SoftInputShowHideReason.SHOW_SOFT_INPUT); + reason); } } @@ -2428,11 +2428,10 @@ public final class InputMethodManager { initialServedView = getServedViewLocked(); } - final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide( - null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - reason, ImeTracker.isFromUser(initialServedView)); - ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - reason, ActivityThread::currentApplication); + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(initialServedView)); + ImeTracker.forLatency().onRequestHide(statsToken, + ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication); ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow", this, null /* icProto */); checkFocus(); @@ -2471,20 +2470,18 @@ public final class InputMethodManager { } } - final var reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW; - final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide( - null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - reason, ImeTracker.isFromUser(view)); - ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - reason, ActivityThread::currentApplication); + final int reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW; + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(view)); + ImeTracker.forLatency().onRequestHide(statsToken, + ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication); ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromView", this, null /* icProto */); synchronized (mH) { if (!hasServedByInputMethodLocked(view)) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); - ImeTracker.forLatency().onShowFailed( - statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED, - ActivityThread::currentApplication); + ImeTracker.forLatency().onShowFailed(statsToken, + ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication); Log.w(TAG, "Ignoring hideSoftInputFromView() as view=" + view + " is not served."); return false; } @@ -2497,6 +2494,19 @@ public final class InputMethodManager { } /** + * A test API for CTS to request hiding the current soft input window, with the request origin + * on the server side. + * + * @hide + */ + @SuppressLint("UnflaggedApi") // @TestApi without associated feature. + @TestApi + @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) + public void hideSoftInputFromServerForTest() { + IInputMethodManagerGlobalInvoker.hideSoftInputFromServerForTest(); + } + + /** * Start stylus handwriting session. * * If supported by the current input method, a stylus handwriting session is started on the @@ -2972,10 +2982,11 @@ public final class InputMethodManager { if (view != null) { final WindowInsets rootInsets = view.getRootWindowInsets(); if (rootInsets != null && rootInsets.isVisible(WindowInsets.Type.ime())) { - hideSoftInputFromWindow(view.getWindowToken(), hideFlags, null, + hideSoftInputFromWindow(view.getWindowToken(), hideFlags, + null /* resultReceiver */, SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT); } else { - showSoftInput(view, null /* statsToken */, showFlags, null /* resultReceiver */, + showSoftInput(view, showFlags, null /* resultReceiver */, SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT); } } @@ -3536,11 +3547,11 @@ public final class InputMethodManager { @UnsupportedAppUsage void closeCurrentInput() { - final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide( - null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION, false /* fromUser */); - ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION, + final int reason = SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION; + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */); + ImeTracker.forLatency().onRequestHide(statsToken, + ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication); synchronized (mH) { @@ -3561,7 +3572,7 @@ public final class InputMethodManager { statsToken, HIDE_NOT_ALWAYS, null, - SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION); + reason); } } @@ -3602,12 +3613,12 @@ public final class InputMethodManager { * * @param windowToken the window from which this request originates. If this doesn't match the * currently served view, the request is ignored and returns {@code false}. - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request. * * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise. * @hide */ - public boolean requestImeShow(IBinder windowToken, @Nullable ImeTracker.Token statsToken) { + public boolean requestImeShow(IBinder windowToken, @NonNull ImeTracker.Token statsToken) { checkFocus(); synchronized (mH) { final View servedView = getServedViewLocked(); @@ -3631,16 +3642,11 @@ public final class InputMethodManager { * * @param windowToken the window from which this request originates. If this doesn't match the * currently served view, the request is ignored. - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request. * @hide */ - public void notifyImeHidden(IBinder windowToken, @Nullable ImeTracker.Token statsToken) { - if (statsToken == null) { - statsToken = ImeTracker.forLogging().onRequestHide(null /* component */, - Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, - SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, false /* fromUser */); - } - ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, + public void notifyImeHidden(IBinder windowToken, @NonNull ImeTracker.Token statsToken) { + ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, ActivityThread::currentApplication); ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this, @@ -4024,8 +4030,11 @@ public final class InputMethodManager { */ @Deprecated public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) { - InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput( - flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION); + final int reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION; + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */); + InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(statsToken, flags, + reason); } /** @@ -4043,7 +4052,11 @@ public final class InputMethodManager { */ @Deprecated public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) { - InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags); + final int reason = SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION; + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW, + ImeTracker.ORIGIN_CLIENT, reason, false /* fromUser */); + InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(statsToken, flags, + reason); } /** diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl index 275904347d2b..b45bc1c46967 100644 --- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl +++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl @@ -25,28 +25,19 @@ import android.view.inputmethod.ImeTracker; interface IImeTracker { /** - * Called when an IME show request is created. + * Called when an IME request is started. * * @param tag the logging tag. - * @param uid the uid of the client that requested the IME. - * @param origin the origin of the IME show request. - * @param reason the reason why the IME show request was created. + * @param uid the uid of the client that started the request. + * @param type the type of the request. + * @param origin the origin of the request. * @param fromUser whether this request was created directly from user interaction. - * @return A new IME tracking token. - */ - ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason, boolean fromUser); - - /** - * Called when an IME hide request is created. + * @param reason the reason for starting the request. * - * @param tag the logging tag. - * @param uid the uid of the client that requested the IME. - * @param origin the origin of the IME hide request. - * @param reason the reason why the IME hide request was created. - * @param fromUser whether this request was created directly from user interaction. - * @return A new IME tracking token. + * @return An IME request tracking token. */ - ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason, boolean fromUser); + ImeTracker.Token onStart(String tag, int uid, int type, int origin, int reason, + boolean fromUser); /** * Called when the IME request progresses to a further phase. diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl index 6abd9e8a8a17..2593b78f5182 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl @@ -71,11 +71,11 @@ oneway interface IInputMethod { void setSessionEnabled(IInputMethodSession session, boolean enabled); - void showSoftInput(in IBinder showInputToken, in @nullable ImeTracker.Token statsToken, - int flags, in ResultReceiver resultReceiver); + void showSoftInput(in IBinder showInputToken, in ImeTracker.Token statsToken, int flags, + in ResultReceiver resultReceiver); - void hideSoftInput(in IBinder hideInputToken, in @nullable ImeTracker.Token statsToken, - int flags, in ResultReceiver resultReceiver); + void hideSoftInput(in IBinder hideInputToken, in ImeTracker.Token statsToken, int flags, + in ResultReceiver resultReceiver); void updateEditorToolType(int toolType); diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index 65a2f4be9901..457b9dd34644 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -35,15 +35,17 @@ oneway interface IInputMethodPrivilegedOperations { void setInputMethod(String id, in AndroidFuture future /* T=Void */); void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype, in AndroidFuture future /* T=Void */); - void hideMySoftInput(int flags, int reason, in AndroidFuture future /* T=Void */); - void showMySoftInput(int flags, in AndroidFuture future /* T=Void */); + void hideMySoftInput(in ImeTracker.Token statsToken, int flags, int reason, + in AndroidFuture future /* T=Void */); + void showMySoftInput(in ImeTracker.Token statsToken, int flags, int reason, + in AndroidFuture future /* T=Void */); void updateStatusIconAsync(String packageName, int iconId); void switchToPreviousInputMethod(in AndroidFuture future /* T=Boolean */); void switchToNextInputMethod(boolean onlyCurrentIme, in AndroidFuture future /* T=Boolean */); void shouldOfferSwitchingToNextInputMethod(in AndroidFuture future /* T=Boolean */); void notifyUserActionAsync(); void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible, - in @nullable ImeTracker.Token statsToken); + in ImeTracker.Token statsToken); void onStylusHandwritingReady(int requestId, int pid); void resetStylusHandwriting(int requestId); void switchKeyboardLayoutAsync(int direction); diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 9b7fa2f4f9e9..a0aad31d2e04 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -189,6 +189,8 @@ public final class InputMethodDebug { */ public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) { switch (reason) { + case SoftInputShowHideReason.NOT_SET: + return "NOT_SET"; case SoftInputShowHideReason.SHOW_SOFT_INPUT: return "SHOW_SOFT_INPUT"; case SoftInputShowHideReason.ATTACH_NEW_INPUT: @@ -265,6 +267,36 @@ public final class InputMethodDebug { return "HIDE_SOFT_INPUT_CLOSE_CURRENT_SESSION"; case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW: return "HIDE_SOFT_INPUT_FROM_VIEW"; + case SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT: + return "SHOW_SOFT_INPUT_LEGACY_DIRECT"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT: + return "HIDE_SOFT_INPUT_LEGACY_DIRECT"; + case SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT: + return "SHOW_WINDOW_LEGACY_DIRECT"; + case SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT: + return "HIDE_WINDOW_LEGACY_DIRECT"; + case SoftInputShowHideReason.RESET_NEW_CONFIGURATION: + return "RESET_NEW_CONFIGURATION"; + case SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY: + return "UPDATE_CANDIDATES_VIEW_VISIBILITY"; + case SoftInputShowHideReason.CONTROLS_CHANGED: + return "CONTROLS_CHANGED"; + case SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED: + return "DISPLAY_CONFIGURATION_CHANGED"; + case SoftInputShowHideReason.DISPLAY_INSETS_CHANGED: + return "DISPLAY_INSETS_CHANGED"; + case SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED: + return "DISPLAY_CONTROLS_CHANGED"; + case SoftInputShowHideReason.UNBIND_CURRENT_METHOD: + return "UNBIND_CURRENT_METHOD"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED: + return "HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL: + return "HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL"; + case SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT: + return "SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT"; + case SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION: + return "SHOW_SOFT_INPUT_IMM_DEPRECATION"; default: return "Unknown=" + reason; } diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index 792388dca339..635a227e5862 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -252,20 +252,21 @@ public final class InputMethodPrivilegedOperations { } /** - * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)} - * - * @param reason the reason to hide soft input + * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput} */ @AnyThread - public void hideMySoftInput(@InputMethodManager.HideFlags int flags, - @SoftInputShowHideReason int reason) { + public void hideMySoftInput(@NonNull ImeTracker.Token statsToken, + @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS); return; } + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS); try { final AndroidFuture<Void> future = new AndroidFuture<>(); - ops.hideMySoftInput(flags, reason, future); + ops.hideMySoftInput(statsToken, flags, reason, future); CompletableFutureUtil.getResult(future); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -273,17 +274,21 @@ public final class InputMethodPrivilegedOperations { } /** - * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)} + * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput} */ @AnyThread - public void showMySoftInput(@InputMethodManager.ShowFlags int flags) { + public void showMySoftInput(@NonNull ImeTracker.Token statsToken, + @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS); return; } + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS); try { final AndroidFuture<Void> future = new AndroidFuture<>(); - ops.showMySoftInput(flags, future); + ops.showMySoftInput(statsToken, flags, reason, future); CompletableFutureUtil.getResult(future); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -379,19 +384,19 @@ public final class InputMethodPrivilegedOperations { * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, * int)} * @param setVisible {@code true} to set IME visible, else hidden. - * @param statsToken the token tracking the current IME request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request. */ @AnyThread public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible, - @Nullable ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { ImeTracker.forLogging().onFailed(statsToken, - ImeTracker.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER); + ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS); return; } ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER); + ImeTracker.PHASE_IME_PRIVILEGED_OPERATIONS); try { ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible, statsToken); } catch (RemoteException e) { diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java index 861b8a75f730..da738a01ec39 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -34,6 +34,7 @@ import java.lang.annotation.Retention; */ @Retention(SOURCE) @IntDef(value = { + SoftInputShowHideReason.NOT_SET, SoftInputShowHideReason.SHOW_SOFT_INPUT, SoftInputShowHideReason.ATTACH_NEW_INPUT, SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME, @@ -72,8 +73,26 @@ import java.lang.annotation.Retention; SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE, SoftInputShowHideReason.HIDE_CLOSE_CURRENT_SESSION, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW, + SoftInputShowHideReason.SHOW_SOFT_INPUT_LEGACY_DIRECT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_LEGACY_DIRECT, + SoftInputShowHideReason.SHOW_WINDOW_LEGACY_DIRECT, + SoftInputShowHideReason.HIDE_WINDOW_LEGACY_DIRECT, + SoftInputShowHideReason.RESET_NEW_CONFIGURATION, + SoftInputShowHideReason.UPDATE_CANDIDATES_VIEW_VISIBILITY, + SoftInputShowHideReason.CONTROLS_CHANGED, + SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED, + SoftInputShowHideReason.DISPLAY_INSETS_CHANGED, + SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED, + SoftInputShowHideReason.UNBIND_CURRENT_METHOD, + SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED, + SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL, + SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT, + SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION, }) public @interface SoftInputShowHideReason { + /** Default, undefined reason. */ + int NOT_SET = ImeProtoEnums.REASON_NOT_SET; + /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ int SHOW_SOFT_INPUT = ImeProtoEnums.REASON_SHOW_SOFT_INPUT; @@ -291,4 +310,91 @@ public @interface SoftInputShowHideReason { * Hide soft input when {@link InputMethodManager#hideSoftInputFromView(View, int)} gets called. */ int HIDE_SOFT_INPUT_FROM_VIEW = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_FROM_VIEW; + + /** + * Show soft input by legacy (discouraged) call to + * {@link android.inputmethodservice.InputMethodService.InputMethodImpl#showSoftInput}. + */ + int SHOW_SOFT_INPUT_LEGACY_DIRECT = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_LEGACY_DIRECT; + + /** + * Hide soft input by legacy (discouraged) call to + * {@link android.inputmethodservice.InputMethodService.InputMethodImpl#hideSoftInput}. + */ + int HIDE_SOFT_INPUT_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_LEGACY_DIRECT; + + /** + * Show soft input by legacy (discouraged) call to + * {@link android.inputmethodservice.InputMethodService#showWindow}. + */ + int SHOW_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_SHOW_WINDOW_LEGACY_DIRECT; + + /** + * Hide soft input by legacy (discouraged) call to + * {@link android.inputmethodservice.InputMethodService#hideWindow}. + */ + int HIDE_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_WINDOW_LEGACY_DIRECT; + + /** + * Show / Hide soft input by + * {@link android.inputmethodservice.InputMethodService#resetStateForNewConfiguration}. + */ + int RESET_NEW_CONFIGURATION = ImeProtoEnums.REASON_RESET_NEW_CONFIGURATION; + + /** + * Show / Hide soft input by + * {@link android.inputmethodservice.InputMethodService#updateCandidatesVisibility}. + */ + int UPDATE_CANDIDATES_VIEW_VISIBILITY = ImeProtoEnums.REASON_UPDATE_CANDIDATES_VIEW_VISIBILITY; + + /** + * Show / Hide soft input by {@link android.view.InsetsController#onControlsChanged}. + */ + int CONTROLS_CHANGED = ImeProtoEnums.REASON_CONTROLS_CHANGED; + + /** + * Show soft input by + * {@link com.android.wm.shell.common.DisplayImeController#onDisplayConfigurationChanged}. + */ + int DISPLAY_CONFIGURATION_CHANGED = ImeProtoEnums.REASON_DISPLAY_CONFIGURATION_CHANGED; + + /** + * Show soft input by + * {@link com.android.wm.shell.common.DisplayImeController.PerDisplay#insetsChanged}. + */ + int DISPLAY_INSETS_CHANGED = ImeProtoEnums.REASON_DISPLAY_INSETS_CHANGED; + + /** + * Show / Hide soft input by + * {@link com.android.wm.shell.common.DisplayImeController.PerDisplay#insetsControlChanged}. + */ + int DISPLAY_CONTROLS_CHANGED = ImeProtoEnums.REASON_DISPLAY_CONTROLS_CHANGED; + + /** Hide soft input by + * {@link com.android.server.inputmethod.InputMethodManagerService#onUnbindCurrentMethodByReset}. + */ + int UNBIND_CURRENT_METHOD = ImeProtoEnums.REASON_UNBIND_CURRENT_METHOD; + + /** Hide soft input by {@link android.view.ImeInsetsSourceConsumer#onAnimationStateChanged}. */ + int HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED = + ImeProtoEnums.REASON_HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED; + + /** Hide soft input when we already have a {@link android.view.InsetsSourceControl} by + * {@link android.view.ImeInsetsSourceConsumer#requestHide}. + */ + int HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL = + ImeProtoEnums.REASON_HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL; + + /** + * Show soft input by + * {@link android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int)}. + */ + int SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT = + ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT; + + /** + * Show soft input by the deprecated + * {@link InputMethodManager#showSoftInputFromInputMethod(IBinder, int)}. + */ + int SHOW_SOFT_INPUT_IMM_DEPRECATION = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IMM_DEPRECATION; } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 1f4503a69428..dc3b5a8846cf 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -65,12 +65,21 @@ interface IInputMethodManager { InputMethodSubtype getLastInputMethodSubtype(int userId); boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, - in @nullable ImeTracker.Token statsToken, int flags, int lastClickToolType, + in ImeTracker.Token statsToken, int flags, int lastClickToolType, in @nullable ResultReceiver resultReceiver, int reason); boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, - in @nullable ImeTracker.Token statsToken, int flags, + in ImeTracker.Token statsToken, int flags, in @nullable ResultReceiver resultReceiver, int reason); + /** + * A test API for CTS to request hiding the current soft input window, with the request origin + * on the server side. + */ + @EnforcePermission("TEST_INPUT_METHOD") + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + + "android.Manifest.permission.TEST_INPUT_METHOD)") + void hideSoftInputFromServerForTest(); + // 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. diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index 8c93fbbc6b47..48ba52621f64 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -24,8 +24,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.AdditionalMatchers.and; +import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -36,6 +38,7 @@ import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.WindowManager.BadTokenException; import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.ImeTracker; import android.widget.TextView; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -96,12 +99,12 @@ public class ImeInsetsSourceConsumerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // test if setVisibility can show IME mImeConsumer.onWindowFocusGained(true); - mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); + mController.show(WindowInsets.Type.ime(), true /* fromIme */, ImeTracker.Token.empty()); mController.cancelExistingAnimations(); assertTrue((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0); // test if setVisibility can hide IME - mController.hide(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); + mController.hide(WindowInsets.Type.ime(), true /* fromIme */, ImeTracker.Token.empty()); mController.cancelExistingAnimations(); assertFalse((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0); }); @@ -114,8 +117,9 @@ public class ImeInsetsSourceConsumerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Request IME visible before control is available. + final var statsToken = ImeTracker.Token.empty(); mImeConsumer.onWindowFocusGained(true); - mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); + mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken); // set control and verify visibility is applied. InsetsSourceControl control = new InsetsSourceControl(ID_IME, @@ -124,10 +128,10 @@ public class ImeInsetsSourceConsumerTest { // IME show animation should be triggered when control becomes available. verify(mController).applyAnimation( eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */, - any() /* statsToken */); + eq(statsToken)); verify(mController, never()).applyAnimation( eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */, - any() /* statsToken */); + eq(statsToken)); }); } @@ -152,9 +156,9 @@ public class ImeInsetsSourceConsumerTest { // Request IME visible before control is available. mImeConsumer.onWindowFocusGained(hasWindowFocus); final boolean imeVisible = hasWindowFocus && hasViewFocus; + final var statsToken = ImeTracker.Token.empty(); if (imeVisible) { - mController.show(WindowInsets.Type.ime(), true /* fromIme */, - null /* statsToken */); + mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken); } // set control and verify visibility is applied. @@ -168,23 +172,25 @@ public class ImeInsetsSourceConsumerTest { // and expect skip animation state after getAndClearSkipAnimationOnce invoked. mController.onControlsChanged(new InsetsSourceControl[]{ control }); verify(control).getAndClearSkipAnimationOnce(); + // This ends up creating a new request when we gain control, + // so the statsToken won't match. verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, - eq(expectSkipAnim) /* skipAnim */, eq(null) /* statsToken */); + eq(expectSkipAnim) /* skipAnim */, and(not(eq(statsToken)), notNull())); } // If previously hasViewFocus is false, verify when requesting the IME visible next // time will not skip animation. if (!hasViewFocus) { - mController.show(WindowInsets.Type.ime(), true /* fromIme */, - null /* statsToken */); + final var statsTokenNext = ImeTracker.Token.empty(); + mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsTokenNext); mController.onControlsChanged(new InsetsSourceControl[]{ control }); // Verify IME show animation should be triggered when control becomes available and // the animation will be skipped by getAndClearSkipAnimationOnce invoked. verify(control).getAndClearSkipAnimationOnce(); verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */, - eq(false) /* skipAnim */, eq(null) /* statsToken */); + eq(false) /* skipAnim */, eq(statsTokenNext)); } }); } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 1568174e1955..316e191eecbd 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -256,7 +256,7 @@ public class InsetsControllerTest { mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener); mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true); // since there is no focused view, forcefully make IME visible. - mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); // When using the animation thread, this must not invoke onReady() mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); }); @@ -273,14 +273,14 @@ public class InsetsControllerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true); // since there is no focused view, forcefully make IME visible. - mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); mController.show(all()); // quickly jump to final state by cancelling it. mController.cancelExistingAnimations(); - final @InsetsType int types = navigationBars() | statusBars() | ime(); + @InsetsType final int types = navigationBars() | statusBars() | ime(); assertEquals(types, mController.getRequestedVisibleTypes() & types); - mController.hide(ime(), true /* fromIme */, null /* statsToken */); + mController.hide(ime(), true /* fromIme */, ImeTracker.Token.empty()); mController.hide(all()); mController.cancelExistingAnimations(); assertEquals(0, mController.getRequestedVisibleTypes() & types); @@ -295,10 +295,10 @@ public class InsetsControllerTest { mController.onControlsChanged(new InsetsSourceControl[] { ime }); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true); - mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); mController.cancelExistingAnimations(); assertTrue(isRequestedVisible(mController, ime())); - mController.hide(ime(), true /* fromIme */, null /* statsToken */); + mController.hide(ime(), true /* fromIme */, ImeTracker.Token.empty()); mController.cancelExistingAnimations(); assertFalse(isRequestedVisible(mController, ime())); mController.getSourceConsumer(ID_IME, ime()).onWindowFocusLost(); @@ -465,7 +465,7 @@ public class InsetsControllerTest { assertFalse(mController.getState().peekSource(ID_IME).isVisible()); // Pretend IME is calling - mController.show(ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); // Gaining control shortly after mController.onControlsChanged(createSingletonControl(ID_IME, ime())); @@ -489,7 +489,7 @@ public class InsetsControllerTest { mController.onControlsChanged(createSingletonControl(ID_IME, ime())); // Pretend IME is calling - mController.show(ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime())); mController.cancelExistingAnimations(); @@ -567,7 +567,7 @@ public class InsetsControllerTest { verify(listener, never()).onReady(any(), anyInt()); // Pretend that IME is calling. - mController.show(ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); // Ready gets deferred until next predraw mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); @@ -651,7 +651,7 @@ public class InsetsControllerTest { mController.onControlsChanged(createSingletonControl(ID_IME, ime())); // Pretend IME is calling - mController.show(ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); InsetsState copy = new InsetsState(mController.getState(), true /* copySources */); copy.peekSource(ID_IME).setFrame(0, 1, 2, 3); @@ -851,7 +851,7 @@ public class InsetsControllerTest { // Showing invisible ime should only causes insets change once. clearInvocations(mTestHost); - mController.show(ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); verify(mTestHost, times(1)).notifyInsetsChanged(); // Sending the same insets state should not cause insets change. @@ -918,7 +918,7 @@ public class InsetsControllerTest { assertNull(imeInsetsConsumer.getControl()); // Verify IME requested visibility should be updated to IME consumer from controller. - mController.show(ime(), true /* fromIme */, null /* statsToken */); + mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty()); assertTrue(isRequestedVisible(mController, ime())); mController.hide(ime()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 2ea43162d225..ad01d0fa311a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -20,12 +20,12 @@ import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_CANCEL; import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_END; import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_START; import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY; -import static android.view.inputmethod.ImeTracker.TOKEN_NONE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.res.Configuration; @@ -51,6 +51,7 @@ import android.view.inputmethod.InputMethodManagerGlobal; import androidx.annotation.VisibleForTesting; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.wm.shell.sysui.ShellInit; import java.util.ArrayList; @@ -122,7 +123,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } if (mDisplayController.getDisplayLayout(displayId).rotation() != pd.mRotation && isImeShowing(displayId)) { - pd.startAnimation(true, false /* forceRestart */, null /* statsToken */); + pd.startAnimation(true, false /* forceRestart */, + SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED); } } @@ -257,7 +259,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mInsetsState.set(insetsState, true /* copySources */); if (mImeShowing && !Objects.equals(oldFrame, newFrame) && newSourceVisible) { if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation"); - startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */); + startAnimation(mImeShowing, true /* forceRestart */, + SoftInputShowHideReason.DISPLAY_INSETS_CHANGED); } } @@ -291,7 +294,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged final boolean positionChanged = !imeSourceControl.getSurfacePosition().equals(lastSurfacePosition); if (positionChanged) { - startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */); + startAnimation(mImeShowing, true /* forceRestart */, + SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED); } } else { if (!haveSameLeash(mImeSourceControl, imeSourceControl)) { @@ -384,7 +388,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } private void startAnimation(final boolean show, final boolean forceRestart, - @Nullable ImeTracker.Token statsToken) { + @SoftInputShowHideReason int reason) { + final var imeSource = mInsetsState.peekSource(InsetsSource.ID_IME); + if (imeSource == null || mImeSourceControl == null) { + return; + } + final var statsToken = ImeTracker.forLogging().onStart( + show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_WM_SHELL, + reason, false /* fromUser */); + + startAnimation(show, forceRestart, statsToken); + } + + private void startAnimation(final boolean show, final boolean forceRestart, + @NonNull final ImeTracker.Token statsToken) { final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME); if (imeSource == null || mImeSourceControl == null) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE); @@ -458,7 +475,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE); mAnimation.addListener(new AnimatorListenerAdapter() { private boolean mCancelled = false; - @Nullable + @NonNull private final ImeTracker.Token mStatsToken = statsToken; @Override @@ -484,7 +501,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START, - statsToken != null ? statsToken.getTag() : TOKEN_NONE, + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, mDisplayId, mAnimationDirection, alpha, startY , endY, Objects.toString(mImeSourceControl.getLeash()), Objects.toString(mImeSourceControl.getInsetsHint()), @@ -500,7 +517,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mCancelled = true; if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL, - statsToken != null ? statsToken.getTag() : TOKEN_NONE, mDisplayId, + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, + mDisplayId, Objects.toString(mImeSourceControl.getInsetsHint())); } } @@ -528,7 +546,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END, - statsToken != null ? statsToken.getTag() : TOKEN_NONE, + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, mDisplayId, mAnimationDirection, endY, Objects.toString(mImeSourceControl.getLeash()), Objects.toString(mImeSourceControl.getInsetsHint()), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java index 9bdda14cf00b..ca06024a9adb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java @@ -277,8 +277,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan * * @param types {@link InsetsType} to show * @param fromIme true if this request originated from IME (InputMethodService). - * @param statsToken the token tracking the current IME show request - * or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ default void showInsets(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {} @@ -288,8 +287,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan * * @param types {@link InsetsType} to hide * @param fromIme true if this request originated from IME (InputMethodService). - * @param statsToken the token tracking the current IME hide request - * or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ default void hideInsets(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index 01e2f988fbfc..2c0aa12f22d2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -38,6 +38,7 @@ import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; +import android.view.inputmethod.ImeTracker; import androidx.test.filters.SmallTest; @@ -51,6 +52,12 @@ import org.mockito.MockitoAnnotations; import java.util.concurrent.Executor; +/** + * Tests for the display IME controller. + * + * <p> Build/Install/Run: + * atest WMShellUnitTests:DisplayImeControllerTest + */ @SmallTest public class DisplayImeControllerTest extends ShellTestCase { @@ -99,13 +106,13 @@ public class DisplayImeControllerTest extends ShellTestCase { @Test public void showInsets_schedulesNoWorkOnExecutor() { - mPerDisplay.showInsets(ime(), true /* fromIme */, null /* statsToken */); + mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty()); verifyZeroInteractions(mExecutor); } @Test public void hideInsets_schedulesNoWorkOnExecutor() { - mPerDisplay.hideInsets(ime(), true /* fromIme */, null /* statsToken */); + mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty()); verifyZeroInteractions(mExecutor); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java index 956f1cd419c2..669e433ba386 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java @@ -50,6 +50,12 @@ import org.mockito.MockitoAnnotations; import java.util.List; +/** + * Tests for the display insets controller. + * + * <p> Build/Install/Run: + * atest WMShellUnitTests:DisplayInsetsControllerTest + */ @SmallTest public class DisplayInsetsControllerTest extends ShellTestCase { @@ -114,9 +120,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase { mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null); mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null); mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mExecutor.flushAll(); assertTrue(defaultListener.topFocusedWindowChangedCount == 1); @@ -136,9 +142,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase { mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null); mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null); mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mExecutor.flushAll(); assertTrue(defaultListener.topFocusedWindowChangedCount == 1); diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index f1698dd0f025..8826e3d2d345 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.ResultReceiver; import android.util.EventLog; import android.util.Slog; +import android.view.MotionEvent; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; @@ -75,7 +76,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override - public void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, + public void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final IInputMethodInvoker curMethod = mService.getCurMethodLocked(); @@ -88,7 +89,8 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) { if (DEBUG_IME_VISIBILITY) { - EventLog.writeEvent(IMF_SHOW_IME, statsToken.getTag(), + EventLog.writeEvent(IMF_SHOW_IME, + statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE, Objects.toString(mService.mCurFocusedWindow), InputMethodDebug.softInputDisplayReasonToString(reason), InputMethodDebug.softInputModeToString( @@ -102,7 +104,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override - public void performHideIme(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, + public void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final IInputMethodInvoker curMethod = mService.getCurMethodLocked(); if (curMethod != null) { @@ -118,7 +120,8 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not. if (curMethod.hideSoftInput(hideInputToken, statsToken, 0, resultReceiver)) { if (DEBUG_IME_VISIBILITY) { - EventLog.writeEvent(IMF_HIDE_IME, statsToken.getTag(), + EventLog.writeEvent(IMF_HIDE_IME, + statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE, Objects.toString(mService.mCurFocusedWindow), InputMethodDebug.softInputDisplayReasonToString(reason), InputMethodDebug.softInputModeToString( @@ -132,14 +135,16 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override - public void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @ImeVisibilityStateComputer.VisibilityState int state) { - applyImeVisibility(windowToken, statsToken, state, -1 /* ignore reason */); + applyImeVisibility(windowToken, statsToken, state, + SoftInputShowHideReason.NOT_SET /* ignore reason */); } @GuardedBy("ImfLock.class") void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, - @ImeVisibilityStateComputer.VisibilityState int state, int reason) { + @ImeVisibilityStateComputer.VisibilityState int state, + @SoftInputShowHideReason int reason) { switch (state) { case STATE_SHOW_IME: ImeTracker.forLogging().onProgress(statsToken, @@ -164,18 +169,20 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { } break; case STATE_HIDE_IME_EXPLICIT: - mService.hideCurrentInputLocked(windowToken, statsToken, 0, null, reason); + mService.hideCurrentInputLocked(windowToken, statsToken, + 0 /* flags */, null /* resultReceiver */, reason); break; case STATE_HIDE_IME_NOT_ALWAYS: mService.hideCurrentInputLocked(windowToken, statsToken, - InputMethodManager.HIDE_NOT_ALWAYS, null, reason); + InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason); break; case STATE_SHOW_IME_IMPLICIT: mService.showCurrentInputLocked(windowToken, statsToken, - InputMethodManager.SHOW_IMPLICIT, null, reason); + InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN, + null /* resultReceiver */, reason); break; case STATE_SHOW_IME_SNAPSHOT: - showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked(), null); + showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked()); break; case STATE_REMOVE_IME_SNAPSHOT: removeImeScreenshot(mService.getDisplayIdToShowImeLocked()); @@ -187,11 +194,10 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override - public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId, - @Nullable ImeTracker.Token statsToken) { + public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) { if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) { mService.onShowHideSoftInputRequested(false /* show */, imeTarget, - SHOW_IME_SCREENSHOT_FROM_IMMS, statsToken); + SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */); return true; } return false; @@ -202,7 +208,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { public boolean removeImeScreenshot(int displayId) { if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) { mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow, - REMOVE_IME_SCREENSHOT_FROM_IMMS, null); + REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */); return true; } return false; diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java index 776184fb098c..a380bc1ca171 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java @@ -201,7 +201,7 @@ final class IInputMethodInvoker { // TODO(b/192412909): Convert this back to void method @AnyThread - boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, + boolean showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { try { mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver); @@ -214,8 +214,8 @@ final class IInputMethodInvoker { // TODO(b/192412909): Convert this back to void method @AnyThread - boolean hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, int flags, - ResultReceiver resultReceiver) { + boolean hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, + int flags, ResultReceiver resultReceiver) { try { mTarget.hideSoftInput(hideInputToken, statsToken, flags, resultReceiver); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java index d06c31cf36f0..85ab77355c9a 100644 --- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java +++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java @@ -75,34 +75,12 @@ public final class ImeTrackerService extends IImeTracker.Stub { @NonNull @Override - public ImeTracker.Token onRequestShow(@NonNull String tag, int uid, + public ImeTracker.Token onStart(@NonNull String tag, int uid, @ImeTracker.Type int type, @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) { final var binder = new Binder(); final var token = new ImeTracker.Token(binder, tag); - final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN, - origin, reason, fromUser); - synchronized (mLock) { - mHistory.addEntry(binder, entry); - - // Register a delayed task to handle the case where the new entry times out. - mHandler.postDelayed(() -> { - synchronized (mLock) { - mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, - ImeTracker.PHASE_NOT_SET); - } - }, TIMEOUT_MS); - } - return token; - } - - @NonNull - @Override - public ImeTracker.Token onRequestHide(@NonNull String tag, int uid, - @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) { - final var binder = new Binder(); - final var token = new ImeTracker.Token(binder, tag); - final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN, - origin, reason, fromUser); + final var entry = new History.Entry(tag, uid, type, ImeTracker.STATUS_RUN, origin, reason, + fromUser); synchronized (mLock) { mHistory.addEntry(binder, entry); @@ -158,7 +136,7 @@ public final class ImeTrackerService extends IImeTracker.Stub { /** * Updates the IME request tracking token with new information available in IMMS. * - * @param statsToken the token corresponding to the current IME request. + * @param statsToken the token tracking the current IME request. * @param requestWindowName the name of the window that created the IME request. */ public void onImmsUpdate(@NonNull ImeTracker.Token statsToken, @@ -223,7 +201,7 @@ public final class ImeTrackerService extends IImeTracker.Stub { * Sets the live entry corresponding to the tracking token, if it exists, as finished, * and uploads the data for metrics. * - * @param statsToken the token corresponding to the current IME request. + * @param statsToken the token tracking the current IME request. * @param status the finish status of the IME request. * @param phase the phase the IME request finished at, if it exists * (or {@link ImeTracker#PHASE_NOT_SET} otherwise). diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java index 29fa36982351..9f2b84d9bfa5 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -17,7 +17,6 @@ package com.android.server.inputmethod; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.IBinder; import android.os.ResultReceiver; import android.view.inputmethod.ImeTracker; @@ -34,12 +33,12 @@ interface ImeVisibilityApplier { * Performs showing IME on top of the given window. * * @param showInputToken A token that represents the requester to show IME. - * @param statsToken A token that tracks the progress of an IME request. + * @param statsToken The token tracking the current IME request. * @param resultReceiver If non-null, this will be called back to the caller when * it has processed request to tell what it has done. * @param reason The reason for requesting to show IME. */ - default void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, + default void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {} @@ -47,12 +46,12 @@ interface ImeVisibilityApplier { * Performs hiding IME to the given window * * @param hideInputToken A token that represents the requester to hide IME. - * @param statsToken A token that tracks the progress of an IME request. + * @param statsToken The token tracking the current IME request. * @param resultReceiver If non-null, this will be called back to the caller when * it has processed request to tell what it has done. * @param reason The reason for requesting to hide IME. */ - default void performHideIme(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, + default void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {} /** @@ -60,10 +59,10 @@ interface ImeVisibilityApplier { * according to the given visibility state. * * @param windowToken The token of a window for applying the IME visibility - * @param statsToken A token that tracks the progress of an IME request. + * @param statsToken The token tracking the current IME request. * @param state The new IME visibility state for the applier to handle */ - default void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, + default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @ImeVisibilityStateComputer.VisibilityState int state) {} /** @@ -84,11 +83,9 @@ interface ImeVisibilityApplier { * * @param windowToken The token of a window to show the IME screenshot. * @param displayId The unique id to identify the display - * @param statsToken A token that tracks the progress of an IME request. * @return {@code true} if success, {@code false} otherwise. */ - default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId, - @Nullable ImeTracker.Token statsToken) { + default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId) { return false; } diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 0dd48ae6c9e1..743b8e382347 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -212,9 +212,11 @@ public final class ImeVisibilityStateComputer { boolean visibleRequested, boolean removed) { if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed) && mCurVisibleImeLayeringOverlay != null) { - mService.onApplyImeVisibilityFromComputer(imeInputTarget, - new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, - SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE)); + final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE; + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */); + mService.onApplyImeVisibilityFromComputer(imeInputTarget, statsToken, + new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason)); } mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; } @@ -224,7 +226,7 @@ public final class ImeVisibilityStateComputer { /** * Called when {@link InputMethodManagerService} is processing the show IME request. * - * @param statsToken The token for tracking this show request. + * @param statsToken The token tracking the current IME request. * @return {@code true} when the show request can proceed. */ boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, @@ -250,7 +252,7 @@ public final class ImeVisibilityStateComputer { /** * Called when {@link InputMethodManagerService} is processing the hide IME request. * - * @param statsToken The token for tracking this hide request. + * @param statsToken The token tracking the current IME request. * @return {@code true} when the hide request can proceed. */ boolean canHideIme(@NonNull ImeTracker.Token statsToken, diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 307b70a89634..ac586bc1f6bb 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -578,7 +578,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return mBindingController.hasMainConnection(); } - /** The token tracking the current IME request or {@code null} otherwise. */ + /** + * The token tracking the current IME show request that is waiting for a connection to an IME, + * otherwise {@code null}. + */ @Nullable private ImeTracker.Token mCurStatsToken; @@ -1128,11 +1131,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard( accessibilitySoftKeyboardSetting); if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) { - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, - 0 /* flags */, null /* resultReceiver */, + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE); } else if (isShowRequestedForCurrentWindow()) { - showCurrentInputImplicitLocked(mCurFocusedWindow, + showCurrentInputLocked(mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } } else if (stylusHandwritingEnabledUri.equals(uri)) { @@ -1613,8 +1615,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } // Hide soft input before user switch task since switch task may block main handler a while // and delayed the hideCurrentInputLocked(). - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, - null /* resultReceiver */, SoftInputShowHideReason.HIDE_SWITCH_USER); + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_SWITCH_USER); final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId, clientToBeReset); mUserSwitchHandlerTask = task; @@ -2202,8 +2204,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub clearClientSessionLocked(client); clearClientSessionForAccessibilityLocked(client); if (mCurClient == client) { - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, - null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_REMOVE_CLIENT); if (mBoundToMethod) { mBoundToMethod = false; IInputMethodInvoker curMethod = getCurMethodLocked(); @@ -2277,8 +2279,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // service, that wouldn't make the attached IME token validity check in time) // As a result, we have to notify WM to apply IME visibility before clearing the // binding states in the first place. - mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, mCurStatsToken, - STATE_HIDE_IME); + final var statsToken = createStatsTokenForFocusedClient(false /* show */, + SoftInputShowHideReason.UNBIND_CURRENT_METHOD); + mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken, STATE_HIDE_IME); } } @@ -2355,10 +2358,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (isShowRequestedForCurrentWindow()) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); // Re-use current statsToken, if it exists. - final ImeTracker.Token statsToken = mCurStatsToken; + final var statsToken = mCurStatsToken != null ? mCurStatsToken + : createStatsTokenForFocusedClient(true /* show */, + SoftInputShowHideReason.ATTACH_NEW_INPUT); mCurStatsToken = null; showCurrentInputLocked(mCurFocusedWindow, statsToken, - mVisibilityStateComputer.getShowFlags(), + mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); } @@ -2450,8 +2455,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) { - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, - null /* resultReceiver */, + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE); return InputBindResult.NO_IME; } @@ -3371,8 +3375,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, - int lastClickTooType, ResultReceiver resultReceiver, + @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, + int lastClickToolType, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput"); int uid = Binder.getCallingUid(); @@ -3388,7 +3392,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final long ident = Binder.clearCallingIdentity(); try { if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(windowToken, statsToken, flags, lastClickTooType, + return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType, resultReceiver, reason); } finally { Binder.restoreCallingIdentity(ident); @@ -3630,24 +3634,18 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("ImfLock.class") - boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken, - @InputMethodManager.ShowFlags int flags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + private boolean showCurrentInputLocked(IBinder windowToken, + @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) { + final var statsToken = createStatsTokenForFocusedClient(true /* show */, reason); return showCurrentInputLocked(windowToken, statsToken, flags, - MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason); + MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason); } @GuardedBy("ImfLock.class") - private boolean showCurrentInputLocked(IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, - int lastClickToolType, ResultReceiver resultReceiver, + boolean showCurrentInputLocked(IBinder windowToken, + @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, + int lastClickToolType, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { - // Create statsToken is none exists. - if (statsToken == null) { - statsToken = createStatsTokenForFocusedClient(true /* show */, - ImeTracker.ORIGIN_SERVER_START_INPUT, reason, false /* fromUser */); - } - if (!mVisibilityStateComputer.onImeShowFlags(statsToken, flags)) { return false; } @@ -3685,7 +3683,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, + @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { int uid = Binder.getCallingUid(); ImeTracing.getInstance().triggerManagerServiceDump( @@ -3714,17 +3712,29 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } - @GuardedBy("ImfLock.class") - boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken, - @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { - // Create statsToken is none exists. - if (statsToken == null) { - final boolean fromUser = reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY; - statsToken = createStatsTokenForFocusedClient(false /* show */, - ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason, fromUser); + @Override + @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD) + public void hideSoftInputFromServerForTest() { + super.hideSoftInputFromServerForTest_enforcePermission(); + + synchronized (ImfLock.class) { + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_SOFT_INPUT); } + } + + @GuardedBy("ImfLock.class") + private boolean hideCurrentInputLocked(IBinder windowToken, + @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) { + final var statsToken = createStatsTokenForFocusedClient(false /* show */, reason); + return hideCurrentInputLocked(windowToken, statsToken, flags, null /* resultReceiver */, + reason); + } + @GuardedBy("ImfLock.class") + boolean hideCurrentInputLocked(IBinder windowToken, @NonNull ImeTracker.Token statsToken, + @InputMethodManager.HideFlags int flags, @Nullable ResultReceiver resultReceiver, + @SoftInputShowHideReason int reason) { if (!mVisibilityStateComputer.canHideIme(statsToken, flags)) { return false; } @@ -3901,9 +3911,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Slog.w(TAG, "If you need to impersonate a foreground user/profile from" + " a background user, use EditorInfo.targetInputMethodUser with" + " INTERACT_ACROSS_USERS_FULL permission."); - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, - 0 /* flags */, - null /* resultReceiver */, + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_INVALID_USER); return InputBindResult.INVALID_USER; } @@ -4011,11 +4019,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.computeState(windowState, isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags)); if (imeVisRes != null) { + boolean isShow = false; switch (imeVisRes.getReason()) { case SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY: case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV: case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV: case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE: + isShow = true; + if (editorInfo != null) { res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, editorInfo, startInputFlags, @@ -4025,8 +4036,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } break; } - - mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null /* statsToken */, + final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason()); + mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken, imeVisRes.getState(), imeVisRes.getReason()); if (imeVisRes.getReason() == SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW) { @@ -4054,13 +4065,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("ImfLock.class") - private void showCurrentInputImplicitLocked(@NonNull IBinder windowToken, - @SoftInputShowHideReason int reason) { - showCurrentInputLocked(windowToken, null /* statsToken */, InputMethodManager.SHOW_IMPLICIT, - null /* resultReceiver */, reason); - } - - @GuardedBy("ImfLock.class") private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName, @Nullable ImeTracker.Token statsToken) { if (mCurClient == null || client == null @@ -4762,15 +4766,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible, - @Nullable ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility"); synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { ImeTracker.forLogging().onFailed(statsToken, - ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); + ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; } + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom( windowToken); mVisibilityApplier.applyImeVisibility(requestToken, statsToken, @@ -4848,17 +4854,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void hideMySoftInput(@NonNull IBinder token, @InputMethodManager.HideFlags int flags, - @SoftInputShowHideReason int reason) { + private void hideMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken, + @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput"); synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; } + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final long ident = Binder.clearCallingIdentity(); try { - hideCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags, + hideCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, null /* resultReceiver */, reason); } finally { Binder.restoreCallingIdentity(ident); @@ -4870,18 +4880,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void showMySoftInput(@NonNull IBinder token, @InputMethodManager.ShowFlags int flags) { + private void showMySoftInput(@NonNull IBinder token, @NonNull ImeTracker.Token statsToken, + @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput"); synchronized (ImfLock.class) { if (!calledWithValidTokenLocked(token)) { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); return; } + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final long ident = Binder.clearCallingIdentity(); try { - showCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags, - null /* resultReceiver */, - SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME); + showCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, + MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason); } finally { Binder.restoreCallingIdentity(ident); } @@ -4898,10 +4912,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } - void onApplyImeVisibilityFromComputer(IBinder windowToken, + void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken, @NonNull ImeVisibilityResult result) { synchronized (ImfLock.class) { - mVisibilityApplier.applyImeVisibility(windowToken, null, result.getState(), + mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(), result.getReason()); } } @@ -5002,9 +5016,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_HIDE_ALL_INPUT_METHODS: synchronized (ImfLock.class) { - final @SoftInputShowHideReason int reason = (int) msg.obj; - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, - null /* resultReceiver */, reason); + @SoftInputShowHideReason final int reason = (int) msg.obj; + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, reason); } return true; @@ -5162,7 +5175,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged( mCurFocusedWindow, interactive); if (imeVisRes != null) { - mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null, + // Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker. + mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason()); } // Eligible IME processes use new "setInteractive" protocol. @@ -6670,8 +6684,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final String nextIme; final List<InputMethodInfo> nextEnabledImes; if (userId == mSettings.getUserId()) { - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, - 0 /* flags */, null /* resultReceiver */, + hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); mBindingController.unbindCurrentMethod(); @@ -6800,13 +6813,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub * Creates an IME request tracking token for the current focused client. * * @param show whether this is a show or a hide request. - * @param origin the origin of the IME request. * @param reason the reason why the IME request was created. - * @param fromUser whether this request was created directly from user interaction. */ @NonNull private ImeTracker.Token createStatsTokenForFocusedClient(boolean show, - @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser) { + @SoftInputShowHideReason int reason) { final int uid = mCurFocusedWindowClient != null ? mCurFocusedWindowClient.mUid : -1; @@ -6814,13 +6825,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub ? mCurFocusedWindowEditorInfo.packageName : "uid(" + uid + ")"; - if (show) { - return ImeTracker.forLogging() - .onRequestShow(packageName, uid, origin, reason, fromUser); - } else { - return ImeTracker.forLogging() - .onRequestHide(packageName, uid, origin, reason, fromUser); - } + return ImeTracker.forLogging().onStart(packageName, uid, + show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_SERVER, + reason, false /* fromUser */); } private static final class InputMethodPrivilegedOperationsImpl @@ -6895,12 +6902,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void hideMySoftInput(@InputMethodManager.HideFlags int flags, - @SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) { + public void hideMySoftInput(@NonNull ImeTracker.Token statsToken, + @InputMethodManager.HideFlags int flags, @SoftInputShowHideReason int reason, + AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { - mImms.hideMySoftInput(mToken, flags, reason); + mImms.hideMySoftInput(mToken, statsToken, flags, reason); typedFuture.complete(null); } catch (Throwable e) { typedFuture.completeExceptionally(e); @@ -6909,12 +6917,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void showMySoftInput(@InputMethodManager.ShowFlags int flags, + public void showMySoftInput(@NonNull ImeTracker.Token statsToken, + @InputMethodManager.ShowFlags int flags, @SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { - mImms.showMySoftInput(mToken, flags); + mImms.showMySoftInput(mToken, statsToken, flags, reason); typedFuture.complete(null); } catch (Throwable e) { typedFuture.completeExceptionally(e); @@ -6973,7 +6982,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible, - @Nullable ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken) { mImms.applyImeVisibility(mToken, windowToken, setVisible, statsToken); } diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java index 62d44557ae4c..21fb0f3b4823 100644 --- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java +++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java @@ -183,6 +183,13 @@ public class ZeroJankProxy extends IInputMethodManager.Stub { return true; } + @Override + @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD) + public void hideSoftInputFromServerForTest() throws RemoteException { + super.hideSoftInputFromServerForTest_enforcePermission(); + mInner.hideSoftInputFromServerForTest(); + } + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) @Override public void startInputOrWindowGainedFocusAsync( diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index ea31e632cfb8..9fee3433f6be 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -215,11 +215,11 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { * when {@link android.inputmethodservice.InputMethodService} requests to show IME * on {@param imeTarget}. * - * @param imeTarget imeTarget on which IME show request is coming from. - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param imeTarget imeTarget on which IME request is coming from. + * @param statsToken the token tracking the current IME request. */ void scheduleShowImePostLayout(InsetsControlTarget imeTarget, - @Nullable ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken) { boolean targetChanged = isTargetChangedWithinActivity(imeTarget); mImeRequester = imeTarget; // Cancel the pre-existing stats token, if any. diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index b74eb56ebdca..cc3de7a3462c 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -60,8 +60,8 @@ interface InsetsControlTarget { * Instructs the control target to show inset sources. * * @param types to specify which types of insets source window should be shown. - * @param fromIme {@code true} if IME show request originated from {@link InputMethodService}. - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param fromIme {@code true} if the IME request originated from {@link InputMethodService}. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ default void showInsets(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) { @@ -71,8 +71,8 @@ interface InsetsControlTarget { * Instructs the control target to hide inset sources. * * @param types to specify which types of insets source window should be hidden. - * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}. - * @param statsToken the token tracking the current IME hide request or {@code null} otherwise. + * @param fromIme {@code true} if the IME request originated from {@link InputMethodService}. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ default void hideInsets(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 5df2edc808f6..77319cc0ba8a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -829,20 +829,20 @@ public abstract class WindowManagerInternal { * Show IME on imeTargetWindow once IME has finished layout. * * @param imeTargetWindowToken token of the (IME target) window which IME should be shown. - * @param statsToken the token tracking the current IME show request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request. */ public abstract void showImePostLayout(IBinder imeTargetWindowToken, - @Nullable ImeTracker.Token statsToken); + @NonNull ImeTracker.Token statsToken); /** * Hide IME using imeTargetWindow when requested. * - * @param imeTargetWindowToken token of the (IME target) window on which requests hiding IME. + * @param imeTargetWindowToken token of the (IME target) window which requests hiding IME. * @param displayId the id of the display the IME is on. - * @param statsToken the token tracking the current IME hide request or {@code null} otherwise. + * @param statsToken the token tracking the current IME request. */ public abstract void hideIme(IBinder imeTargetWindowToken, int displayId, - @Nullable ImeTracker.Token statsToken); + @NonNull ImeTracker.Token statsToken); /** * Tell window manager about a package that should be running with a restricted range of diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c93cc074aa3d..02b2fb2e53c1 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8258,12 +8258,17 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void showImePostLayout(IBinder imeTargetWindowToken, - @Nullable ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken) { synchronized (mGlobalLock) { InputTarget imeTarget = getInputTargetFromWindowTokenLocked(imeTargetWindowToken); if (imeTarget == null) { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET); return; } + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET); + Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget(); imeTarget = controlTarget.getWindow(); @@ -8278,7 +8283,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void hideIme(IBinder imeTargetWindowToken, int displayId, - @Nullable ImeTracker.Token statsToken) { + @NonNull ImeTracker.Token statsToken) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.hideIme"); synchronized (mGlobalLock) { WindowState imeTarget = mWindowMap.get(imeTargetWindowToken); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index 1c71a6287c79..1d225ba09bbd 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -30,7 +30,10 @@ import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SH import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT; import static org.junit.Assert.assertThrows; -import static org.mockito.Mockito.any; +import static org.mockito.AdditionalMatchers.and; +import static org.mockito.AdditionalMatchers.not; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -40,6 +43,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.view.Display; +import android.view.inputmethod.ImeTracker; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -56,7 +60,7 @@ import org.junit.runner.RunWith; * Test the behavior of {@link DefaultImeVisibilityApplier} when performing or applying the IME * visibility state. * - * Build/Install/Run: + * <p>Build/Install/Run: * atest FrameworksInputMethodSystemServerTests:DefaultImeVisibilityApplierTest */ @RunWith(AndroidJUnit4.class) @@ -75,7 +79,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testPerformShowIme() throws Exception { synchronized (ImfLock.class) { mVisibilityApplier.performShowIme(new Binder() /* showInputToken */, - null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT); + ImeTracker.Token.empty(), 0 /* showFlags */, null /* resultReceiver */, + SHOW_SOFT_INPUT); } verifyShowSoftInput(false, true, 0 /* showFlags */); } @@ -84,46 +89,66 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testPerformHideIme() throws Exception { synchronized (ImfLock.class) { mVisibilityApplier.performHideIme(new Binder() /* hideInputToken */, - null /* statsToken */, null, HIDE_SOFT_INPUT); + ImeTracker.Token.empty(), null /* resultReceiver */, HIDE_SOFT_INPUT); } verifyHideSoftInput(false, true); } @Test public void testApplyImeVisibility_throwForInvalidState() { - assertThrows(IllegalArgumentException.class, - () -> mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID)); + assertThrows(IllegalArgumentException.class, () -> { + synchronized (ImfLock.class) { + mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), + STATE_INVALID); + } + }); } @Test public void testApplyImeVisibility_showIme() { - mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME); - verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), any()); + final var statsToken = ImeTracker.Token.empty(); + synchronized (ImfLock.class) { + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME); + } + verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken)); } @Test public void testApplyImeVisibility_hideIme() { - mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME); - verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt(), any()); + final var statsToken = ImeTracker.Token.empty(); + synchronized (ImfLock.class) { + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME); + } + verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */, + eq(statsToken)); } @Test public void testApplyImeVisibility_hideImeExplicit() throws Exception { mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; - mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_EXPLICIT); + synchronized (ImfLock.class) { + mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), + STATE_HIDE_IME_EXPLICIT); + } verifyHideSoftInput(true, true); } @Test public void testApplyImeVisibility_hideNotAlways() throws Exception { mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; - mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_NOT_ALWAYS); + synchronized (ImfLock.class) { + mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), + STATE_HIDE_IME_NOT_ALWAYS); + } verifyHideSoftInput(true, true); } @Test public void testApplyImeVisibility_showImeImplicit() throws Exception { - mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT); + synchronized (ImfLock.class) { + mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), + STATE_SHOW_IME_IMPLICIT); + } verifyShowSoftInput(true, true, 0 /* showFlags */); } @@ -135,21 +160,21 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe mInputMethodManagerService.setAttachedClientForTesting(null); startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE); + final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); // Verify hideIme will apply the expected displayId when the default IME // visibility applier app STATE_HIDE_IME. - mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME); + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( - eq(mWindowToken), eq(displayIdToShowIme), eq(null)); + eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken)); } } @Test public void testShowImeScreenshot() { synchronized (ImfLock.class) { - mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY, - null /* statsToken */); + mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY); } verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken), @@ -174,17 +199,20 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe synchronized (ImfLock.class) { // Simulate the system hides the IME when switching IME services in different users. // (e.g. unbinding the IME from the current user to the profile user) + final var statsToken = ImeTracker.Token.empty(); final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); - mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, null, 0, null, + mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, + statsToken, 0 /* flags */, null /* resultReceiver */, HIDE_SWITCH_USER); mInputMethodManagerService.onUnbindCurrentMethodByReset(); // Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing // the IME hidden state. - verify(mVisibilityApplier).applyImeVisibility(eq(mWindowToken), any(), - eq(STATE_HIDE_IME)); + // The unbind will cancel the previous stats token, and create a new one internally. + verify(mVisibilityApplier).applyImeVisibility( + eq(mWindowToken), any(), eq(STATE_HIDE_IME)); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( - eq(mWindowToken), eq(displayIdToShowIme), eq(null)); + eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull())); } } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java index fae5f86e4007..a22cacbcb5df 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java @@ -39,9 +39,12 @@ import static com.android.server.inputmethod.InputMethodManagerService.ImeDispla import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.notNull; + import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethodManager; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -58,7 +61,7 @@ import org.mockito.ArgumentCaptor; * Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when * requesting the IME visibility. * - * Build/Install/Run: + * <p> Build/Install/Run: * atest FrameworksInputMethodSystemServerTests:ImeVisibilityStateComputerTest */ @RunWith(AndroidJUnit4.class) @@ -91,7 +94,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes @Test public void testRequestImeVisibility_showImplicit() { initImeTargetWindowState(mWindowToken); - boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT); + boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), + InputMethodManager.SHOW_IMPLICIT); mComputer.requestImeVisibility(mWindowToken, res); final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken); @@ -106,7 +110,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes @Test public void testRequestImeVisibility_showExplicit() { initImeTargetWindowState(mWindowToken); - boolean res = mComputer.onImeShowFlags(null, 0 /* showFlags */); + boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); mComputer.requestImeVisibility(mWindowToken, res); final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken); @@ -125,7 +129,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes @Test public void testRequestImeVisibility_showExplicit_thenShowImplicit() { initImeTargetWindowState(mWindowToken); - mComputer.onImeShowFlags(null, 0 /* showFlags */); + mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); assertThat(mComputer.mRequestedShowExplicitly).isTrue(); mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT); @@ -139,10 +143,10 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes @Test public void testRequestImeVisibility_showForced_thenShowExplicit() { initImeTargetWindowState(mWindowToken); - mComputer.onImeShowFlags(null, InputMethodManager.SHOW_FORCED); + mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_FORCED); assertThat(mComputer.mShowForced).isTrue(); - mComputer.onImeShowFlags(null, 0 /* showFlags */); + mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */); assertThat(mComputer.mShowForced).isTrue(); } @@ -152,7 +156,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN); initImeTargetWindowState(mWindowToken); - boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT); + boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), + InputMethodManager.SHOW_IMPLICIT); mComputer.requestImeVisibility(mWindowToken, res); final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken); @@ -170,7 +175,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); initImeTargetWindowState(mWindowToken); - boolean res = mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT); + boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), + InputMethodManager.SHOW_IMPLICIT); mComputer.requestImeVisibility(mWindowToken, res); final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken); @@ -188,7 +194,8 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes mComputer.setInputShown(true); initImeTargetWindowState(mWindowToken); - assertThat(mComputer.canHideIme(null, InputMethodManager.HIDE_NOT_ALWAYS)).isTrue(); + assertThat(mComputer.canHideIme(ImeTracker.Token.empty(), + InputMethodManager.HIDE_NOT_ALWAYS)).isTrue(); mComputer.requestImeVisibility(mWindowToken, false); final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken); @@ -281,7 +288,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass( ImeVisibilityResult.class); verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(), - resultCaptor.capture()); + notNull() /* statsToken */, resultCaptor.capture()); final IBinder imeInputTarget = targetCaptor.getValue(); final ImeVisibilityResult result = resultCaptor.getValue(); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index a1be00aab340..f4d95afaacf4 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -277,8 +278,9 @@ public class InputMethodManagerServiceTestBase { .setCurrentMethodVisible(); } verify(mMockInputMethod, times(showSoftInput ? 1 : 0)) - .showSoftInput(any(), any(), - showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt(), any()); + .showSoftInput(any() /* showInputToken */ , notNull() /* statsToken */, + showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt() /* flags*/, + any() /* resultReceiver */); } protected void verifyHideSoftInput(boolean setNotVisible, boolean hideSoftInput) @@ -288,6 +290,7 @@ public class InputMethodManagerServiceTestBase { .setCurrentMethodNotVisible(); } verify(mMockInputMethod, times(hideSoftInput ? 1 : 0)) - .hideSoftInput(any(), any(), anyInt(), any()); + .hideSoftInput(any() /* hideInputToken */, notNull() /* statsToken */, + anyInt() /* flags */, any() /* resultReceiver */); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java index 20bb549301aa..faa6d97ce0e3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue; import android.graphics.PixelFormat; import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; +import android.view.inputmethod.ImeTracker; import androidx.test.filters.SmallTest; @@ -34,6 +35,12 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +/** + * Tests for the {@link ImeInsetsSourceProvider} class. + * + * <p> Build/Install/Run: + * atest WmTests:ImeInsetsSourceProviderTest + */ @SmallTest @Presubmit @RunWith(WindowTestRunner.class) @@ -56,7 +63,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { mDisplayContent.setImeControlTarget(popup); mDisplayContent.setImeLayeringTarget(appWin); popup.mAttrs.format = PixelFormat.TRANSPARENT; - mImeProvider.scheduleShowImePostLayout(appWin, null /* statsToken */); + mImeProvider.scheduleShowImePostLayout(appWin, ImeTracker.Token.empty()); assertTrue(mImeProvider.isReadyToShowIme()); } @@ -65,7 +72,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { WindowState target = createWindow(null, TYPE_APPLICATION, "app"); mDisplayContent.setImeLayeringTarget(target); mDisplayContent.updateImeInputAndControlTarget(target); - mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */); + mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty()); assertTrue(mImeProvider.isReadyToShowIme()); } @@ -79,7 +86,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { mDisplayContent.setImeLayeringTarget(target); mDisplayContent.setImeControlTarget(target); - mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */); + mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty()); assertFalse(mImeProvider.isImeShowing()); mImeProvider.checkShowImePostLayout(); assertTrue(mImeProvider.isImeShowing()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index cd3ce9192509..c8ad4bd47880 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -104,6 +104,7 @@ import android.view.SurfaceControl; import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.inputmethod.ImeTracker; import android.window.ClientWindowFrames; import android.window.ITaskFragmentOrganizer; import android.window.TaskFragmentOrganizer; @@ -126,7 +127,7 @@ import java.util.List; /** * Tests for the {@link WindowState} class. * - * Build/Install/Run: + * <p> Build/Install/Run: * atest WmTests:WindowStateTests */ @SmallTest @@ -1099,7 +1100,7 @@ public class WindowStateTests extends WindowTestsBase { mDisplayContent.setImeInputTarget(app); app.setRequestedVisibleTypes(ime(), ime()); assertTrue(mDisplayContent.shouldImeAttachedToApp()); - controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */); + controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty()); controller.getImeSourceProvider().getSource().setVisible(true); controller.updateAboveInsetsState(false); @@ -1137,7 +1138,7 @@ public class WindowStateTests extends WindowTestsBase { mDisplayContent.setImeInputTarget(app); app.setRequestedVisibleTypes(ime(), ime()); assertTrue(mDisplayContent.shouldImeAttachedToApp()); - controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */); + controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty()); controller.getImeSourceProvider().getSource().setVisible(true); controller.updateAboveInsetsState(false); |