summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Cosmin Băieș <cosminbaies@google.com> 2022-11-14 14:36:36 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-11-14 14:36:36 +0000
commitfc03fbd025e747dea1dbbb652fa1dfdc415b7ab9 (patch)
tree311f710a5147273e4d34b9f1c19d278587730dbd
parent70453947debc9b35f6c76fd66007450f4e9ebcd2 (diff)
parent44ff0dd9eb26d429620dddff3511a84f184986a2 (diff)
Merge changes from topic "revert-20437706-revert-20313761-IME tracking - hide-HZXGDCCPOZ-XOCVITMXNR"
* changes: Revert^2 "Input latency & reliability tracking - hide flow" Revert^2 "Input latency & reliability tracking"
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java36
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java33
-rw-r--r--core/java/android/view/IDisplayWindowInsetsController.aidl5
-rw-r--r--core/java/android/view/IWindow.aidl7
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java15
-rw-r--r--core/java/android/view/InsetsAnimationControlRunner.java8
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java11
-rw-r--r--core/java/android/view/InsetsController.java97
-rw-r--r--core/java/android/view/InsetsResizeAnimationRunner.java7
-rw-r--r--core/java/android/view/ViewRootImpl.java38
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java14
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.aidl19
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java488
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java14
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java58
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethod.aidl7
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl4
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java20
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java8
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl10
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java23
-rw-r--r--core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java3
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java18
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java11
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java230
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java27
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java9
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java33
-rw-r--r--services/core/java/com/android/server/wm/InsetsControlTarget.java10
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java23
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestIWindow.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java7
44 files changed, 1189 insertions, 268 deletions
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index d23fb363df1c..d55367f19a70 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -32,6 +32,7 @@ import android.os.ResultReceiver;
import android.util.Log;
import android.view.InputChannel;
import android.view.MotionEvent;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
@@ -93,9 +94,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
final int mTargetSdkVersion;
/**
- * This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()}
- * so that {@link RemoteInputConnection} can query if {@link #unbindInput()} has already been
- * called or not, mainly to avoid unnecessary blocking operations.
+ * This is not {@code null} only between {@link #bindInput(InputBinding)} and
+ * {@link #unbindInput()} so that {@link RemoteInputConnection} can query if
+ * {@link #unbindInput()} has already been called or not, mainly to avoid unnecessary
+ * blocking operations.
*
* <p>This field must be set and cleared only from the binder thread(s), where the system
* guarantees that {@link #bindInput(InputBinding)},
@@ -219,18 +221,26 @@ class IInputMethodWrapper extends IInputMethod.Stub
return;
case DO_SHOW_SOFT_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
+ final ImeTracker.Token statsToken = (ImeTracker.Token) args.arg3;
if (isValid(inputMethod, target, "DO_SHOW_SOFT_INPUT")) {
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
inputMethod.showSoftInputWithToken(
- msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
+ msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1, statsToken);
+ } else {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
}
args.recycle();
return;
}
case DO_HIDE_SOFT_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
+ final ImeTracker.Token statsToken = (ImeTracker.Token) args.arg3;
if (isValid(inputMethod, target, "DO_HIDE_SOFT_INPUT")) {
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
- (IBinder) args.arg1);
+ (IBinder) args.arg1, statsToken);
+ } else {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH);
}
args.recycle();
return;
@@ -416,16 +426,20 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_SHOW_SOFT_INPUT,
- flags, showInputToken, resultReceiver));
+ public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ int flags, ResultReceiver resultReceiver) {
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
+ flags, showInputToken, resultReceiver, statsToken));
}
@BinderThread
@Override
- public void hideSoftInput(IBinder hideInputToken, int flags, ResultReceiver resultReceiver) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_HIDE_SOFT_INPUT,
- flags, hideInputToken, resultReceiver));
+ public void hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken,
+ int flags, ResultReceiver resultReceiver) {
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_HIDE_SOFT_INPUT,
+ flags, hideInputToken, resultReceiver, statsToken));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index d902486faced..bf4fc4a72fdc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -124,6 +124,7 @@ import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
import android.view.inputmethod.InputBinding;
@@ -669,6 +670,10 @@ public class InputMethodService extends AbstractInputMethodService {
*/
private IBinder mCurHideInputToken;
+ /** The token tracking the current IME request or {@code null} otherwise. */
+ @Nullable
+ private ImeTracker.Token mCurStatsToken;
+
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
onComputeInsets(mTmpInsets);
if (!mViewsCreated) {
@@ -870,10 +875,12 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder hideInputToken) {
+ IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) {
mSystemCallingHideSoftInput = true;
mCurHideInputToken = hideInputToken;
+ mCurStatsToken = statsToken;
hideSoftInput(flags, resultReceiver);
+ mCurStatsToken = null;
mCurHideInputToken = null;
mSystemCallingHideSoftInput = false;
}
@@ -884,6 +891,7 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
+ ImeTracker.get().onProgress(mCurStatsToken, ImeTracker.PHASE_IME_HIDE_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "hideSoftInput()");
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
&& !mSystemCallingHideSoftInput) {
@@ -918,12 +926,17 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder showInputToken) {
+ IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
mSystemCallingShowSoftInput = true;
mCurShowInputToken = showInputToken;
- showSoftInput(flags, resultReceiver);
- mCurShowInputToken = null;
- mSystemCallingShowSoftInput = false;
+ mCurStatsToken = statsToken;
+ try {
+ showSoftInput(flags, resultReceiver);
+ } finally {
+ mCurStatsToken = null;
+ mCurShowInputToken = null;
+ mSystemCallingShowSoftInput = false;
+ }
}
/**
@@ -932,6 +945,7 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
+ ImeTracker.get().onProgress(mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "showSoftInput()");
// TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods.
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
@@ -947,7 +961,12 @@ public class InputMethodService extends AbstractInputMethodService {
null /* icProto */);
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
+ ImeTracker.get().onProgress(mCurStatsToken,
+ ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
showWindow(true);
+ } else {
+ ImeTracker.get().onFailed(mCurStatsToken,
+ ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
}
setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
@@ -2923,8 +2942,10 @@ public class InputMethodService extends AbstractInputMethodService {
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
null /* icProto */);
+ ImeTracker.get().onProgress(mCurStatsToken,
+ ImeTracker.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER);
mPrivOps.applyImeVisibilityAsync(setVisible
- ? mCurShowInputToken : mCurHideInputToken, setVisible);
+ ? mCurShowInputToken : mCurHideInputToken, setVisible, mCurStatsToken);
}
private void finishViews(boolean finishingInput) {
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
index 0769f1209a2b..91270d4160f5 100644
--- a/core/java/android/view/IDisplayWindowInsetsController.aidl
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -19,6 +19,7 @@ package android.view;
import android.content.ComponentName;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.inputmethod.ImeTracker;
/**
* Singular controller of insets to use when there isn't another obvious controller available.
@@ -48,10 +49,10 @@ oneway interface IDisplayWindowInsetsController {
/**
* @see IWindow#showInsets
*/
- void showInsets(int types, boolean fromIme);
+ void showInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
/**
* @see IWindow#hideInsets
*/
- void hideInsets(int types, boolean fromIme);
+ void hideInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index a8564740c0c8..8e16f24b154f 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -29,6 +29,7 @@ import android.view.InsetsState;
import android.view.IScrollCaptureResponseListener;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
@@ -68,16 +69,18 @@ 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.
*/
- void showInsets(int types, boolean fromIme);
+ void showInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
/**
* Called when a set of insets source window should be hidden by policy.
*
* @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.
*/
- void hideInsets(int types, boolean fromIme);
+ void hideInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken);
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 27b4d8744452..e775969238a0 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -58,6 +58,7 @@ import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Interpolator;
+import android.view.inputmethod.ImeTracker;
import com.android.internal.annotations.VisibleForTesting;
@@ -68,7 +69,7 @@ import java.util.Objects;
* Implements {@link WindowInsetsAnimationController}
* @hide
*/
-@VisibleForTesting
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class InsetsAnimationControlImpl implements InternalInsetsAnimationController,
InsetsAnimationControlRunner {
@@ -96,6 +97,8 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
/** @see WindowInsetsAnimationController#hasZeroInsetsIme */
private final boolean mHasZeroInsetsIme;
private final CompatibilityInfo.Translator mTranslator;
+ @Nullable
+ private final ImeTracker.Token mStatsToken;
private Insets mCurrentInsets;
private Insets mPendingInsets;
private float mPendingFraction;
@@ -114,7 +117,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
@InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs,
Interpolator interpolator, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- CompatibilityInfo.Translator translator) {
+ CompatibilityInfo.Translator translator, @Nullable ImeTracker.Token statsToken) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -152,6 +155,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
mAnimationType = animationType;
mLayoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
mTranslator = translator;
+ mStatsToken = statsToken;
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets));
}
@@ -228,6 +232,11 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
}
@Override
+ public ImeTracker.Token getStatsToken() {
+ return mStatsToken;
+ }
+
+ @Override
public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
setInsetsAndAlpha(insets, alpha, fraction, false /* allowWhenFinished */);
}
@@ -253,10 +262,10 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
}
}
- @VisibleForTesting
/**
* @return Whether the finish callback of this animation should be invoked.
*/
+ @VisibleForTesting
public boolean applyChangeInsets(@Nullable InsetsState outState) {
if (mCancelled) {
if (DEBUG) Log.d(TAG, "applyChangeInsets canceled");
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
index 291351e0b9d3..cf40e7e4d308 100644
--- a/core/java/android/view/InsetsAnimationControlRunner.java
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -16,10 +16,12 @@
package android.view;
+import android.annotation.Nullable;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
/**
* Interface representing a runner for an insets animation.
@@ -74,6 +76,12 @@ public interface InsetsAnimationControlRunner {
@AnimationType int getAnimationType();
/**
+ * @return The token tracking the current IME request or {@code null} otherwise.
+ */
+ @Nullable
+ ImeTracker.Token getStatsToken();
+
+ /**
*
* Export the state of classes that implement this interface into a protocol buffer
* output stream.
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index fc97541bd34d..f7b9aa26ad96 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -34,6 +34,7 @@ import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.animation.Interpolator;
+import android.view.inputmethod.ImeTracker;
/**
* Insets animation runner that uses {@link InsetsAnimationThread} to run the animation off from the
@@ -112,12 +113,13 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
@InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs,
Interpolator interpolator, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- CompatibilityInfo.Translator translator, Handler mainThreadHandler) {
+ CompatibilityInfo.Translator translator, Handler mainThreadHandler,
+ @Nullable ImeTracker.Token statsToken) {
mMainThreadHandler = mainThreadHandler;
mOuterCallbacks = controller;
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener, types,
mCallbacks, durationMs, interpolator, animationType, layoutInsetsDuringAnimation,
- translator);
+ translator, statsToken);
InsetsAnimationThread.getHandler().post(() -> {
if (mControl.isCancelled()) {
return;
@@ -141,6 +143,11 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
}
@Override
+ public ImeTracker.Token getStatsToken() {
+ return mControl.getStatsToken();
+ }
+
+ @Override
@UiThread
public int getTypes() {
return mControl.getTypes();
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 35838a39057e..fbd82266246b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -44,6 +44,7 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Trace;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -60,6 +61,7 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -929,10 +931,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
hideTypes[0] &= ~animatingTypes;
if (showTypes[0] != 0) {
- applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
+ applyAnimation(showTypes[0], true /* show */, false /* fromIme */,
+ null /* statsToken */);
}
if (hideTypes[0] != 0) {
- applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
+ applyAnimation(hideTypes[0], false /* show */, false /* fromIme */,
+ null /* statsToken */);
}
if (mControllableTypes != controllableTypes) {
@@ -948,11 +952,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void show(@InsetsType int types) {
- show(types, false /* fromIme */);
+ show(types, false /* fromIme */, null /* statsToken */);
}
- @VisibleForTesting
- public void show(@InsetsType int types, boolean fromIme) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void show(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
if ((types & ime()) != 0) {
Log.d(TAG, "show(ime(), fromIme=" + fromIme + ")");
}
@@ -979,7 +984,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
pendingRequest.animationType,
pendingRequest.layoutInsetsDuringAnimation,
- pendingRequest.useInsetsAnimationThread);
+ pendingRequest.useInsetsAnimationThread, statsToken);
return;
}
@@ -990,8 +995,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if ((types & type) == 0) {
continue;
}
- final @AnimationType int animationType = getAnimationType(type);
+ @AnimationType final int animationType = getAnimationType(type);
final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0;
+ final boolean isImeAnimation = type == ime();
if (requestedVisible && animationType == ANIMATION_TYPE_NONE
|| animationType == ANIMATION_TYPE_SHOW) {
// no-op: already shown or animating in (because window visibility is
@@ -999,25 +1005,36 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (DEBUG) Log.d(TAG, String.format(
"show ignored for type: %d animType: %d requestedVisible: %s",
type, animationType, requestedVisible));
+ if (isImeAnimation) {
+ ImeTracker.get().onCancelled(statsToken,
+ ImeTracker.PHASE_CLIENT_APPLY_ANIMATION);
+ }
continue;
}
if (fromIme && animationType == ANIMATION_TYPE_USER) {
// App is already controlling the IME, don't cancel it.
+ if (isImeAnimation) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION);
+ }
continue;
}
+ if (isImeAnimation) {
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION);
+ }
typesReady |= type;
}
if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady);
- applyAnimation(typesReady, true /* show */, fromIme);
+ applyAnimation(typesReady, true /* show */, fromIme, statsToken);
}
@Override
public void hide(@InsetsType int types) {
- hide(types, false /* fromIme */);
+ hide(types, false /* fromIme */, null /* statsToken */);
}
@VisibleForTesting
- public void hide(@InsetsType int types, boolean fromIme) {
+ public void hide(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("InsetsController#hide",
mHost.getInputMethodManager(), null /* icProto */);
@@ -1030,16 +1047,25 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if ((types & type) == 0) {
continue;
}
- final @AnimationType int animationType = getAnimationType(type);
+ @AnimationType final int animationType = getAnimationType(type);
final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0;
+ final boolean isImeAnimation = type == ime();
if (!requestedVisible && animationType == ANIMATION_TYPE_NONE
|| animationType == ANIMATION_TYPE_HIDE) {
- // no-op: already hidden or animating out.
+ // no-op: already hidden or animating out (because window visibility is
+ // applied before starting animation).
+ if (isImeAnimation) {
+ ImeTracker.get().onCancelled(statsToken,
+ ImeTracker.PHASE_CLIENT_APPLY_ANIMATION);
+ }
continue;
}
+ if (isImeAnimation) {
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION);
+ }
typesReady |= type;
}
- applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
+ applyAnimation(typesReady, false /* show */, fromIme, statsToken);
}
@Override
@@ -1068,7 +1094,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
interpolator, animationType, getLayoutInsetsDuringAnimationMode(types),
- false /* useInsetsAnimationThread */);
+ false /* useInsetsAnimationThread */, null /* statsToken */);
}
private void controlAnimationUnchecked(@InsetsType int types,
@@ -1077,7 +1103,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
long durationMs, Interpolator interpolator,
@AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- boolean useInsetsAnimationThread) {
+ boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION);
if ((types & mTypesBeingCancelled) != 0) {
throw new IllegalStateException("Cannot start a new insets animation of "
+ Type.toString(types)
@@ -1152,14 +1179,16 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
? new InsetsAnimationThreadControlRunner(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator,
animationType, layoutInsetsDuringAnimation, mHost.getTranslator(),
- mHost.getHandler())
+ mHost.getHandler(), statsToken)
: new InsetsAnimationControlImpl(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator,
- animationType, layoutInsetsDuringAnimation, mHost.getTranslator());
+ animationType, layoutInsetsDuringAnimation, mHost.getTranslator(),
+ statsToken);
if ((typesReady & WindowInsets.Type.ime()) != 0) {
ImeTracing.getInstance().triggerClientDump("InsetsAnimationControlImpl",
mHost.getInputMethodManager(), null /* icProto */);
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING);
mRunningAnimations.add(new RunningAnimation(runner, animationType));
if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
+ useInsetsAnimationThread);
@@ -1311,11 +1340,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// requested visibility.
return;
}
+ final ImeTracker.Token statsToken = runner.getStatsToken();
if (shown) {
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_SHOW);
showDirectly(runner.getTypes(), true /* fromIme */);
+ ImeTracker.get().onShown(statsToken);
} else {
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_HIDE);
hideDirectly(runner.getTypes(), true /* animationFinished */,
runner.getAnimationType(), true /* fromIme */);
+ ImeTracker.get().onHidden(statsToken);
}
}
@@ -1339,10 +1375,19 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
- if (DEBUG) Log.d(TAG, String.format("cancelAnimation of types: %d, animType: %d, host: %s",
- control.getTypes(), control.getAnimationType(), mHost.getRootViewTitle()));
if (invokeCallback) {
+ ImeTracker.get().onCancelled(control.getStatsToken(),
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL);
control.cancel();
+ } else {
+ // Succeeds if invokeCallback is false (i.e. when called from notifyFinished).
+ ImeTracker.get().onProgress(control.getStatsToken(),
+ ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL);
+ }
+ if (DEBUG) {
+ Log.d(TAG, TextUtils.formatSimple(
+ "cancelAnimation of types: %d, animType: %d, host: %s",
+ control.getTypes(), control.getAnimationType(), mHost.getRootViewTitle()));
}
boolean stateChanged = false;
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
@@ -1452,7 +1497,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
@VisibleForTesting
- public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
+ public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
// TODO(b/166736352): We should only skip the animation of specific types, not all types.
boolean skipAnim = false;
if ((types & ime()) != 0) {
@@ -1465,12 +1511,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
&& consumer.hasViewFocusWhenWindowFocusGain();
}
}
- applyAnimation(types, show, fromIme, skipAnim);
+ applyAnimation(types, show, fromIme, skipAnim, statsToken);
}
@VisibleForTesting
public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme,
- boolean skipAnim) {
+ boolean skipAnim, @Nullable ImeTracker.Token statsToken) {
if (types == 0) {
// nothing to animate.
if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate");
@@ -1490,12 +1536,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
listener.getDurationMs(), listener.getInsetsInterpolator(),
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
- !hasAnimationCallbacks /* useInsetsAnimationThread */);
+ !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken);
}
- private void hideDirectly(
- @InsetsType int types, boolean animationFinished, @AnimationType int animationType,
- boolean fromIme) {
+ private void hideDirectly(@InsetsType int types, boolean animationFinished,
+ @AnimationType int animationType, boolean fromIme) {
if ((types & ime()) != 0) {
ImeTracing.getInstance().triggerClientDump("InsetsController#hideDirectly",
mHost.getInputMethodManager(), null /* icProto */);
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index edcfc95fe4e4..778c677c58bd 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -37,6 +37,7 @@ import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.animation.Interpolator;
+import android.view.inputmethod.ImeTracker;
/**
* Runs a fake animation of resizing insets to produce insets animation callbacks.
@@ -92,6 +93,12 @@ public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner
}
@Override
+ public ImeTracker.Token getStatsToken() {
+ // Return null as resizing the IME view is not explicitly tracked.
+ return null;
+ }
+
+ @Override
public void cancel() {
if (mCancelled || mFinished) {
return;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 43f09473b2d8..636023938b9d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -192,6 +192,7 @@ import android.view.autofill.AutofillManager;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.MainContentCaptureSession;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.window.ClientWindowFrames;
@@ -5649,17 +5650,23 @@ public final class ViewRootImpl implements ViewParent,
break;
}
case MSG_SHOW_INSETS: {
+ final ImeTracker.Token statsToken = (ImeTracker.Token) msg.obj;
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_CLIENT_HANDLE_SHOW_INSETS);
if (mView == null) {
Log.e(TAG,
String.format("Calling showInsets(%d,%b) on window that no longer"
+ " has views.", msg.arg1, msg.arg2 == 1));
}
clearLowProfileModeIfNeeded(msg.arg1, msg.arg2 == 1);
- mInsetsController.show(msg.arg1, msg.arg2 == 1);
+ mInsetsController.show(msg.arg1, msg.arg2 == 1, statsToken);
break;
}
case MSG_HIDE_INSETS: {
- mInsetsController.hide(msg.arg1, msg.arg2 == 1);
+ final ImeTracker.Token statsToken = (ImeTracker.Token) msg.obj;
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_CLIENT_HANDLE_HIDE_INSETS);
+ mInsetsController.hide(msg.arg1, msg.arg2 == 1, statsToken);
break;
}
case MSG_WINDOW_MOVED:
@@ -8811,12 +8818,14 @@ public final class ViewRootImpl implements ViewParent,
mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget();
}
- private void showInsets(@InsetsType int types, boolean fromIme) {
- mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget();
+ private void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
+ mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0, statsToken).sendToTarget();
}
- private void hideInsets(@InsetsType int types, boolean fromIme) {
- mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget();
+ private void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
+ mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0, statsToken).sendToTarget();
}
public void dispatchMoved(int newX, int newY) {
@@ -10180,7 +10189,8 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
- public void showInsets(@InsetsType int types, boolean fromIme) {
+ public void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#showInsets",
@@ -10188,13 +10198,16 @@ public final class ViewRootImpl implements ViewParent,
null /* icProto */);
}
if (viewAncestor != null) {
- viewAncestor.showInsets(types, fromIme);
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_SHOW_INSETS);
+ viewAncestor.showInsets(types, fromIme, statsToken);
+ } else {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_SHOW_INSETS);
}
}
@Override
- public void hideInsets(@InsetsType int types, boolean fromIme) {
-
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#hideInsets",
@@ -10202,7 +10215,10 @@ public final class ViewRootImpl implements ViewParent,
null /* icProto */);
}
if (viewAncestor != null) {
- viewAncestor.hideInsets(types, fromIme);
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_HIDE_INSETS);
+ viewAncestor.hideInsets(types, fromIme, statsToken);
+ } else {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_HIDE_INSETS);
}
}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index a66c67b2e255..6eae63afcba7 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -275,15 +275,16 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver,
+ @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
+ @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
- return service.showSoftInput(
- client, windowToken, flags, lastClickToolType, resultReceiver, reason);
+ return service.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
+ resultReceiver, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -291,14 +292,15 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- int flags, @Nullable ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @Nullable ImeTracker.Token statsToken, int flags,
+ @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
- return service.hideSoftInput(client, windowToken, flags, resultReceiver, reason);
+ return service.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
+ reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/ImeTracker.aidl b/core/java/android/view/inputmethod/ImeTracker.aidl
new file mode 100644
index 000000000000..1988f482497d
--- /dev/null
+++ b/core/java/android/view/inputmethod/ImeTracker.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable ImeTracker.Token;
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
new file mode 100644
index 000000000000..f4ecdff12c7a
--- /dev/null
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import static android.view.inputmethod.ImeTracker.Debug.originToString;
+import static android.view.inputmethod.ImeTracker.Debug.phaseToString;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+/** @hide */
+public interface ImeTracker {
+
+ String TAG = "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).
+ */
+ @IntDef(prefix = { "ORIGIN_" }, value = {
+ ORIGIN_CLIENT_SHOW_SOFT_INPUT,
+ ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ ORIGIN_SERVER_START_INPUT,
+ ORIGIN_SERVER_HIDE_INPUT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Origin {}
+
+ /**
+ * The IME show request originated in the client.
+ */
+ int ORIGIN_CLIENT_SHOW_SOFT_INPUT = 0;
+
+ /**
+ * The IME hide request originated in the client.
+ */
+ int ORIGIN_CLIENT_HIDE_SOFT_INPUT = 1;
+
+ /**
+ * The IME show request originated in the server.
+ */
+ int ORIGIN_SERVER_START_INPUT = 2;
+
+ /**
+ * The IME hide request originated in the server.
+ */
+ int ORIGIN_SERVER_HIDE_INPUT = 3;
+
+ /**
+ * The current phase of the IME request.
+ *
+ * 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 = {
+ PHASE_CLIENT_VIEW_SERVED,
+ PHASE_SERVER_CLIENT_KNOWN,
+ PHASE_SERVER_CLIENT_FOCUSED,
+ PHASE_SERVER_ACCESSIBILITY,
+ PHASE_SERVER_SYSTEM_READY,
+ PHASE_SERVER_HIDE_IMPLICIT,
+ PHASE_SERVER_HIDE_NOT_ALWAYS,
+ PHASE_SERVER_WAIT_IME,
+ PHASE_SERVER_HAS_IME,
+ PHASE_SERVER_SHOULD_HIDE,
+ PHASE_IME_WRAPPER,
+ PHASE_IME_WRAPPER_DISPATCH,
+ 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,
+ PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET,
+ PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS,
+ PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS,
+ PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS,
+ PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS,
+ PHASE_WM_REMOTE_INSETS_CONTROLLER,
+ PHASE_WM_ANIMATION_CREATE,
+ PHASE_WM_ANIMATION_RUNNING,
+ PHASE_CLIENT_SHOW_INSETS,
+ PHASE_CLIENT_HIDE_INSETS,
+ PHASE_CLIENT_HANDLE_SHOW_INSETS,
+ PHASE_CLIENT_HANDLE_HIDE_INSETS,
+ PHASE_CLIENT_APPLY_ANIMATION,
+ PHASE_CLIENT_CONTROL_ANIMATION,
+ PHASE_CLIENT_ANIMATION_RUNNING,
+ PHASE_CLIENT_ANIMATION_CANCEL,
+ PHASE_CLIENT_ANIMATION_FINISHED_SHOW,
+ PHASE_CLIENT_ANIMATION_FINISHED_HIDE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Phase {}
+
+ /** The view that requested the IME has been served by the IMM. */
+ int PHASE_CLIENT_VIEW_SERVED = 0;
+
+ /** The IME client that requested the IME has window manager focus. */
+ int PHASE_SERVER_CLIENT_KNOWN = 1;
+
+ /** The IME client that requested the IME has IME focus. */
+ int PHASE_SERVER_CLIENT_FOCUSED = 2;
+
+ /** The IME request complies with the current accessibility settings. */
+ int PHASE_SERVER_ACCESSIBILITY = 3;
+
+ /** The server is ready to run third party code. */
+ int PHASE_SERVER_SYSTEM_READY = 4;
+
+ /** Checked the implicit hide request against any explicit show requests. */
+ int PHASE_SERVER_HIDE_IMPLICIT = 5;
+
+ /** Checked the not-always hide request against any forced show requests. */
+ int PHASE_SERVER_HIDE_NOT_ALWAYS = 6;
+
+ /** The server is waiting for a connection to the IME. */
+ int PHASE_SERVER_WAIT_IME = 7;
+
+ /** The server has a connection to the IME. */
+ int PHASE_SERVER_HAS_IME = 8;
+
+ /** The server decided the IME should be hidden. */
+ int PHASE_SERVER_SHOULD_HIDE = 9;
+
+ /** Reached the IME wrapper. */
+ int PHASE_IME_WRAPPER = 10;
+
+ /** Dispatched from the IME wrapper to the IME. */
+ int PHASE_IME_WRAPPER_DISPATCH = 11;
+
+ /** Reached the IME' showSoftInput method. */
+ int PHASE_IME_SHOW_SOFT_INPUT = 12;
+
+ /** Reached the IME' hideSoftInput method. */
+ int PHASE_IME_HIDE_SOFT_INPUT = 13;
+
+ /** The server decided the IME should be shown. */
+ int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = 14;
+
+ /** Requested applying the IME visibility in the insets source consumer. */
+ int PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER = 15;
+
+ /** Applied the IME visibility. */
+ int PHASE_SERVER_APPLY_IME_VISIBILITY = 16;
+
+ /** Created the show IME runner. */
+ int PHASE_WM_SHOW_IME_RUNNER = 17;
+
+ /** Ready to show IME. */
+ int PHASE_WM_SHOW_IME_READY = 18;
+
+ /** The Window Manager has a connection to the IME insets control target. */
+ int PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET = 19;
+
+ /** Reached the window insets control target's show insets method. */
+ int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS = 20;
+
+ /** Reached the window insets control target's hide insets method. */
+ int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS = 21;
+
+ /** Reached the remote insets control target's show insets method. */
+ int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS = 22;
+
+ /** Reached the remote insets control target's hide insets method. */
+ int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS = 23;
+
+ /** Reached the remote insets controller. */
+ int PHASE_WM_REMOTE_INSETS_CONTROLLER = 24;
+
+ /** Created the IME window insets show animation. */
+ int PHASE_WM_ANIMATION_CREATE = 25;
+
+ /** Started the IME window insets show animation. */
+ int PHASE_WM_ANIMATION_RUNNING = 26;
+
+ /** Reached the client's show insets method. */
+ int PHASE_CLIENT_SHOW_INSETS = 27;
+
+ /** Reached the client's hide insets method. */
+ int PHASE_CLIENT_HIDE_INSETS = 28;
+
+ /** Handling the IME window insets show request. */
+ int PHASE_CLIENT_HANDLE_SHOW_INSETS = 29;
+
+ /** Handling the IME window insets hide request. */
+ int PHASE_CLIENT_HANDLE_HIDE_INSETS = 30;
+
+ /** Applied the IME window insets show animation. */
+ int PHASE_CLIENT_APPLY_ANIMATION = 31;
+
+ /** Started the IME window insets show animation. */
+ int PHASE_CLIENT_CONTROL_ANIMATION = 32;
+
+ /** Queued the IME window insets show animation. */
+ int PHASE_CLIENT_ANIMATION_RUNNING = 33;
+
+ /** Cancelled the IME window insets show animation. */
+ int PHASE_CLIENT_ANIMATION_CANCEL = 34;
+
+ /** Finished the IME window insets show animation. */
+ int PHASE_CLIENT_ANIMATION_FINISHED_SHOW = 35;
+
+ /** Finished the IME window insets hide animation. */
+ int PHASE_CLIENT_ANIMATION_FINISHED_HIDE = 36;
+
+ /**
+ * Called when an IME show request is created.
+ *
+ * @param token the token tracking the current IME show request or {@code null} otherwise.
+ * @param origin the origin of the IME show request.
+ * @param reason the reason why the IME show request was created.
+ */
+ void onRequestShow(@Nullable Token token, @Origin int origin,
+ @SoftInputShowHideReason int reason);
+
+ /**
+ * Called when an IME hide request is created.
+ *
+ * @param token the token tracking the current IME hide request or {@code null} otherwise.
+ * @param origin the origin of the IME hide request.
+ * @param reason the reason why the IME hide request was created.
+ */
+ void onRequestHide(@Nullable Token token, @Origin int origin,
+ @SoftInputShowHideReason int reason);
+
+ /**
+ * Called when an IME request progresses to a further phase.
+ *
+ * @param token the token tracking the current IME request or {@code null} otherwise.
+ * @param phase the new phase the IME request reached.
+ */
+ void onProgress(@Nullable Token token, @Phase int phase);
+
+ /**
+ * Called when an IME request fails.
+ *
+ * @param token the token tracking the current IME request or {@code null} otherwise.
+ * @param phase the phase the IME request failed at.
+ */
+ void onFailed(@Nullable Token token, @Phase int phase);
+
+ /**
+ * Called when an IME request reached a flow that is not yet implemented.
+ *
+ * @param token the token tracking the current IME request or {@code null} otherwise.
+ * @param phase the phase the IME request was currently at.
+ */
+ void onTodo(@Nullable Token token, @Phase int phase);
+
+ /**
+ * Called when an IME request is cancelled.
+ *
+ * @param token the token tracking the current IME request or {@code null} otherwise.
+ * @param phase the phase the IME request was cancelled at.
+ */
+ void onCancelled(@Nullable Token token, @Phase int phase);
+
+ /**
+ * Called when the IME show request is successful.
+ *
+ * @param token the token tracking the current IME show 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.
+ */
+ void onHidden(@Nullable Token token);
+
+ /**
+ * Get the singleton instance of this class.
+ *
+ * @return the singleton instance of this class
+ */
+ @NonNull
+ static ImeTracker get() {
+ return SystemProperties.getBoolean("persist.debug.imetracker", false)
+ ? LOGGER
+ : NOOP_LOGGER;
+ }
+
+ /** The singleton IME tracker instance. */
+ ImeTracker LOGGER = new ImeTracker() {
+
+ @Override
+ public void onRequestShow(@Nullable Token token, int origin,
+ @SoftInputShowHideReason int reason) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onRequestShow at " + originToString(origin)
+ + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
+ }
+
+ @Override
+ public void onRequestHide(@Nullable Token token, int origin,
+ @SoftInputShowHideReason int reason) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onRequestHide at " + originToString(origin)
+ + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
+ }
+
+ @Override
+ public void onProgress(@Nullable Token token, int phase) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onProgress at " + phaseToString(phase));
+ }
+
+ @Override
+ public void onFailed(@Nullable Token token, int phase) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onFailed at " + phaseToString(phase));
+ }
+
+ @Override
+ public void onTodo(@Nullable Token token, int phase) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onTodo at " + phaseToString(phase));
+ }
+
+ @Override
+ public void onCancelled(@Nullable Token token, int phase) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onCancelled at " + phaseToString(phase));
+ }
+
+ @Override
+ public void onShown(@Nullable Token token) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onShown");
+ }
+
+ @Override
+ public void onHidden(@Nullable Token token) {
+ if (token == null) return;
+ Log.i(TAG, token.mTag + ": onHidden");
+ }
+ };
+
+ /** The singleton no-op IME tracker instance. */
+ ImeTracker NOOP_LOGGER = new ImeTracker() {
+
+ @Override
+ public void onRequestShow(@Nullable Token token, int origin,
+ @SoftInputShowHideReason int reason) {}
+
+ @Override
+ public void onRequestHide(@Nullable Token token, int origin,
+ @SoftInputShowHideReason int reason) {}
+
+ @Override
+ public void onProgress(@Nullable Token token, int phase) {}
+
+ @Override
+ public void onFailed(@Nullable Token token, int phase) {}
+
+ @Override
+ public void onTodo(@Nullable Token token, int phase) {}
+
+ @Override
+ public void onCancelled(@Nullable Token token, int phase) {}
+
+ @Override
+ public void onShown(@Nullable Token token) {}
+
+ @Override
+ public void onHidden(@Nullable Token token) {}
+ };
+
+ /** A token that tracks the progress of an IME request. */
+ class Token implements Parcelable {
+
+ private final IBinder mBinder;
+ private final String mTag;
+
+ public Token() {
+ this(ActivityThread.currentProcessName());
+ }
+
+ public Token(String component) {
+ this(new Binder(), component + ":" + Integer.toHexString((new Random().nextInt())));
+ }
+
+ private Token(IBinder binder, String tag) {
+ mBinder = binder;
+ mTag = tag;
+ }
+
+ /** For Parcelable, no special marshalled objects. */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mBinder);
+ dest.writeString8(mTag);
+ }
+
+ @NonNull
+ public static final Creator<Token> CREATOR = new Creator<>() {
+ @Override
+ public Token createFromParcel(Parcel source) {
+ IBinder binder = source.readStrongBinder();
+ String tag = source.readString8();
+ return new Token(binder, tag);
+ }
+
+ @Override
+ public Token[] newArray(int size) {
+ return new Token[size];
+ }
+ };
+ }
+
+ /**
+ * Utilities for mapping phases and origins IntDef values to their names.
+ *
+ * Note: This is held in a separate class so that it only gets initialized when actually needed.
+ */
+ class Debug {
+
+ private static final Map<Integer, String> sOrigins =
+ getFieldMapping(ImeTracker.class, "ORIGIN_");
+ private static final Map<Integer, String> sPhases =
+ getFieldMapping(ImeTracker.class, "PHASE_");
+
+ public static String originToString(int origin) {
+ return sOrigins.getOrDefault(origin, "ORIGIN_" + origin);
+ }
+
+ public static String phaseToString(int phase) {
+ return sPhases.getOrDefault(phase, "PHASE_" + phase);
+ }
+
+ private static Map<Integer, String> getFieldMapping(Class<?> cls, String fieldPrefix) {
+ return Arrays.stream(cls.getDeclaredFields())
+ .filter(field -> field.getName().startsWith(fieldPrefix))
+ .collect(Collectors.toMap(Debug::getFieldValue, Field::getName));
+ }
+
+ private static int getFieldValue(Field field) {
+ try {
+ return field.getInt(null);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 4d5a17d92f11..92380ed7a7bc 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -300,11 +300,12 @@ 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.
* @hide
*/
@MainThread
- default public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder showInputToken) {
+ public default void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+ IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
showSoftInput(flags, resultReceiver);
}
@@ -338,11 +339,14 @@ 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.
* @hide
*/
@MainThread
- public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder hideInputToken);
+ public default void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+ IBinder hideInputToken, @Nullable ImeTracker.Token statsToken) {
+ hideSoftInput(flags, resultReceiver);
+ }
/**
* Request that any soft input part of the input method be hidden from the user.
@@ -369,7 +373,7 @@ public interface InputMethod {
/**
* Checks if IME is ready to start stylus handwriting session.
- * If yes, {@link #startStylusHandwriting(InputChannel, List)} is called.
+ * If yes, {@link #startStylusHandwriting(int, InputChannel, List)} is called.
* @param requestId
* @hide
*/
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7e720ffbcbce..ee31fd5763b7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2007,6 +2007,10 @@ public final class InputMethodManager {
private boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
+ final ImeTracker.Token statsToken = new ImeTracker.Token();
+ ImeTracker.get().onRequestShow(statsToken, ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
+ reason);
+
ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this,
null /* icProto */);
// Re-dispatch if there is a context mismatch.
@@ -2018,10 +2022,13 @@ public final class InputMethodManager {
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view)) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
Log.w(TAG, "Ignoring showSoftInput() as view=" + view + " is not served.");
return false;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
+
// Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread.
// TODO(b/229426865): call WindowInsetsController#show instead.
mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED));
@@ -2030,6 +2037,7 @@ public final class InputMethodManager {
return IInputMethodManagerGlobalInvoker.showSoftInput(
mClient,
view.getWindowToken(),
+ statsToken,
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
@@ -2049,19 +2057,28 @@ public final class InputMethodManager {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499)
public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
synchronized (mH) {
+ final ImeTracker.Token statsToken = new ImeTracker.Token();
+ ImeTracker.get().onRequestShow(statsToken, ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT);
+
Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be"
+ " removed soon. If you are using androidx.appcompat.widget.SearchView,"
+ " please update to version 26.0 or newer version.");
if (mCurRootView == null || mCurRootView.getView() == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
Log.w(TAG, "No current root view, ignoring showSoftInputUnchecked()");
return;
}
+
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
+
// Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread.
// TODO(b/229426865): call WindowInsetsController#show instead.
mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED));
IInputMethodManagerGlobalInvoker.showSoftInput(
mClient,
mCurRootView.getView().getWindowToken(),
+ statsToken,
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
@@ -2131,17 +2148,24 @@ public final class InputMethodManager {
private boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ final ImeTracker.Token statsToken = new ImeTracker.Token();
+ ImeTracker.get().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ reason);
+
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow",
this, null /* icProto */);
checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
if (servedView == null || servedView.getWindowToken() != windowToken) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
return false;
}
- return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, flags,
- resultReceiver, reason);
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
+
+ return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken,
+ flags, resultReceiver, reason);
}
}
@@ -2769,14 +2793,23 @@ public final class InputMethodManager {
@UnsupportedAppUsage
void closeCurrentInput() {
+ final ImeTracker.Token statsToken = new ImeTracker.Token();
+ ImeTracker.get().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT);
+
synchronized (mH) {
if (mCurRootView == null || mCurRootView.getView() == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
Log.w(TAG, "No current root view, ignoring closeCurrentInput()");
return;
}
+
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
+
IInputMethodManagerGlobalInvoker.hideSoftInput(
mClient,
mCurRootView.getView().getWindowToken(),
+ statsToken,
HIDE_NOT_ALWAYS,
null,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
@@ -2845,15 +2878,24 @@ public final class InputMethodManager {
* @hide
*/
public void notifyImeHidden(IBinder windowToken) {
+ final ImeTracker.Token statsToken = new ImeTracker.Token();
+ ImeTracker.get().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+
ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this,
null /* icProto */);
synchronized (mH) {
- if (isImeSessionAvailableLocked() && mCurRootView != null
- && mCurRootView.getWindowToken() == windowToken) {
- IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, 0 /* flags */,
- null /* resultReceiver */,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+ if (!isImeSessionAvailableLocked() || mCurRootView == null
+ || mCurRootView.getWindowToken() != windowToken) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
+ return;
}
+
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
+
+ IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken,
+ 0 /* flags */, null /* resultReceiver */,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
}
}
@@ -3977,7 +4019,7 @@ public final class InputMethodManager {
/**
* As reported by {@link InputBindResult}. This value is determined by
- * {@link com.android.internal.R.styleable#InputMethod_suppressesSpellChecking}.
+ * {@link com.android.internal.R.styleable#InputMethod_suppressesSpellChecker}.
*/
final boolean mIsInputMethodSuppressingSpellChecker;
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index c62fba9fb9b8..1e3714eb342d 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -21,6 +21,7 @@ import android.os.ResultReceiver;
import android.view.InputChannel;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
@@ -69,9 +70,11 @@ oneway interface IInputMethod {
void setSessionEnabled(IInputMethodSession session, boolean enabled);
- void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver);
+ void showSoftInput(in IBinder showInputToken, in @nullable ImeTracker.Token statsToken,
+ int flags, in ResultReceiver resultReceiver);
- void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
+ void hideSoftInput(in IBinder hideInputToken, in @nullable 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 4babb7080176..f77e96286bea 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -17,6 +17,7 @@
package com.android.internal.inputmethod;
import android.net.Uri;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.infra.AndroidFuture;
@@ -41,7 +42,8 @@ oneway interface IInputMethodPrivilegedOperations {
void switchToNextInputMethod(boolean onlyCurrentIme, in AndroidFuture future /* T=Boolean */);
void shouldOfferSwitchingToNextInputMethod(in AndroidFuture future /* T=Boolean */);
void notifyUserActionAsync();
- void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
+ void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible,
+ in @nullable ImeTracker.Token statsToken);
void onStylusHandwritingReady(int requestId, int pid);
void resetStylusHandwriting(int requestId);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 67c2103450bb..66e3333acf7c 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -25,6 +25,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.GuardedBy;
@@ -100,7 +101,7 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatusAsync(int, int}.
+ * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatusAsync(int, int)}.
*
* @param vis visibility flags
* @param backDisposition disposition flags
@@ -250,7 +251,7 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, IVoidResultCallback)}
+ * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)}
*
* @param flags additional operating flags
* @param reason the reason to hide soft input
@@ -316,7 +317,7 @@ public final class InputMethodPrivilegedOperations {
/**
* Calls {@link IInputMethodPrivilegedOperations#switchToNextInputMethod(boolean,
- * IBooleanResultCallback)}
+ * AndroidFuture)}
*
* @param onlyCurrentIme {@code true} to switch to a {@link InputMethodSubtype} within the same
* IME
@@ -375,22 +376,25 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibilityAsync(IBinder, boolean)}.
+ * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibilityAsync(IBinder, boolean,
+ * ImeTracker.Token)}.
*
* @param showOrHideInputToken placeholder token that maps to window requesting
* {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
- * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow
- * (IBinder, int)}
+ * {@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.
*/
@AnyThread
- public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible) {
+ public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible,
+ @Nullable ImeTracker.Token statsToken) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible);
+ ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible, statsToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index fe7723658761..2ac43099c741 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,6 +16,7 @@
package com.android.internal.view;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputManager;
import android.os.Bundle;
@@ -31,6 +32,7 @@ import android.view.InsetsState;
import android.view.PointerIcon;
import android.view.ScrollCaptureResponse;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
@@ -66,11 +68,13 @@ public class BaseIWindow extends IWindow.Stub {
}
@Override
- public void showInsets(@InsetsType int types, boolean fromIme) {
+ public void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
}
@Override
- public void hideInsets(@InsetsType int types, boolean fromIme) {
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
}
@Override
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 40c6a05650cf..00bc3f2ae530 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -17,6 +17,7 @@
package com.android.internal.view;
import android.os.ResultReceiver;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
@@ -54,11 +55,12 @@ interface IInputMethodManager {
+ "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
InputMethodSubtype getLastInputMethodSubtype(int userId);
- boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
- int lastClickToolType, in @nullable ResultReceiver resultReceiver, int reason);
- boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
+ boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
+ in @nullable 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 @nullable ResultReceiver resultReceiver, int reason);
-
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'editorInfo' is non-null then also does startInput.
// @NonNull
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index ddcb17544ec7..0bf133fe01b6 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -24,6 +24,7 @@ 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.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -98,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 */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
mController.cancelExistingAnimations();
assertTrue((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
// test if setVisibility can hide IME
- mController.hide(WindowInsets.Type.ime(), true /* fromIme */);
+ mController.hide(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
mController.cancelExistingAnimations();
assertFalse((mController.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
});
@@ -117,7 +118,7 @@ public class ImeInsetsSourceConsumerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
mImeConsumer.onWindowFocusGained(true);
- mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
// set control and verify visibility is applied.
InsetsSourceControl control =
@@ -125,9 +126,11 @@ public class ImeInsetsSourceConsumerTest {
mController.onControlsChanged(new InsetsSourceControl[] { control });
// IME show animation should be triggered when control becomes available.
verify(mController).applyAnimation(
- eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */);
+ eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
+ any() /* statsToken */);
verify(mController, never()).applyAnimation(
- eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */);
+ eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */,
+ any() /* statsToken */);
});
}
@@ -153,7 +156,8 @@ public class ImeInsetsSourceConsumerTest {
mImeConsumer.onWindowFocusGained(hasWindowFocus);
final boolean imeVisible = hasWindowFocus && hasViewFocus;
if (imeVisible) {
- mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */,
+ null /* statsToken */);
}
// set control and verify visibility is applied.
@@ -169,20 +173,21 @@ public class ImeInsetsSourceConsumerTest {
verify(control).getAndClearSkipAnimationOnce();
verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
eq(true) /* show */, eq(false) /* fromIme */,
- eq(expectSkipAnim) /* skipAnim */);
+ eq(expectSkipAnim) /* skipAnim */, null /* statsToken */);
}
// 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);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */,
+ null /* statsToken */);
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(false) /* skipAnim */, null /* statsToken */);
}
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index e9cd8ad7d5c2..c88255ef0e00 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -109,7 +109,8 @@ public class InsetsAnimationControlImplTest {
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */, new LinearInterpolator(),
- 0 /* animationType */, 0 /* layoutInsetsDuringAnimation */, null /* translator */);
+ 0 /* animationType */, 0 /* layoutInsetsDuringAnimation */, null /* translator */,
+ null /* statsToken */);
mController.setReadyDispatched(true);
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 409bae8addc2..c6fa778763a8 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -248,7 +248,7 @@ public class InsetsControllerTest {
mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
- mController.show(ime(), true /* fromIme */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
verify(loggingListener).onReady(notNull(), anyInt());
});
}
@@ -260,14 +260,14 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
- mController.show(ime(), true /* fromIme */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
mController.show(all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimations();
final @InsetsType int types = navigationBars() | statusBars() | ime();
assertEquals(types, mController.getRequestedVisibleTypes() & types);
- mController.hide(ime(), true /* fromIme */);
+ mController.hide(ime(), true /* fromIme */, null /* statsToken */);
mController.hide(all());
mController.cancelExistingAnimations();
assertEquals(0, mController.getRequestedVisibleTypes() & types);
@@ -282,10 +282,10 @@ public class InsetsControllerTest {
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
- mController.show(ime(), true /* fromIme */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
mController.cancelExistingAnimations();
assertTrue(isRequestedVisible(mController, ime()));
- mController.hide(ime(), true /* fromIme */);
+ mController.hide(ime(), true /* fromIme */, null /* statsToken */);
mController.cancelExistingAnimations();
assertFalse(isRequestedVisible(mController, ime()));
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
@@ -452,7 +452,7 @@ public class InsetsControllerTest {
assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */);
+ mController.show(ime(), true /* fromIme */, null /* statsToken */);
// Gaining control shortly after
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
@@ -476,7 +476,7 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */);
+ mController.show(ime(), true /* fromIme */, null /* statsToken */);
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
mController.cancelExistingAnimations();
@@ -554,7 +554,7 @@ public class InsetsControllerTest {
verify(listener, never()).onReady(any(), anyInt());
// Pretend that IME is calling.
- mController.show(ime(), true);
+ mController.show(ime(), true /* fromIme */, null /* statsToken */);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -638,7 +638,7 @@ public class InsetsControllerTest {
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
// Pretend IME is calling
- mController.show(ime(), true /* fromIme */);
+ mController.show(ime(), true /* fromIme */, null /* statsToken */);
InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3);
@@ -845,7 +845,7 @@ public class InsetsControllerTest {
// Showing invisible ime should only causes insets change once.
clearInvocations(mTestHost);
- mController.show(ime(), true /* fromIme */);
+ mController.show(ime(), true /* fromIme */, null /* statsToken */);
verify(mTestHost, times(1)).notifyInsetsChanged();
// Sending the same insets state should not cause insets change.
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 bb7c4134aaaf..d9b4f475a50c 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,6 +20,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -38,6 +39,7 @@ import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManagerGlobal;
import androidx.annotation.VisibleForTesting;
@@ -112,7 +114,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
- pd.startAnimation(true, false /* forceRestart */);
+ pd.startAnimation(true, false /* forceRestart */, null /* statsToken */);
}
}
@@ -244,7 +246,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mInsetsState.set(insetsState, true /* copySources */);
if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) {
if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
- startAnimation(mImeShowing, true /* forceRestart */);
+ startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
}
}
@@ -280,7 +282,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
!haveSameLeash(mImeSourceControl, imeSourceControl);
if (mAnimation != null) {
if (positionChanged) {
- startAnimation(mImeShowing, true /* forceRestart */);
+ startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
}
} else {
if (leashChanged) {
@@ -312,21 +314,23 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
@Override
- public void showInsets(int types, boolean fromIme) {
+ public void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
- startAnimation(true /* show */, false /* forceRestart */);
+ startAnimation(true /* show */, false /* forceRestart */, statsToken);
}
@Override
- public void hideInsets(int types, boolean fromIme) {
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
- startAnimation(false /* show */, false /* forceRestart */);
+ startAnimation(false /* show */, false /* forceRestart */, statsToken);
}
@Override
@@ -367,9 +371,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
.navBarFrameHeight();
}
- private void startAnimation(final boolean show, final boolean forceRestart) {
+ private void startAnimation(final boolean show, final boolean forceRestart,
+ @Nullable ImeTracker.Token statsToken) {
final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
if (imeSource == null || mImeSourceControl == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
return;
}
final Rect newFrame = imeSource.getFrame();
@@ -390,8 +396,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
+ (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
: (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
}
- if (!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show)
+ if ((!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show))
|| (mAnimationDirection == DIRECTION_HIDE && !show)) {
+ ImeTracker.get().onCancelled(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
return;
}
boolean seek = false;
@@ -435,8 +442,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mTransactionPool.release(t);
});
mAnimation.setInterpolator(INTERPOLATOR);
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
+ @Nullable
+ private final ImeTracker.Token mStatsToken = statsToken;
@Override
public void onAnimationStart(Animator animation) {
@@ -455,6 +465,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
: 1.f;
t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (mAnimationDirection == DIRECTION_SHOW) {
+ ImeTracker.get().onProgress(mStatsToken,
+ ImeTracker.PHASE_WM_ANIMATION_RUNNING);
t.show(mImeSourceControl.getLeash());
}
t.apply();
@@ -476,8 +488,16 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
+ ImeTracker.get().onProgress(mStatsToken,
+ ImeTracker.PHASE_WM_ANIMATION_RUNNING);
t.hide(mImeSourceControl.getLeash());
removeImeSurface();
+ ImeTracker.get().onHidden(mStatsToken);
+ } else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
+ ImeTracker.get().onShown(mStatsToken);
+ } else if (mCancelled) {
+ ImeTracker.get().onCancelled(mStatsToken,
+ ImeTracker.PHASE_WM_ANIMATION_RUNNING);
}
t.apply();
mTransactionPool.release(t);
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 8d4a09d8f371..8759301f695b 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.common;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.RemoteException;
import android.util.Slog;
@@ -25,6 +26,7 @@ import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
import androidx.annotation.BinderThread;
@@ -156,23 +158,29 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
}
}
- private void showInsets(int types, boolean fromIme) {
+ private void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
return;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
for (OnInsetsChangedListener listener : listeners) {
- listener.showInsets(types, fromIme);
+ listener.showInsets(types, fromIme, statsToken);
}
}
- private void hideInsets(int types, boolean fromIme) {
+ private void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
return;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
for (OnInsetsChangedListener listener : listeners) {
- listener.hideInsets(types, fromIme);
+ listener.hideInsets(types, fromIme, statsToken);
}
}
@@ -214,16 +222,18 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
}
@Override
- public void showInsets(int types, boolean fromIme) throws RemoteException {
+ public void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) throws RemoteException {
mMainExecutor.execute(() -> {
- PerDisplay.this.showInsets(types, fromIme);
+ PerDisplay.this.showInsets(types, fromIme, statsToken);
});
}
@Override
- public void hideInsets(int types, boolean fromIme) throws RemoteException {
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) throws RemoteException {
mMainExecutor.execute(() -> {
- PerDisplay.this.hideInsets(types, fromIme);
+ PerDisplay.this.hideInsets(types, fromIme, statsToken);
});
}
}
@@ -263,15 +273,21 @@ 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.
*/
- default void showInsets(@InsetsType int types, boolean fromIme) {}
+ default void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {}
/**
* Called when a set of insets source window should be hidden by policy.
*
* @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.
*/
- default void hideInsets(@InsetsType int types, boolean fromIme) {}
+ default void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index e270edb800bd..af13bf54f691 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.common;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Region;
@@ -46,6 +47,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
@@ -351,10 +353,10 @@ public class SystemWindows {
InsetsSourceControl[] activeControls) {}
@Override
- public void showInsets(int types, boolean fromIme) {}
+ public void showInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {}
@Override
- public void hideInsets(int types, boolean fromIme) {}
+ public void hideInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {}
@Override
public void moved(int newX, int newY) {}
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 a6f19e7d11d3..40f2e88f34fd 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
@@ -97,13 +97,13 @@ public class DisplayImeControllerTest extends ShellTestCase {
@Test
public void showInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.showInsets(ime(), true);
+ mPerDisplay.showInsets(ime(), true /* fromIme */, null /* statsToken */);
verifyZeroInteractions(mExecutor);
}
@Test
public void hideInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.hideInsets(ime(), true);
+ mPerDisplay.hideInsets(ime(), true /* fromIme */, null /* statsToken */);
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 39db328ef0e0..956f1cd419c2 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
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.RemoteException;
import android.util.SparseArray;
@@ -33,6 +34,7 @@ import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.WindowInsets;
+import android.view.inputmethod.ImeTracker;
import androidx.test.filters.SmallTest;
@@ -111,8 +113,10 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
WindowInsets.Type.defaultVisible());
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null);
- mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false);
- mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false,
+ null /* statsToken */);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false,
+ null /* statsToken */);
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
@@ -131,8 +135,10 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
WindowInsets.Type.defaultVisible());
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null);
- mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false);
- mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false,
+ null /* statsToken */);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false,
+ null /* statsToken */);
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
@@ -191,12 +197,12 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
}
@Override
- public void showInsets(int types, boolean fromIme) {
+ public void showInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {
showInsetsCount++;
}
@Override
- public void hideInsets(int types, boolean fromIme) {
+ public void hideInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {
hideInsetsCount++;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 1a0f6f7fff4a..015e5768d505 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -28,6 +28,7 @@ import android.util.Slog;
import android.view.InputChannel;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
@@ -198,9 +199,10 @@ final class IInputMethodInvoker {
// TODO(b/192412909): Convert this back to void method
@AnyThread
- boolean showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) {
+ boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, int flags,
+ ResultReceiver resultReceiver) {
try {
- mTarget.showSoftInput(showInputToken, flags, resultReceiver);
+ mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver);
} catch (RemoteException e) {
logRemoteException(e);
return false;
@@ -210,9 +212,10 @@ final class IInputMethodInvoker {
// TODO(b/192412909): Convert this back to void method
@AnyThread
- boolean hideSoftInput(IBinder hideInputToken, int flags, ResultReceiver resultReceiver) {
+ boolean hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, int flags,
+ ResultReceiver resultReceiver) {
try {
- mTarget.hideSoftInput(hideInputToken, flags, resultReceiver);
+ mTarget.hideSoftInput(hideInputToken, statsToken, flags, resultReceiver);
} catch (RemoteException e) {
logRemoteException(e);
return false;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 114c512b3f95..8b083bd72722 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -129,6 +129,7 @@ import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
@@ -151,6 +152,7 @@ import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.IInputMethod;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.IInputMethodSession;
@@ -642,6 +644,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*/
private boolean mInputShown;
+ /** The token tracking the current IME request or {@code null} otherwise. */
+ @Nullable
+ private ImeTracker.Token mCurStatsToken;
+
/**
* {@code true} if the current input method is in fullscreen mode.
*/
@@ -761,7 +767,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
* <dd>
* If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
* </dd>
- * dt>{@link InputMethodService#IME_INVISIBLE}</dt>
+ * <dt>{@link InputMethodService#IME_INVISIBLE}</dt>
* <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
* currently invisible.
* </dd>
@@ -785,8 +791,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
/**
* Internal state snapshot when
- * {@link com.android.internal.view.IInputMethod#startInput(IBinder, IRemoteInputConnection, EditorInfo,
- * boolean)} is about to be called.
+ * {@link IInputMethod#startInput(IInputMethod.StartInputParams)} is about to be called.
*
* <p>Calling that IPC endpoint basically means that
* {@link InputMethodService#doStartInput(InputConnection, EditorInfo, boolean)} will be called
@@ -1071,7 +1076,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
/**
* Add a new entry and discard the oldest entry as needed.
- * @param info {@lin StartInputInfo} to be added.
+ * @param info {@link StartInputInfo} to be added.
*/
void addEntry(@NonNull StartInputInfo info) {
final int index = mNextIndex;
@@ -1189,18 +1194,18 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
} else if (accessibilityRequestingNoImeUri.equals(uri)) {
final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId);
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0 /* def */, mUserId);
mAccessibilityRequestingNoSoftKeyboard =
(accessibilitySoftKeyboardSetting & AccessibilityService.SHOW_MODE_MASK)
== AccessibilityService.SHOW_MODE_HIDDEN;
if (mAccessibilityRequestingNoSoftKeyboard) {
final boolean showRequested = mShowRequested;
- hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
+ 0 /* flags */, null /* resultReceiver */,
SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
mShowRequested = showRequested;
} else if (mShowRequested) {
- showCurrentInputLocked(mCurFocusedWindow,
- InputMethodManager.SHOW_IMPLICIT, null,
+ showCurrentInputImplicitLocked(mCurFocusedWindow,
SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE);
}
} else {
@@ -1666,8 +1671,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, 0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */, SoftInputShowHideReason.HIDE_SWITCH_USER);
final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
clientToBeReset);
mUserSwitchHandlerTask = task;
@@ -2222,7 +2227,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
try {
- client.asBinder().linkToDeath(deathRecipient, 0);
+ client.asBinder().linkToDeath(deathRecipient, 0 /* flags */);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -2247,7 +2252,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (ImfLock.class) {
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
- client.asBinder().unlinkToDeath(cs.mClientDeathRecipient, 0);
+ client.asBinder().unlinkToDeath(cs.mClientDeathRecipient, 0 /* flags */);
clearClientSessionLocked(cs);
clearClientSessionForAccessibilityLocked(cs);
@@ -2260,8 +2265,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
if (mCurClient == cs) {
- hideCurrentInputLocked(
- mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
if (mBoundToMethod) {
mBoundToMethod = false;
IInputMethodInvoker curMethod = getCurMethodLocked();
@@ -2308,6 +2313,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mCurClient.mSessionRequestedForAccessibility = false;
mCurClient = null;
mCurVirtualDisplayToScreenMatrix = null;
+ ImeTracker.get().onFailed(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
+ mCurStatsToken = null;
mMenuController.hideInputMethodMenuLocked();
}
@@ -2380,8 +2387,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
navButtonFlags, mCurImeDispatcher);
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
- showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
- SoftInputShowHideReason.ATTACH_NEW_INPUT);
+ // Re-use current statsToken, if it exists.
+ final ImeTracker.Token statsToken = mCurStatsToken;
+ mCurStatsToken = null;
+ showCurrentInputLocked(mCurFocusedWindow, statsToken, getAppShowFlagsLocked(),
+ null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
String curId = getCurIdLocked();
@@ -2500,7 +2510,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (mDisplayIdToShowIme == INVALID_DISPLAY) {
mImeHiddenByDisplayPolicy = true;
- hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */,
SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE);
return InputBindResult.NO_IME;
}
@@ -3278,22 +3289,23 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
- int lastClickTooType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, int flags, int lastClickTooType,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#showSoftInput");
synchronized (ImfLock.class) {
- if (!canInteractWithImeLocked(uid, client, "showSoftInput")) {
+ if (!canInteractWithImeLocked(uid, client, "showSoftInput", statsToken)) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
return false;
}
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
- return showCurrentInputLocked(
- windowToken, flags, lastClickTooType, resultReceiver, reason);
+ return showCurrentInputLocked(windowToken, statsToken, flags, lastClickTooType,
+ resultReceiver, reason);
} finally {
Binder.restoreCallingIdentity(ident);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3310,7 +3322,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
"InputMethodManagerService#startStylusHandwriting");
int uid = Binder.getCallingUid();
synchronized (ImfLock.class) {
- if (!canInteractWithImeLocked(uid, client, "startStylusHandwriting")) {
+ if (!canInteractWithImeLocked(uid, client, "startStylusHandwriting",
+ null /* statsToken */)) {
return;
}
if (!hasSupportedStylusLocked()) {
@@ -3365,19 +3378,33 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- boolean showCurrentInputLocked(IBinder windowToken, int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
- return showCurrentInputLocked(
- windowToken, flags, MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
+ boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ return showCurrentInputLocked(windowToken, statsToken, flags,
+ MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
}
@GuardedBy("ImfLock.class")
- private boolean showCurrentInputLocked(IBinder windowToken, int flags, int lastClickToolType,
+ private boolean showCurrentInputLocked(IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ // Create statsToken is none exists.
+ if (statsToken == null) {
+ String packageName = null;
+ if (mCurEditorInfo != null) {
+ packageName = mCurEditorInfo.packageName;
+ }
+ statsToken = new ImeTracker.Token(packageName);
+ ImeTracker.get().onRequestShow(statsToken, ImeTracker.ORIGIN_SERVER_START_INPUT,
+ reason);
+ }
+
mShowRequested = true;
if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
return false;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
if ((flags & InputMethodManager.SHOW_FORCED) != 0) {
mShowExplicitlyRequested = true;
@@ -3387,8 +3414,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
if (!mSystemReady) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_SYSTEM_READY);
return false;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_SYSTEM_READY);
mBindingController.setCurrentMethodVisible();
final IInputMethodInvoker curMethod = getCurMethodLocked();
@@ -3396,6 +3425,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
+ ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME);
+ mCurStatsToken = null;
final int showFlags = getImeShowFlagsLocked();
if (DEBUG) {
Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken
@@ -3407,23 +3439,34 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
curMethod.updateEditorToolType(lastClickToolType);
}
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
- if (curMethod.showSoftInput(showInputToken, showFlags, resultReceiver)) {
+ if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) {
onShowHideSoftInputRequested(true /* show */, windowToken, reason);
}
mInputShown = true;
return true;
+ } else {
+ ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
+ mCurStatsToken = statsToken;
}
return false;
}
@Override
- public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput");
synchronized (ImfLock.class) {
- if (!canInteractWithImeLocked(uid, client, "hideSoftInput")) {
+ if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken)) {
+ if (mInputShown) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
+ } else {
+ ImeTracker.get().onCancelled(statsToken,
+ ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
+ }
return false;
}
final long ident = Binder.clearCallingIdentity();
@@ -3431,7 +3474,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput");
if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
return InputMethodManagerService.this.hideCurrentInputLocked(windowToken,
- flags, resultReceiver, reason);
+ statsToken, flags, resultReceiver, reason);
} finally {
Binder.restoreCallingIdentity(ident);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3440,17 +3483,32 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ // Create statsToken is none exists.
+ if (statsToken == null) {
+ String packageName = null;
+ if (mCurEditorInfo != null) {
+ packageName = mCurEditorInfo.packageName;
+ }
+ statsToken = new ImeTracker.Token(packageName);
+ ImeTracker.get().onRequestHide(statsToken, ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason);
+ }
+
if ((flags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
&& (mShowExplicitlyRequested || mShowForced)) {
if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT);
return false;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT);
+
if (mShowForced && (flags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
return false;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
// There is a chance that IMM#hideSoftInput() is called in a transient state where
// IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
@@ -3461,8 +3519,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// IMMS#InputShown indicates that the software keyboard is shown.
// TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
IInputMethodInvoker curMethod = getCurMethodLocked();
- final boolean shouldHideSoftInput = (curMethod != null) && (mInputShown
- || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
+ final boolean shouldHideSoftInput = (curMethod != null)
+ && (mInputShown || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
boolean res;
if (shouldHideSoftInput) {
final Binder hideInputToken = new Binder();
@@ -3471,17 +3529,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// delivered to the IME process as an IPC. Hence the inconsistency between
// IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
// the final state.
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE);
if (DEBUG) {
Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken
+ ", " + resultReceiver + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
}
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
- if (curMethod.hideSoftInput(hideInputToken, 0 /* flags */, resultReceiver)) {
+ if (curMethod.hideSoftInput(hideInputToken, statsToken, 0 /* flags */,
+ resultReceiver)) {
onShowHideSoftInputRequested(false /* show */, windowToken, reason);
}
res = true;
} else {
+ ImeTracker.get().onCancelled(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE);
res = false;
}
mBindingController.setCurrentMethodNotVisible();
@@ -3489,6 +3550,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mShowRequested = false;
mShowExplicitlyRequested = false;
mShowForced = false;
+ // Cancel existing statsToken for show IME as we got a hide request.
+ ImeTracker.get().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
+ mCurStatsToken = null;
return res;
}
@@ -3646,8 +3710,8 @@ 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, 0, null, SoftInputShowHideReason.HIDE_INVALID_USER);
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */, SoftInputShowHideReason.HIDE_INVALID_USER);
return InputBindResult.INVALID_USER;
}
@@ -3703,7 +3767,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
boolean didStart = false;
InputBindResult res = null;
- // We shows the IME when the system allows the IME focused target window to restore the
+ // We show the IME when the system allows the IME focused target window to restore the
// IME visibility (e.g. switching to the app task when last time the IME is visible).
// Note that we don't restore IME visibility for some cases (e.g. when the soft input
// state is ALWAYS_HIDDEN or STATE_HIDDEN with forward navigation).
@@ -3715,7 +3779,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
editorInfo, startInputFlags, startInputReason, unverifiedTargetSdkVersion,
imeDispatcher);
- showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+ showCurrentInputImplicitLocked(windowToken,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
}
@@ -3728,8 +3792,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// be behind any soft input window, so hide the
// soft input window if it is shown.
if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
- hideCurrentInputLocked(
- mCurFocusedWindow, InputMethodManager.HIDE_NOT_ALWAYS, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
+ InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */,
SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
// If focused display changed, we should unbind current method
@@ -3758,10 +3822,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
imeDispatcher);
didStart = true;
}
- showCurrentInputLocked(
- windowToken,
- InputMethodManager.SHOW_IMPLICIT,
- null,
+ showCurrentInputImplicitLocked(windowToken,
SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
}
break;
@@ -3774,14 +3835,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
- hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */,
SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
}
break;
case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
if (!sameWindowFocused) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input");
- hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */,
SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
}
break;
@@ -3797,7 +3860,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
imeDispatcher);
didStart = true;
}
- showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+ showCurrentInputImplicitLocked(windowToken,
SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV);
} else {
Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
@@ -3818,7 +3881,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
imeDispatcher);
didStart = true;
}
- showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+ showCurrentInputImplicitLocked(windowToken,
SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
}
} else {
@@ -3840,7 +3903,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// an editor upon refocusing a window.
if (startInputByWinGainedFocus) {
if (DEBUG) Slog.v(TAG, "Same window without editor will hide input");
- hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
+ 0 /* flags */, null /* resultReceiver */,
SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
}
}
@@ -3854,7 +3918,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// 2) SOFT_INPUT_STATE_VISIBLE state without an editor
// 3) SOFT_INPUT_STATE_ALWAYS_VISIBLE state without an editor
if (DEBUG) Slog.v(TAG, "Window without editor will hide input");
- hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */,
SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR);
}
res = startInputUncheckedLocked(cs, inputContext,
@@ -3869,8 +3934,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- private boolean canInteractWithImeLocked(
- int uid, IInputMethodClient client, String methodName) {
+ 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
|| mCurClient.mClient.asBinder() != client.asBinder()) {
// We need to check if this is the current client with
@@ -3878,13 +3950,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// be made before input is started in it.
final ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
if (!isImeClientFocused(mCurFocusedWindow, cs)) {
Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client));
return false;
}
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
return true;
}
@@ -4237,7 +4312,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final int curTokenDisplayId;
synchronized (ImfLock.class) {
if (!canInteractWithImeLocked(callingUid, client,
- "getInputMethodWindowVisibleHeight")) {
+ "getInputMethodWindowVisibleHeight", null /* statsToken */)) {
if (!mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.get(callingUid)) {
EventLog.writeEvent(0x534e4554, "204906124", callingUid, "");
mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.put(callingUid, true);
@@ -4460,7 +4535,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
int uid = Binder.getCallingUid();
synchronized (ImfLock.class) {
- if (!canInteractWithImeLocked(uid, client, "addVirtualStylusIdForTestSession")) {
+ if (!canInteractWithImeLocked(uid, client, "addVirtualStylusIdForTestSession",
+ null /* statsToken */)) {
return;
}
final long ident = Binder.clearCallingIdentity();
@@ -4487,7 +4563,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
int uid = Binder.getCallingUid();
synchronized (ImfLock.class) {
- if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest")) {
+ if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest",
+ null /* statsToken */)) {
return;
}
final long ident = Binder.clearCallingIdentity();
@@ -4672,7 +4749,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) {
+ private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible,
+ @Nullable ImeTracker.Token statsToken) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
@@ -4680,13 +4758,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
if (!setVisible) {
if (mCurClient != null) {
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
+
mWindowManagerInternal.hideIme(
mHideRequestWindowMap.get(windowToken),
- mCurClient.mSelfReportedDisplayId);
+ mCurClient.mSelfReportedDisplayId, statsToken);
+ } else {
+ ImeTracker.get().onFailed(statsToken,
+ ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
}
} else {
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
// Send to window manager to show IME after IME layout finishes.
- mWindowManagerInternal.showImePostLayout(mShowRequestWindowMap.get(windowToken));
+ mWindowManagerInternal.showImePostLayout(mShowRequestWindowMap.get(windowToken),
+ statsToken);
}
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -4753,7 +4840,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
final long ident = Binder.clearCallingIdentity();
try {
- hideCurrentInputLocked(mLastImeTargetWindow, flags, null, reason);
+ hideCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags,
+ null /* resultReceiver */, reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4770,7 +4858,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
final long ident = Binder.clearCallingIdentity();
try {
- showCurrentInputLocked(mLastImeTargetWindow, flags, null,
+ showCurrentInputLocked(mLastImeTargetWindow, null /* statsToken */, flags,
+ null /* resultReceiver */,
SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4861,7 +4950,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case MSG_HIDE_CURRENT_INPUT_METHOD:
synchronized (ImfLock.class) {
final @SoftInputShowHideReason int reason = (int) msg.obj;
- hideCurrentInputLocked(mCurFocusedWindow, 0, null, reason);
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
+ null /* resultReceiver */, reason);
}
return true;
@@ -6349,7 +6439,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getCurrentUserId()) {
- hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */,
+ 0 /* flags */, null /* resultReceiver */,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
mBindingController.unbindCurrentMethod();
// Reset the current IME
@@ -6614,8 +6705,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible) {
- mImms.applyImeVisibility(mToken, windowToken, setVisible);
+ public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible,
+ @Nullable ImeTracker.Token statsToken) {
+ mImms.applyImeVisibility(mToken, windowToken, setVisible, statsToken);
}
@BinderThread
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8b3444318636..12efe0dc074f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -230,6 +230,7 @@ import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.view.inputmethod.ImeTracker;
import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
import android.window.ScreenCapture;
@@ -454,7 +455,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/**
* Compat metrics computed based on {@link #mDisplayMetrics}.
- * @see #updateDisplayAndOrientation(int, Configuration)
+ * @see #updateDisplayAndOrientation(Configuration)
*/
private final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics();
@@ -5031,7 +5032,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* layer has been assigned since), to facilitate assigning the layer from the IME target, or
* fall back if there is no target.
* - the container doesn't always participate in window traversal, according to
- * {@link #skipImeWindowsDuringTraversal()}
+ * {@link #skipImeWindowsDuringTraversal(DisplayContent)}
*/
private static class ImeContainer extends DisplayArea.Tokens {
boolean mNeedsLayer = false;
@@ -6712,25 +6713,35 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mRemoteInsetsController.insetsControlChanged(stateController.getRawInsetsState(),
stateController.getControlsForDispatch(this));
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to deliver inset state change", e);
+ Slog.w(TAG, "Failed to deliver inset control state change", e);
}
}
@Override
- public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
try {
- mRemoteInsetsController.showInsets(types, fromIme);
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS);
+ mRemoteInsetsController.showInsets(types, fromIme, statsToken);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to deliver showInsets", e);
+ ImeTracker.get().onFailed(statsToken,
+ ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS);
}
}
@Override
- public void hideInsets(@InsetsType int types, boolean fromIme) {
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
try {
- mRemoteInsetsController.hideInsets(types, fromIme);
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS);
+ mRemoteInsetsController.hideInsets(types, fromIme, statsToken);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to deliver showInsets", e);
+ Slog.w(TAG, "Failed to deliver hideInsets", e);
+ ImeTracker.get().onFailed(statsToken,
+ ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 25891485330e..1fef3c22a523 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2071,7 +2071,8 @@ public class DisplayPolicy {
// Don't show status bar when swiping on already visible navigation bar.
// But restore the position of navigation bar if it has been moved by the control
// target.
- controlTarget.showInsets(Type.navigationBars(), false);
+ controlTarget.showInsets(Type.navigationBars(), false /* fromIme */,
+ null /* statsToken */);
return;
}
@@ -2079,10 +2080,12 @@ public class DisplayPolicy {
// Show transient bars if they are hidden; restore position if they are visible.
mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE,
isGestureOnSystemBar);
- controlTarget.showInsets(restorePositionTypes, false);
+ controlTarget.showInsets(restorePositionTypes, false /* fromIme */,
+ null /* statsToken */);
} else {
// Restore visibilities and positions of system bars.
- controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false);
+ controlTarget.showInsets(Type.statusBars() | Type.navigationBars(),
+ false /* fromIme */, null /* statsToken */);
// To further allow the pull-down-from-the-top gesture to pull down the notification
// shade as a consistent motion, we reroute the touch events here from the currently
// touched window to the status bar after making it visible.
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 554791a0da91..7fd093fffe95 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -33,9 +33,11 @@ import android.graphics.Rect;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
+import android.view.InsetsSourceConsumer;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.WindowInsets;
+import android.view.inputmethod.ImeTracker;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +51,9 @@ import java.io.PrintWriter;
*/
final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider {
+ /** The token tracking the current IME request or {@code null} otherwise. */
+ @Nullable
+ private ImeTracker.Token mImeRequesterStatsToken;
private InsetsControlTarget mImeRequester;
private Runnable mShowImeRunner;
private boolean mIsImeLayoutDrawn;
@@ -162,14 +167,20 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
}
/**
- * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
- * requests to show IME on {@param imeTarget}.
+ * Called from {@link WindowManagerInternal#showImePostLayout}
+ * when {@link android.inputmethodservice.InputMethodService} requests to show IME
+ * on {@param imeTarget}.
*
- * @param imeTarget imeTarget on which IME request is coming from.
+ * @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.
*/
- void scheduleShowImePostLayout(InsetsControlTarget imeTarget) {
+ void scheduleShowImePostLayout(InsetsControlTarget imeTarget,
+ @Nullable ImeTracker.Token statsToken) {
boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
mImeRequester = imeTarget;
+ // There was still a stats token, so that request presumably failed.
+ ImeTracker.get().onFailed(mImeRequesterStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+ mImeRequesterStatsToken = statsToken;
if (targetChanged) {
// target changed, check if new target can show IME.
ProtoLog.d(WM_DEBUG_IME, "IME target changed within ActivityRecord");
@@ -183,15 +194,20 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeRequester.getWindow() == null
? mImeRequester : mImeRequester.getWindow().getName());
mShowImeRunner = () -> {
+ ImeTracker.get().onProgress(mImeRequesterStatsToken,
+ ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
// Target should still be the same.
if (isReadyToShowIme()) {
+ ImeTracker.get().onProgress(mImeRequesterStatsToken,
+ ImeTracker.PHASE_WM_SHOW_IME_READY);
final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
target.getWindow() != null ? target.getWindow().getName() : "");
setImeShowing(true);
- target.showInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ target.showInsets(WindowInsets.Type.ime(), true /* fromIme */,
+ mImeRequesterStatsToken);
Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
if (target != mImeRequester && mImeRequester != null) {
ProtoLog.w(WM_DEBUG_IME,
@@ -199,7 +215,12 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
(mImeRequester.getWindow() != null
? mImeRequester.getWindow().getName() : ""));
}
+ } else {
+ ImeTracker.get().onFailed(mImeRequesterStatsToken,
+ ImeTracker.PHASE_WM_SHOW_IME_READY);
}
+ // Clear token here so we don't report an error in abortShowImePostLayout().
+ mImeRequesterStatsToken = null;
abortShowImePostLayout();
};
mDisplayContent.mWmService.requestTraversal();
@@ -234,6 +255,8 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
mImeRequester = null;
mIsImeLayoutDrawn = false;
mShowImeRunner = null;
+ ImeTracker.get().onCancelled(mImeRequesterStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+ mImeRequesterStatsToken = null;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index d35b7c3d5fbe..8ecbc177896c 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -16,9 +16,11 @@
package com.android.server.wm;
+import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
/**
* Generalization of an object that can control insets state.
@@ -57,8 +59,10 @@ interface InsetsControlTarget {
*
* @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.
*/
- default void showInsets(@InsetsType int types, boolean fromIme) {
+ default void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
}
/**
@@ -66,8 +70,10 @@ interface InsetsControlTarget {
*
* @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.
*/
- default void hideInsets(@InsetsType int types, boolean fromIme) {
+ default void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
}
/**
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index b9fa80cf2c0f..f66fa0f33b04 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -799,7 +799,7 @@ class InsetsPolicy {
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show
? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
- null /* translator */);
+ null /* translator */, null /* statsToken */);
SurfaceAnimationThread.getHandler().post(
() -> mListener.onReady(mAnimationControl, typesReady));
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index c206a15503de..bab3a0553e63 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -43,6 +43,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
+import android.view.inputmethod.ImeTracker;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
@@ -729,16 +730,20 @@ public abstract class WindowManagerInternal {
* Show IME on imeTargetWindow once IME has finished layout.
*
* @param imeTargetWindowToken token of the (IME target) window on which IME should be shown.
+ * @param statsToken the token tracking the current IME show request or {@code null} otherwise.
*/
- public abstract void showImePostLayout(IBinder imeTargetWindowToken);
+ public abstract void showImePostLayout(IBinder imeTargetWindowToken,
+ @Nullable ImeTracker.Token statsToken);
/**
* Hide IME using imeTargetWindow when requested.
*
* @param imeTargetWindowToken token of the (IME target) window on which IME should be hidden.
* @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.
*/
- public abstract void hideIme(IBinder imeTargetWindowToken, int displayId);
+ public abstract void hideIme(IBinder imeTargetWindowToken, int displayId,
+ @Nullable 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 4080223f817f..3419207eb14f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -292,6 +292,7 @@ import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import android.window.ITaskFpsCallback;
import android.window.ScreenCapture;
@@ -8014,7 +8015,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void showImePostLayout(IBinder imeTargetWindowToken) {
+ public void showImePostLayout(IBinder imeTargetWindowToken,
+ @Nullable ImeTracker.Token statsToken) {
synchronized (mGlobalLock) {
InputTarget imeTarget = getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
if (imeTarget == null) {
@@ -8023,17 +8025,18 @@ public class WindowManagerService extends IWindowManager.Stub
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
imeTarget = controlTarget.getWindow();
- // If InsetsControlTarget doesn't have a window, its using remoteControlTarget which
- // is controlled by default display
+ // If InsetsControlTarget doesn't have a window, it's using remoteControlTarget
+ // which is controlled by default display
final DisplayContent dc = imeTarget != null
? imeTarget.getDisplayContent() : getDefaultDisplayContentLocked();
dc.getInsetsStateController().getImeSourceProvider()
- .scheduleShowImePostLayout(controlTarget);
+ .scheduleShowImePostLayout(controlTarget, statsToken);
}
}
@Override
- public void hideIme(IBinder imeTargetWindowToken, int displayId) {
+ public void hideIme(IBinder imeTargetWindowToken, int displayId,
+ @Nullable ImeTracker.Token statsToken) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.hideIme");
synchronized (mGlobalLock) {
WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
@@ -8049,10 +8052,15 @@ public class WindowManagerService extends IWindowManager.Stub
dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
}
if (dc != null && dc.getImeTarget(IME_TARGET_CONTROL) != null) {
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
ProtoLog.d(WM_DEBUG_IME, "hideIme Control target: %s ",
dc.getImeTarget(IME_TARGET_CONTROL));
- dc.getImeTarget(IME_TARGET_CONTROL).hideInsets(
- WindowInsets.Type.ime(), true /* fromIme */);
+ dc.getImeTarget(IME_TARGET_CONTROL).hideInsets(WindowInsets.Type.ime(),
+ true /* fromIme */, statsToken);
+ } else {
+ ImeTracker.get().onFailed(statsToken,
+ ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
}
if (dc != null) {
dc.getInsetsStateController().getImeSourceProvider().setImeShowing(false);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2712cb7c4c35..5c5c70334e65 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -249,6 +249,7 @@ import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import android.window.OnBackInvokedCallbackInfo;
@@ -4018,7 +4019,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mClient.insetsControlChanged(getCompatInsetsState(),
stateController.getControlsForDispatch(this));
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to deliver inset state change to w=" + this, e);
+ Slog.w(TAG, "Failed to deliver inset control state change to w=" + this, e);
}
}
@@ -4028,20 +4029,30 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- public void showInsets(@InsetsType int types, boolean fromIme) {
+ public void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
try {
- mClient.showInsets(types, fromIme);
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS);
+ mClient.showInsets(types, fromIme, statsToken);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to deliver showInsets", e);
+ ImeTracker.get().onFailed(statsToken,
+ ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS);
}
}
@Override
- public void hideInsets(@InsetsType int types, boolean fromIme) {
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
try {
- mClient.hideInsets(types, fromIme);
+ ImeTracker.get().onProgress(statsToken,
+ ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS);
+ mClient.hideInsets(types, fromIme, statsToken);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to deliver showInsets", e);
+ Slog.w(TAG, "Failed to deliver hideInsets", e);
+ ImeTracker.get().onFailed(statsToken,
+ ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS);
}
}
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 3fbc4004785d..640bde330cee 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -244,7 +244,7 @@ public class InputMethodManagerServiceTestBase {
.setCurrentMethodVisible();
}
verify(mMockInputMethod, times(showSoftInput ? 1 : 0))
- .showSoftInput(any(), anyInt(), any());
+ .showSoftInput(any(), any(), anyInt(), any());
}
protected void verifyHideSoftInput(boolean setNotVisible, boolean hideSoftInput)
@@ -254,6 +254,6 @@ public class InputMethodManagerServiceTestBase {
.setCurrentMethodNotVisible();
}
verify(mMockInputMethod, times(hideSoftInput ? 1 : 0))
- .hideSoftInput(any(), anyInt(), any());
+ .hideSoftInput(any(), any(), anyInt(), any());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 52af8adcf9f6..d99946f0a5c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -45,6 +45,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -375,10 +376,10 @@ public class DisplayPolicyTests extends WindowTestsBase {
displayPolicy.setCanSystemBarsBeShownByUser(false);
displayPolicy.requestTransientBars(windowState, true);
- verify(controlTarget, never()).showInsets(anyInt(), anyBoolean());
+ verify(controlTarget, never()).showInsets(anyInt(), anyBoolean(), any() /* statsToken */);
displayPolicy.setCanSystemBarsBeShownByUser(true);
displayPolicy.requestTransientBars(windowState, true);
- verify(controlTarget).showInsets(anyInt(), anyBoolean());
+ verify(controlTarget).showInsets(anyInt(), anyBoolean(), any() /* statsToken */);
}
}
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 eb8b89d59310..a26cad98f4d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -55,7 +55,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mDisplayContent.setImeControlTarget(popup);
mDisplayContent.setImeLayeringTarget(appWin);
popup.mAttrs.format = PixelFormat.TRANSPARENT;
- mImeProvider.scheduleShowImePostLayout(appWin);
+ mImeProvider.scheduleShowImePostLayout(appWin, null /* statsToken */);
assertTrue(mImeProvider.isReadyToShowIme());
}
@@ -64,7 +64,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
WindowState target = createWindow(null, TYPE_APPLICATION, "app");
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.updateImeInputAndControlTarget(target);
- mImeProvider.scheduleShowImePostLayout(target);
+ mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */);
assertTrue(mImeProvider.isReadyToShowIme());
}
@@ -78,7 +78,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.setImeControlTarget(target);
- mImeProvider.scheduleShowImePostLayout(target);
+ mImeProvider.scheduleShowImePostLayout(target, null /* statsToken */);
assertFalse(mImeProvider.isImeShowing());
mImeProvider.checkShowImePostLayout();
assertTrue(mImeProvider.isImeShowing());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index bb5acebfacd7..6e72bf360295 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -26,6 +27,7 @@ import android.view.IWindow;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.ScrollCaptureResponse;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
@@ -117,10 +119,12 @@ public class TestIWindow extends IWindow.Stub {
}
@Override
- public void showInsets(int types, boolean fromIme) throws RemoteException {
+ public void showInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)
+ throws RemoteException {
}
@Override
- public void hideInsets(int types, boolean fromIme) throws RemoteException {
+ public void hideInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)
+ throws RemoteException {
}
}
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 0139f6a5695a..6bd341210cad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -1000,7 +1000,7 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(app);
mDisplayContent.setImeInputTarget(app);
assertTrue(mDisplayContent.shouldImeAttachedToApp());
- controller.getImeSourceProvider().scheduleShowImePostLayout(app);
+ controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */);
controller.getImeSourceProvider().getSource().setVisible(true);
controller.updateAboveInsetsState(false);
@@ -1037,7 +1037,7 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(app);
mDisplayContent.setImeInputTarget(app);
assertTrue(mDisplayContent.shouldImeAttachedToApp());
- controller.getImeSourceProvider().scheduleShowImePostLayout(app);
+ controller.getImeSourceProvider().scheduleShowImePostLayout(app, null /* statsToken */);
controller.getImeSourceProvider().getSource().setVisible(true);
controller.updateAboveInsetsState(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index eca7cbbc5486..ab042d15963b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -100,6 +100,7 @@ import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
+import android.view.inputmethod.ImeTracker;
import android.window.ITransitionPlayer;
import android.window.ScreenCapture;
import android.window.StartingWindowInfo;
@@ -848,11 +849,13 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
@Override
- public void showInsets(int i, boolean b) throws RemoteException {
+ public void showInsets(int i, boolean b, @Nullable ImeTracker.Token t)
+ throws RemoteException {
}
@Override
- public void hideInsets(int i, boolean b) throws RemoteException {
+ public void hideInsets(int i, boolean b, @Nullable ImeTracker.Token t)
+ throws RemoteException {
}
@Override