Implement ImeTrackerService
Bug: 261716110 Test: atest android.view.inputmethod.cts.InputMethodStatsTest Change-Id: I432bab2de58a9df2c421bb00946ab211de445660
This commit is contained in:
@@ -3256,6 +3256,7 @@ package android.view.inputmethod {
|
||||
method public int getDisplayId();
|
||||
method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
|
||||
method public boolean hasActiveInputConnection(@Nullable android.view.View);
|
||||
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
|
||||
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
|
||||
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long);
|
||||
field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.os.Trace.TRACE_TAG_VIEW;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.IS_HIDE_ANIMATION_RUNNING;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
|
||||
@@ -23,11 +24,15 @@ import static android.view.ImeInsetsSourceConsumerProto.IS_SHOW_REQUESTED_DURING
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.Trace;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.inputmethod.ImeTracker;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.android.internal.inputmethod.ImeTracing;
|
||||
import com.android.internal.inputmethod.SoftInputShowHideReason;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -48,8 +53,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
/**
|
||||
* Tracks whether {@link WindowInsetsController#show(int)} or
|
||||
* {@link InputMethodManager#showSoftInput(View, int)} is called during IME hide animation.
|
||||
* If it was called, we should not call {@link InputMethodManager#notifyImeHidden(IBinder)},
|
||||
* because the IME is being shown.
|
||||
* If it was called, we should not call {@link InputMethodManager#notifyImeHidden(IBinder,
|
||||
* ImeTracker.Token)}, because the IME is being shown.
|
||||
*/
|
||||
private boolean mIsShowRequestedDuringHideAnimation;
|
||||
|
||||
@@ -76,7 +81,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
// Remove IME surface as IME has finished hide animation, if there is no pending
|
||||
// show request.
|
||||
if (!mIsShowRequestedDuringHideAnimation) {
|
||||
notifyHidden();
|
||||
notifyHidden(null /* statsToken */);
|
||||
removeSurface();
|
||||
}
|
||||
}
|
||||
@@ -120,7 +125,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
* @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
|
||||
*/
|
||||
@Override
|
||||
public @ShowResult int requestShow(boolean fromIme) {
|
||||
@ShowResult
|
||||
public int requestShow(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
|
||||
if (fromIme) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"ImeInsetsSourceConsumer#requestShow",
|
||||
@@ -129,6 +135,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
|
||||
// TODO: ResultReceiver for IME.
|
||||
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
|
||||
ImeTracker.get().onProgress(statsToken,
|
||||
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW);
|
||||
|
||||
if (getControl() == null) {
|
||||
// If control is null, schedule to show IME when control is available.
|
||||
mIsRequestedVisibleAwaitingControl = true;
|
||||
@@ -140,16 +149,32 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
return ShowResult.SHOW_IMMEDIATELY;
|
||||
}
|
||||
|
||||
return getImm().requestImeShow(mController.getHost().getWindowToken())
|
||||
return getImm().requestImeShow(mController.getHost().getWindowToken(), statsToken)
|
||||
? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
|
||||
* IME insets are hidden.
|
||||
*
|
||||
* @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
|
||||
*/
|
||||
private void notifyHidden() {
|
||||
getImm().notifyImeHidden(mController.getHost().getWindowToken());
|
||||
private void notifyHidden(@Nullable ImeTracker.Token statsToken) {
|
||||
// Create a new stats token to track the hide request when:
|
||||
// - we do not already have one, or
|
||||
// - we do already have one, but we have control and use the passed in token
|
||||
// for the insets animation already.
|
||||
if (statsToken == null || getControl() != null) {
|
||||
statsToken = ImeTracker.get().onRequestHide(null /* component */, Process.myUid(),
|
||||
ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
|
||||
SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
|
||||
}
|
||||
|
||||
ImeTracker.get().onProgress(statsToken,
|
||||
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN);
|
||||
|
||||
getImm().notifyImeHidden(mController.getHost().getWindowToken(), statsToken);
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.graphics.Rect;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.Trace;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
@@ -65,6 +66,7 @@ import android.view.inputmethod.InputMethodManager;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
|
||||
import com.android.internal.inputmethod.ImeTracing;
|
||||
import com.android.internal.inputmethod.SoftInputShowHideReason;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -978,7 +980,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
@Override
|
||||
public void show(@InsetsType int types) {
|
||||
show(types, false /* fromIme */, null /* statsToken */);
|
||||
ImeTracker.Token statsToken = null;
|
||||
if ((types & ime()) != 0) {
|
||||
statsToken = ImeTracker.get().onRequestShow(null /* component */,
|
||||
Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
|
||||
SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API);
|
||||
}
|
||||
|
||||
show(types, false /* fromIme */, statsToken);
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
@@ -1055,7 +1064,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
@Override
|
||||
public void hide(@InsetsType int types) {
|
||||
hide(types, false /* fromIme */, null /* statsToken */);
|
||||
ImeTracker.Token statsToken = null;
|
||||
if ((types & ime()) != 0) {
|
||||
statsToken = ImeTracker.get().onRequestHide(null /* component */,
|
||||
Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
|
||||
SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
|
||||
}
|
||||
|
||||
hide(types, false /* fromIme */, statsToken);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -1165,13 +1181,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes);
|
||||
types &= ~mDisabledUserAnimationInsetsTypes;
|
||||
|
||||
if (fromIme && (disabledTypes & ime()) != 0
|
||||
&& !mState.getSource(mImeSourceConsumer.getId()).isVisible()) {
|
||||
// We've requested IMM to show IME, but the IME is not controllable. We need to
|
||||
// cancel the request.
|
||||
setRequestedVisibleTypes(0 /* visibleTypes */, ime());
|
||||
if (mImeSourceConsumer.onAnimationStateChanged(false /* running */)) {
|
||||
notifyVisibilityChanged();
|
||||
if ((disabledTypes & ime()) != 0) {
|
||||
ImeTracker.get().onFailed(statsToken,
|
||||
ImeTracker.PHASE_CLIENT_DISABLED_USER_ANIMATION);
|
||||
|
||||
if (fromIme && !mState.getSource(mImeSourceConsumer.getId()).isVisible()) {
|
||||
// We've requested IMM to show IME, but the IME is not controllable. We need to
|
||||
// cancel the request.
|
||||
setRequestedVisibleTypes(0 /* visibleTypes */, ime());
|
||||
if (mImeSourceConsumer.onAnimationStateChanged(false /* running */)) {
|
||||
notifyVisibilityChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1181,6 +1201,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
|
||||
return;
|
||||
}
|
||||
ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_DISABLED_USER_ANIMATION);
|
||||
|
||||
cancelExistingControllers(types);
|
||||
if (DEBUG) Log.d(TAG, "controlAnimation types: " + types);
|
||||
mLastStartedAnimTypes |= types;
|
||||
@@ -1188,7 +1210,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
|
||||
|
||||
Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
|
||||
fromIme, types, controls, animationType);
|
||||
fromIme, types, controls, animationType, statsToken);
|
||||
int typesReady = typesReadyPair.first;
|
||||
boolean imeReady = typesReadyPair.second;
|
||||
if (DEBUG) Log.d(TAG, String.format(
|
||||
@@ -1279,7 +1301,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
* @return Pair of (types ready to animate, IME ready to animate).
|
||||
*/
|
||||
private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, @InsetsType int types,
|
||||
SparseArray<InsetsSourceControl> controls, @AnimationType int animationType) {
|
||||
SparseArray<InsetsSourceControl> controls, @AnimationType int animationType,
|
||||
@Nullable ImeTracker.Token statsToken) {
|
||||
ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS);
|
||||
|
||||
int typesReady = 0;
|
||||
boolean imeReady = true;
|
||||
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
|
||||
@@ -1292,7 +1317,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
boolean canRun = true;
|
||||
if (show) {
|
||||
// Show request
|
||||
switch(consumer.requestShow(fromIme)) {
|
||||
switch(consumer.requestShow(fromIme, statsToken)) {
|
||||
case ShowResult.SHOW_IMMEDIATELY:
|
||||
break;
|
||||
case ShowResult.IME_SHOW_DELAYED:
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.util.Log;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
import android.view.inputmethod.ImeTracker;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
@@ -50,10 +51,14 @@ import java.util.function.Supplier;
|
||||
public class InsetsSourceConsumer {
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(value = {ShowResult.SHOW_IMMEDIATELY, ShowResult.IME_SHOW_DELAYED, ShowResult.IME_SHOW_FAILED})
|
||||
@IntDef(value = {
|
||||
ShowResult.SHOW_IMMEDIATELY,
|
||||
ShowResult.IME_SHOW_DELAYED,
|
||||
ShowResult.IME_SHOW_FAILED
|
||||
})
|
||||
@interface ShowResult {
|
||||
/**
|
||||
* Window type is ready to be shown, will be shown immidiately.
|
||||
* Window type is ready to be shown, will be shown immediately.
|
||||
*/
|
||||
int SHOW_IMMEDIATELY = 0;
|
||||
/**
|
||||
@@ -71,11 +76,13 @@ public class InsetsSourceConsumer {
|
||||
protected final InsetsController mController;
|
||||
protected final InsetsState mState;
|
||||
private int mId;
|
||||
private final @InsetsType int mType;
|
||||
@InsetsType
|
||||
private final int mType;
|
||||
|
||||
private static final String TAG = "InsetsSourceConsumer";
|
||||
private final Supplier<Transaction> mTransactionSupplier;
|
||||
private @Nullable InsetsSourceControl mSourceControl;
|
||||
@Nullable
|
||||
private InsetsSourceControl mSourceControl;
|
||||
private boolean mHasWindowFocus;
|
||||
|
||||
/**
|
||||
@@ -180,7 +187,7 @@ public class InsetsSourceConsumer {
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public InsetsSourceControl getControl() {
|
||||
return mSourceControl;
|
||||
}
|
||||
@@ -280,10 +287,16 @@ public class InsetsSourceConsumer {
|
||||
* @param fromController {@code true} if request is coming from controller.
|
||||
* (e.g. in IME case, controller is
|
||||
* {@link android.inputmethodservice.InputMethodService}).
|
||||
* @param statsToken the token tracking the current IME show request or {@code null} otherwise.
|
||||
*
|
||||
* @implNote The {@code statsToken} is ignored here, and only handled in
|
||||
* {@link ImeInsetsSourceConsumer} for IME animations only.
|
||||
*
|
||||
* @return @see {@link ShowResult}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public @ShowResult int requestShow(boolean fromController) {
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
@ShowResult
|
||||
public int requestShow(boolean fromController, @Nullable ImeTracker.Token statsToken) {
|
||||
return ShowResult.SHOW_IMMEDIATELY;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.android.internal.inputmethod.InputBindResult;
|
||||
import com.android.internal.inputmethod.SoftInputShowHideReason;
|
||||
import com.android.internal.inputmethod.StartInputFlags;
|
||||
import com.android.internal.inputmethod.StartInputReason;
|
||||
import com.android.internal.view.IImeTracker;
|
||||
import com.android.internal.view.IInputMethodManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -61,6 +62,9 @@ final class IInputMethodManagerGlobalInvoker {
|
||||
@Nullable
|
||||
private static volatile IInputMethodManager sServiceCache = null;
|
||||
|
||||
@Nullable
|
||||
private static volatile IImeTracker sTrackerServiceCache = null;
|
||||
|
||||
/**
|
||||
* @return {@code true} if {@link IInputMethodManager} is available.
|
||||
*/
|
||||
@@ -527,4 +531,137 @@ final class IInputMethodManagerGlobalInvoker {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@Nullable
|
||||
static IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
final IImeTracker service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return service.onRequestShow(uid, origin, reason);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@Nullable
|
||||
static IBinder onRequestHide(int uid, @ImeTracker.Origin int origin,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
final IImeTracker service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return service.onRequestHide(uid, origin, reason);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
static void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
|
||||
final IImeTracker service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
service.onProgress(statsToken, phase);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
static void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
|
||||
final IImeTracker service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
service.onFailed(statsToken, phase);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
static void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
|
||||
final IImeTracker service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
service.onCancelled(statsToken, phase);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
static void onShown(@NonNull IBinder statsToken) {
|
||||
final IImeTracker service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
service.onShown(statsToken);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
static void onHidden(@NonNull IBinder statsToken) {
|
||||
final IImeTracker service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
service.onHidden(statsToken);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
|
||||
static boolean hasPendingImeVisibilityRequests() {
|
||||
final var service = getImeTrackerService();
|
||||
if (service == null) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return service.hasPendingImeVisibilityRequests();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@Nullable
|
||||
private static IImeTracker getImeTrackerService() {
|
||||
var trackerService = sTrackerServiceCache;
|
||||
if (trackerService == null) {
|
||||
final var service = getService();
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
trackerService = service.getImeTrackerService();
|
||||
if (trackerService == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
sTrackerServiceCache = trackerService;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
return trackerService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
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;
|
||||
@@ -46,6 +43,46 @@ public interface ImeTracker {
|
||||
|
||||
String TAG = "ImeTracker";
|
||||
|
||||
/** The type of the IME request. */
|
||||
@IntDef(prefix = { "TYPE_" }, value = {
|
||||
TYPE_SHOW,
|
||||
TYPE_HIDE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Type {}
|
||||
|
||||
/** IME show request type. */
|
||||
int TYPE_SHOW = ImeProtoEnums.TYPE_SHOW;
|
||||
|
||||
/** IME hide request type. */
|
||||
int TYPE_HIDE = ImeProtoEnums.TYPE_HIDE;
|
||||
|
||||
/** The status of the IME request. */
|
||||
@IntDef(prefix = { "STATUS_" }, value = {
|
||||
STATUS_RUN,
|
||||
STATUS_CANCEL,
|
||||
STATUS_FAIL,
|
||||
STATUS_SUCCESS,
|
||||
STATUS_TIMEOUT
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Status {}
|
||||
|
||||
/** IME request running. */
|
||||
int STATUS_RUN = ImeProtoEnums.STATUS_RUN;
|
||||
|
||||
/** IME request cancelled. */
|
||||
int STATUS_CANCEL = ImeProtoEnums.STATUS_CANCEL;
|
||||
|
||||
/** IME request failed. */
|
||||
int STATUS_FAIL = ImeProtoEnums.STATUS_FAIL;
|
||||
|
||||
/** IME request succeeded. */
|
||||
int STATUS_SUCCESS = ImeProtoEnums.STATUS_SUCCESS;
|
||||
|
||||
/** IME request timed out. */
|
||||
int STATUS_TIMEOUT = ImeProtoEnums.STATUS_TIMEOUT;
|
||||
|
||||
/**
|
||||
* The origin of the IME request
|
||||
*
|
||||
@@ -61,25 +98,17 @@ public interface ImeTracker {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Origin {}
|
||||
|
||||
/**
|
||||
* The IME show request originated in the client.
|
||||
*/
|
||||
int ORIGIN_CLIENT_SHOW_SOFT_INPUT = 0;
|
||||
/** The IME show request originated in the client. */
|
||||
int ORIGIN_CLIENT_SHOW_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_SHOW_SOFT_INPUT;
|
||||
|
||||
/**
|
||||
* The IME hide request originated in the client.
|
||||
*/
|
||||
int ORIGIN_CLIENT_HIDE_SOFT_INPUT = 1;
|
||||
/** The IME hide request originated in the client. */
|
||||
int ORIGIN_CLIENT_HIDE_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_HIDE_SOFT_INPUT;
|
||||
|
||||
/**
|
||||
* The IME show request originated in the server.
|
||||
*/
|
||||
int ORIGIN_SERVER_START_INPUT = 2;
|
||||
/** The IME show request originated in the server. */
|
||||
int ORIGIN_SERVER_START_INPUT = ImeProtoEnums.ORIGIN_SERVER_START_INPUT;
|
||||
|
||||
/**
|
||||
* The IME hide request originated in the server.
|
||||
*/
|
||||
int ORIGIN_SERVER_HIDE_INPUT = 3;
|
||||
/** The IME hide request originated in the server. */
|
||||
int ORIGIN_SERVER_HIDE_INPUT = ImeProtoEnums.ORIGIN_SERVER_HIDE_INPUT;
|
||||
|
||||
/**
|
||||
* The current phase of the IME request.
|
||||
@@ -88,6 +117,7 @@ public interface ImeTracker {
|
||||
* where the phase is (i.e. {@code PHASE_SERVER_...} occurs in the server).
|
||||
*/
|
||||
@IntDef(prefix = { "PHASE_" }, value = {
|
||||
PHASE_NOT_SET,
|
||||
PHASE_CLIENT_VIEW_SERVED,
|
||||
PHASE_SERVER_CLIENT_KNOWN,
|
||||
PHASE_SERVER_CLIENT_FOCUSED,
|
||||
@@ -121,6 +151,11 @@ public interface ImeTracker {
|
||||
PHASE_CLIENT_HANDLE_HIDE_INSETS,
|
||||
PHASE_CLIENT_APPLY_ANIMATION,
|
||||
PHASE_CLIENT_CONTROL_ANIMATION,
|
||||
PHASE_CLIENT_DISABLED_USER_ANIMATION,
|
||||
PHASE_CLIENT_COLLECT_SOURCE_CONTROLS,
|
||||
PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW,
|
||||
PHASE_CLIENT_REQUEST_IME_SHOW,
|
||||
PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN,
|
||||
PHASE_CLIENT_ANIMATION_RUNNING,
|
||||
PHASE_CLIENT_ANIMATION_CANCEL,
|
||||
PHASE_CLIENT_ANIMATION_FINISHED_SHOW,
|
||||
@@ -129,135 +164,172 @@ public interface ImeTracker {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Phase {}
|
||||
|
||||
int PHASE_NOT_SET = ImeProtoEnums.PHASE_NOT_SET;
|
||||
|
||||
/** The view that requested the IME has been served by the IMM. */
|
||||
int PHASE_CLIENT_VIEW_SERVED = 0;
|
||||
int PHASE_CLIENT_VIEW_SERVED = ImeProtoEnums.PHASE_CLIENT_VIEW_SERVED;
|
||||
|
||||
/** The IME client that requested the IME has window manager focus. */
|
||||
int PHASE_SERVER_CLIENT_KNOWN = 1;
|
||||
int PHASE_SERVER_CLIENT_KNOWN = ImeProtoEnums.PHASE_SERVER_CLIENT_KNOWN;
|
||||
|
||||
/** The IME client that requested the IME has IME focus. */
|
||||
int PHASE_SERVER_CLIENT_FOCUSED = 2;
|
||||
int PHASE_SERVER_CLIENT_FOCUSED = ImeProtoEnums.PHASE_SERVER_CLIENT_FOCUSED;
|
||||
|
||||
/** The IME request complies with the current accessibility settings. */
|
||||
int PHASE_SERVER_ACCESSIBILITY = 3;
|
||||
int PHASE_SERVER_ACCESSIBILITY = ImeProtoEnums.PHASE_SERVER_ACCESSIBILITY;
|
||||
|
||||
/** The server is ready to run third party code. */
|
||||
int PHASE_SERVER_SYSTEM_READY = 4;
|
||||
int PHASE_SERVER_SYSTEM_READY = ImeProtoEnums.PHASE_SERVER_SYSTEM_READY;
|
||||
|
||||
/** Checked the implicit hide request against any explicit show requests. */
|
||||
int PHASE_SERVER_HIDE_IMPLICIT = 5;
|
||||
int PHASE_SERVER_HIDE_IMPLICIT = ImeProtoEnums.PHASE_SERVER_HIDE_IMPLICIT;
|
||||
|
||||
/** Checked the not-always hide request against any forced show requests. */
|
||||
int PHASE_SERVER_HIDE_NOT_ALWAYS = 6;
|
||||
int PHASE_SERVER_HIDE_NOT_ALWAYS = ImeProtoEnums.PHASE_SERVER_HIDE_NOT_ALWAYS;
|
||||
|
||||
/** The server is waiting for a connection to the IME. */
|
||||
int PHASE_SERVER_WAIT_IME = 7;
|
||||
int PHASE_SERVER_WAIT_IME = ImeProtoEnums.PHASE_SERVER_WAIT_IME;
|
||||
|
||||
/** The server has a connection to the IME. */
|
||||
int PHASE_SERVER_HAS_IME = 8;
|
||||
int PHASE_SERVER_HAS_IME = ImeProtoEnums.PHASE_SERVER_HAS_IME;
|
||||
|
||||
/** The server decided the IME should be hidden. */
|
||||
int PHASE_SERVER_SHOULD_HIDE = 9;
|
||||
int PHASE_SERVER_SHOULD_HIDE = ImeProtoEnums.PHASE_SERVER_SHOULD_HIDE;
|
||||
|
||||
/** Reached the IME wrapper. */
|
||||
int PHASE_IME_WRAPPER = 10;
|
||||
int PHASE_IME_WRAPPER = ImeProtoEnums.PHASE_IME_WRAPPER;
|
||||
|
||||
/** Dispatched from the IME wrapper to the IME. */
|
||||
int PHASE_IME_WRAPPER_DISPATCH = 11;
|
||||
int PHASE_IME_WRAPPER_DISPATCH = ImeProtoEnums.PHASE_IME_WRAPPER_DISPATCH;
|
||||
|
||||
/** Reached the IME' showSoftInput method. */
|
||||
int PHASE_IME_SHOW_SOFT_INPUT = 12;
|
||||
int PHASE_IME_SHOW_SOFT_INPUT = ImeProtoEnums.PHASE_IME_SHOW_SOFT_INPUT;
|
||||
|
||||
/** Reached the IME' hideSoftInput method. */
|
||||
int PHASE_IME_HIDE_SOFT_INPUT = 13;
|
||||
int PHASE_IME_HIDE_SOFT_INPUT = ImeProtoEnums.PHASE_IME_HIDE_SOFT_INPUT;
|
||||
|
||||
/** The server decided the IME should be shown. */
|
||||
int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = 14;
|
||||
int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = ImeProtoEnums.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE;
|
||||
|
||||
/** Requested applying the IME visibility in the insets source consumer. */
|
||||
int PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER = 15;
|
||||
int PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER =
|
||||
ImeProtoEnums.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER;
|
||||
|
||||
/** Applied the IME visibility. */
|
||||
int PHASE_SERVER_APPLY_IME_VISIBILITY = 16;
|
||||
int PHASE_SERVER_APPLY_IME_VISIBILITY = ImeProtoEnums.PHASE_SERVER_APPLY_IME_VISIBILITY;
|
||||
|
||||
/** Created the show IME runner. */
|
||||
int PHASE_WM_SHOW_IME_RUNNER = 17;
|
||||
int PHASE_WM_SHOW_IME_RUNNER = ImeProtoEnums.PHASE_WM_SHOW_IME_RUNNER;
|
||||
|
||||
/** Ready to show IME. */
|
||||
int PHASE_WM_SHOW_IME_READY = 18;
|
||||
int PHASE_WM_SHOW_IME_READY = ImeProtoEnums.PHASE_WM_SHOW_IME_READY;
|
||||
|
||||
/** The Window Manager has a connection to the IME insets control target. */
|
||||
int PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET = 19;
|
||||
int PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET =
|
||||
ImeProtoEnums.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET;
|
||||
|
||||
/** Reached the window insets control target's show insets method. */
|
||||
int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS = 20;
|
||||
int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS =
|
||||
ImeProtoEnums.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS;
|
||||
|
||||
/** Reached the window insets control target's hide insets method. */
|
||||
int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS = 21;
|
||||
int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS =
|
||||
ImeProtoEnums.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS;
|
||||
|
||||
/** Reached the remote insets control target's show insets method. */
|
||||
int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS = 22;
|
||||
int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS =
|
||||
ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS;
|
||||
|
||||
/** Reached the remote insets control target's hide insets method. */
|
||||
int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS = 23;
|
||||
int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS =
|
||||
ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS;
|
||||
|
||||
/** Reached the remote insets controller. */
|
||||
int PHASE_WM_REMOTE_INSETS_CONTROLLER = 24;
|
||||
int PHASE_WM_REMOTE_INSETS_CONTROLLER = ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROLLER;
|
||||
|
||||
/** Created the IME window insets show animation. */
|
||||
int PHASE_WM_ANIMATION_CREATE = 25;
|
||||
int PHASE_WM_ANIMATION_CREATE = ImeProtoEnums.PHASE_WM_ANIMATION_CREATE;
|
||||
|
||||
/** Started the IME window insets show animation. */
|
||||
int PHASE_WM_ANIMATION_RUNNING = 26;
|
||||
int PHASE_WM_ANIMATION_RUNNING = ImeProtoEnums.PHASE_WM_ANIMATION_RUNNING;
|
||||
|
||||
/** Reached the client's show insets method. */
|
||||
int PHASE_CLIENT_SHOW_INSETS = 27;
|
||||
int PHASE_CLIENT_SHOW_INSETS = ImeProtoEnums.PHASE_CLIENT_SHOW_INSETS;
|
||||
|
||||
/** Reached the client's hide insets method. */
|
||||
int PHASE_CLIENT_HIDE_INSETS = 28;
|
||||
int PHASE_CLIENT_HIDE_INSETS = ImeProtoEnums.PHASE_CLIENT_HIDE_INSETS;
|
||||
|
||||
/** Handling the IME window insets show request. */
|
||||
int PHASE_CLIENT_HANDLE_SHOW_INSETS = 29;
|
||||
int PHASE_CLIENT_HANDLE_SHOW_INSETS = ImeProtoEnums.PHASE_CLIENT_HANDLE_SHOW_INSETS;
|
||||
|
||||
/** Handling the IME window insets hide request. */
|
||||
int PHASE_CLIENT_HANDLE_HIDE_INSETS = 30;
|
||||
int PHASE_CLIENT_HANDLE_HIDE_INSETS = ImeProtoEnums.PHASE_CLIENT_HANDLE_HIDE_INSETS;
|
||||
|
||||
/** Applied the IME window insets show animation. */
|
||||
int PHASE_CLIENT_APPLY_ANIMATION = 31;
|
||||
int PHASE_CLIENT_APPLY_ANIMATION = ImeProtoEnums.PHASE_CLIENT_APPLY_ANIMATION;
|
||||
|
||||
/** Started the IME window insets show animation. */
|
||||
int PHASE_CLIENT_CONTROL_ANIMATION = 32;
|
||||
int PHASE_CLIENT_CONTROL_ANIMATION = ImeProtoEnums.PHASE_CLIENT_CONTROL_ANIMATION;
|
||||
|
||||
/** Checked that the IME is controllable. */
|
||||
int PHASE_CLIENT_DISABLED_USER_ANIMATION = ImeProtoEnums.PHASE_CLIENT_DISABLED_USER_ANIMATION;
|
||||
|
||||
/** Collecting insets source controls. */
|
||||
int PHASE_CLIENT_COLLECT_SOURCE_CONTROLS = ImeProtoEnums.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS;
|
||||
|
||||
/** Reached the insets source consumer's show request method. */
|
||||
int PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW =
|
||||
ImeProtoEnums.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW;
|
||||
|
||||
/** Reached input method manager's request IME show method. */
|
||||
int PHASE_CLIENT_REQUEST_IME_SHOW = ImeProtoEnums.PHASE_CLIENT_REQUEST_IME_SHOW;
|
||||
|
||||
/** Reached the insets source consumer's notify hidden method. */
|
||||
int PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN =
|
||||
ImeProtoEnums.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN;
|
||||
|
||||
/** Queued the IME window insets show animation. */
|
||||
int PHASE_CLIENT_ANIMATION_RUNNING = 33;
|
||||
int PHASE_CLIENT_ANIMATION_RUNNING = ImeProtoEnums.PHASE_CLIENT_ANIMATION_RUNNING;
|
||||
|
||||
/** Cancelled the IME window insets show animation. */
|
||||
int PHASE_CLIENT_ANIMATION_CANCEL = 34;
|
||||
int PHASE_CLIENT_ANIMATION_CANCEL = ImeProtoEnums.PHASE_CLIENT_ANIMATION_CANCEL;
|
||||
|
||||
/** Finished the IME window insets show animation. */
|
||||
int PHASE_CLIENT_ANIMATION_FINISHED_SHOW = 35;
|
||||
int PHASE_CLIENT_ANIMATION_FINISHED_SHOW = ImeProtoEnums.PHASE_CLIENT_ANIMATION_FINISHED_SHOW;
|
||||
|
||||
/** Finished the IME window insets hide animation. */
|
||||
int PHASE_CLIENT_ANIMATION_FINISHED_HIDE = 36;
|
||||
int PHASE_CLIENT_ANIMATION_FINISHED_HIDE = ImeProtoEnums.PHASE_CLIENT_ANIMATION_FINISHED_HIDE;
|
||||
|
||||
/**
|
||||
* Called when an IME show request is created.
|
||||
* Creates an IME show request tracking token.
|
||||
*
|
||||
* @param token the token tracking the current IME show request or {@code null} otherwise.
|
||||
* @param component the component name where the IME show request was created,
|
||||
* or {@code null} otherwise
|
||||
* (defaulting to {@link ActivityThread#currentProcessName()}).
|
||||
* @param uid the uid of the client that requested the IME.
|
||||
* @param origin the origin of the IME show request.
|
||||
* @param reason the reason why the IME show request was created.
|
||||
*
|
||||
* @return An IME tracking token.
|
||||
*/
|
||||
void onRequestShow(@Nullable Token token, @Origin int origin,
|
||||
@NonNull
|
||||
Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
|
||||
@SoftInputShowHideReason int reason);
|
||||
|
||||
/**
|
||||
* Called when an IME hide request is created.
|
||||
* Creates an IME hide request tracking token.
|
||||
*
|
||||
* @param token the token tracking the current IME hide request or {@code null} otherwise.
|
||||
* @param component the component name where the IME hide request was created,
|
||||
* or {@code null} otherwise
|
||||
* (defaulting to {@link ActivityThread#currentProcessName()}).
|
||||
* @param uid the uid of the client that requested the IME.
|
||||
* @param origin the origin of the IME hide request.
|
||||
* @param reason the reason why the IME hide request was created.
|
||||
*
|
||||
* @return An IME tracking token.
|
||||
*/
|
||||
void onRequestHide(@Nullable Token token, @Origin int origin,
|
||||
@NonNull
|
||||
Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
|
||||
@SoftInputShowHideReason int reason);
|
||||
|
||||
/**
|
||||
@@ -313,112 +385,122 @@ public interface ImeTracker {
|
||||
*/
|
||||
@NonNull
|
||||
static ImeTracker get() {
|
||||
return SystemProperties.getBoolean("persist.debug.imetracker", false)
|
||||
? LOGGER
|
||||
: NOOP_LOGGER;
|
||||
return LOGGER;
|
||||
}
|
||||
|
||||
/** The singleton IME tracker instance. */
|
||||
@NonNull
|
||||
ImeTracker LOGGER = new ImeTracker() {
|
||||
|
||||
{
|
||||
// Set logging flag initial value.
|
||||
mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false);
|
||||
// Update logging flag dynamically.
|
||||
SystemProperties.addChangeCallback(() ->
|
||||
mLogProgress =
|
||||
SystemProperties.getBoolean("persist.debug.imetracker", false));
|
||||
}
|
||||
|
||||
/** Whether progress should be logged. */
|
||||
private boolean mLogProgress;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public void onRequestShow(@Nullable Token token, int origin,
|
||||
public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
if (token == null) return;
|
||||
Log.i(TAG, token.mTag + ": onRequestShow at " + originToString(origin)
|
||||
IBinder binder = IInputMethodManagerGlobalInvoker.onRequestShow(uid, origin, reason);
|
||||
if (binder == null) binder = new Binder();
|
||||
|
||||
final Token token = Token.build(binder, component);
|
||||
|
||||
Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
|
||||
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public void onRequestHide(@Nullable Token token, int origin,
|
||||
public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
if (token == null) return;
|
||||
Log.i(TAG, token.mTag + ": onRequestHide at " + originToString(origin)
|
||||
IBinder binder = IInputMethodManagerGlobalInvoker.onRequestHide(uid, origin, reason);
|
||||
if (binder == null) binder = new Binder();
|
||||
|
||||
final Token token = Token.build(binder, component);
|
||||
|
||||
Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
|
||||
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(@Nullable Token token, int phase) {
|
||||
public void onProgress(@Nullable Token token, @Phase int phase) {
|
||||
if (token == null) return;
|
||||
Log.i(TAG, token.mTag + ": onProgress at " + phaseToString(phase));
|
||||
IInputMethodManagerGlobalInvoker.onProgress(token.mBinder, phase);
|
||||
|
||||
if (mLogProgress) {
|
||||
Log.i(TAG, token.mTag + ": onProgress at " + Debug.phaseToString(phase));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(@Nullable Token token, int phase) {
|
||||
public void onFailed(@Nullable Token token, @Phase int phase) {
|
||||
if (token == null) return;
|
||||
Log.i(TAG, token.mTag + ": onFailed at " + phaseToString(phase));
|
||||
IInputMethodManagerGlobalInvoker.onFailed(token.mBinder, phase);
|
||||
|
||||
Log.i(TAG, token.mTag + ": onFailed at " + Debug.phaseToString(phase));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTodo(@Nullable Token token, int phase) {
|
||||
public void onTodo(@Nullable Token token, @Phase int phase) {
|
||||
if (token == null) return;
|
||||
Log.i(TAG, token.mTag + ": onTodo at " + phaseToString(phase));
|
||||
Log.i(TAG, token.mTag + ": onTodo at " + Debug.phaseToString(phase));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@Nullable Token token, int phase) {
|
||||
public void onCancelled(@Nullable Token token, @Phase int phase) {
|
||||
if (token == null) return;
|
||||
Log.i(TAG, token.mTag + ": onCancelled at " + phaseToString(phase));
|
||||
IInputMethodManagerGlobalInvoker.onCancelled(token.mBinder, phase);
|
||||
|
||||
Log.i(TAG, token.mTag + ": onCancelled at " + Debug.phaseToString(phase));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShown(@Nullable Token token) {
|
||||
if (token == null) return;
|
||||
IInputMethodManagerGlobalInvoker.onShown(token.mBinder);
|
||||
|
||||
Log.i(TAG, token.mTag + ": onShown");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHidden(@Nullable Token token) {
|
||||
if (token == null) return;
|
||||
IInputMethodManagerGlobalInvoker.onHidden(token.mBinder);
|
||||
|
||||
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;
|
||||
@NonNull
|
||||
public final IBinder mBinder;
|
||||
|
||||
@NonNull
|
||||
private final String mTag;
|
||||
|
||||
public Token() {
|
||||
this(ActivityThread.currentProcessName());
|
||||
@NonNull
|
||||
private static Token build(@NonNull IBinder binder, @Nullable String component) {
|
||||
if (component == null) component = ActivityThread.currentProcessName();
|
||||
final String tag = component + ":" + Integer.toHexString((new Random().nextInt()));
|
||||
|
||||
return new Token(binder, tag);
|
||||
}
|
||||
|
||||
public Token(String component) {
|
||||
this(new Binder(), component + ":" + Integer.toHexString((new Random().nextInt())));
|
||||
}
|
||||
|
||||
private Token(IBinder binder, String tag) {
|
||||
private Token(@NonNull IBinder binder, @NonNull String tag) {
|
||||
mBinder = binder;
|
||||
mTag = tag;
|
||||
}
|
||||
@@ -443,10 +525,11 @@ public interface ImeTracker {
|
||||
|
||||
@NonNull
|
||||
public static final Creator<Token> CREATOR = new Creator<>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public Token createFromParcel(Parcel source) {
|
||||
IBinder binder = source.readStrongBinder();
|
||||
String tag = source.readString8();
|
||||
final IBinder binder = source.readStrongBinder();
|
||||
final String tag = source.readString8();
|
||||
return new Token(binder, tag);
|
||||
}
|
||||
|
||||
@@ -458,22 +541,34 @@ public interface ImeTracker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities for mapping phases and origins IntDef values to their names.
|
||||
* Utilities for mapping 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> sTypes =
|
||||
getFieldMapping(ImeTracker.class, "TYPE_");
|
||||
private static final Map<Integer, String> sStatus =
|
||||
getFieldMapping(ImeTracker.class, "STATUS_");
|
||||
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) {
|
||||
public static String typeToString(@Type int type) {
|
||||
return sTypes.getOrDefault(type, "TYPE_" + type);
|
||||
}
|
||||
|
||||
public static String statusToString(@Status int status) {
|
||||
return sStatus.getOrDefault(status, "STATUS_" + status);
|
||||
}
|
||||
|
||||
public static String originToString(@Origin int origin) {
|
||||
return sOrigins.getOrDefault(origin, "ORIGIN_" + origin);
|
||||
}
|
||||
|
||||
public static String phaseToString(int phase) {
|
||||
public static String phaseToString(@Phase int phase) {
|
||||
return sPhases.getOrDefault(phase, "PHASE_" + phase);
|
||||
}
|
||||
|
||||
|
||||
@@ -2002,14 +2002,16 @@ public final class InputMethodManager {
|
||||
* {@link #RESULT_HIDDEN}.
|
||||
*/
|
||||
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
|
||||
return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
|
||||
return showSoftInput(view, null /* statsToken */, flags, resultReceiver,
|
||||
SoftInputShowHideReason.SHOW_SOFT_INPUT);
|
||||
}
|
||||
|
||||
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);
|
||||
private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, int flags,
|
||||
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
|
||||
if (statsToken == null) {
|
||||
statsToken = ImeTracker.get().onRequestShow(null /* component */,
|
||||
Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason);
|
||||
}
|
||||
|
||||
ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this,
|
||||
null /* icProto */);
|
||||
@@ -2057,8 +2059,8 @@ 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,
|
||||
final ImeTracker.Token statsToken = ImeTracker.get().onRequestShow(null /* component */,
|
||||
Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
|
||||
SoftInputShowHideReason.SHOW_SOFT_INPUT);
|
||||
|
||||
Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be"
|
||||
@@ -2148,9 +2150,8 @@ 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);
|
||||
final ImeTracker.Token statsToken = ImeTracker.get().onRequestHide(null /* component */,
|
||||
Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT, reason);
|
||||
|
||||
ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow",
|
||||
this, null /* icProto */);
|
||||
@@ -2283,7 +2284,7 @@ public final class InputMethodManager {
|
||||
hideSoftInputFromWindow(view.getWindowToken(), hideFlags, null,
|
||||
SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT);
|
||||
} else {
|
||||
showSoftInput(view, showFlags, null,
|
||||
showSoftInput(view, null /* statsToken */, showFlags, null /* resultReceiver */,
|
||||
SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT);
|
||||
}
|
||||
}
|
||||
@@ -2793,8 +2794,8 @@ public final class InputMethodManager {
|
||||
|
||||
@UnsupportedAppUsage
|
||||
void closeCurrentInput() {
|
||||
final ImeTracker.Token statsToken = new ImeTracker.Token();
|
||||
ImeTracker.get().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
|
||||
final ImeTracker.Token statsToken = ImeTracker.get().onRequestHide(null /* component */,
|
||||
Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
|
||||
SoftInputShowHideReason.HIDE_SOFT_INPUT);
|
||||
|
||||
synchronized (mH) {
|
||||
@@ -2853,18 +2854,23 @@ public final class InputMethodManager {
|
||||
*
|
||||
* @param windowToken the window from which this request originates. If this doesn't match the
|
||||
* currently served view, the request is ignored and returns {@code false}.
|
||||
* @param statsToken the token tracking the current IME show request or {@code null} otherwise.
|
||||
*
|
||||
* @return {@code true} if IME can (eventually) be shown, {@code false} otherwise.
|
||||
* @hide
|
||||
*/
|
||||
public boolean requestImeShow(IBinder windowToken) {
|
||||
public boolean requestImeShow(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
|
||||
checkFocus();
|
||||
synchronized (mH) {
|
||||
final View servedView = getServedViewLocked();
|
||||
if (servedView == null || servedView.getWindowToken() != windowToken) {
|
||||
ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_CLIENT_REQUEST_IME_SHOW);
|
||||
return false;
|
||||
}
|
||||
showSoftInput(servedView, 0 /* flags */, null /* resultReceiver */,
|
||||
|
||||
ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_REQUEST_IME_SHOW);
|
||||
|
||||
showSoftInput(servedView, statsToken, 0 /* flags */, null /* resultReceiver */,
|
||||
SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API);
|
||||
return true;
|
||||
}
|
||||
@@ -2875,12 +2881,15 @@ public final class InputMethodManager {
|
||||
*
|
||||
* @param windowToken the window from which this request originates. If this doesn't match the
|
||||
* currently served view, the request is ignored.
|
||||
* @param statsToken the token tracking the current IME show request or {@code null} otherwise.
|
||||
* @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);
|
||||
public void notifyImeHidden(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
|
||||
if (statsToken == null) {
|
||||
statsToken = ImeTracker.get().onRequestHide(null /* component */,
|
||||
Process.myUid(), ImeTracker.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
|
||||
SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
|
||||
}
|
||||
|
||||
ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this,
|
||||
null /* icProto */);
|
||||
@@ -3545,6 +3554,18 @@ public final class InputMethodManager {
|
||||
return IInputMethodManagerGlobalInvoker.isInputMethodPickerShownForTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* A test API for CTS to check whether there are any pending IME visibility requests.
|
||||
*
|
||||
* @return {@code true} iff there are pending IME visibility requests.
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
@RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
|
||||
public boolean hasPendingImeVisibilityRequests() {
|
||||
return IInputMethodManagerGlobalInvoker.hasPendingImeVisibilityRequests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the settings for enabling subtypes of the specified input method.
|
||||
*
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.os.IBinder;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ImeProtoEnums;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -64,113 +65,114 @@ import java.lang.annotation.Retention;
|
||||
SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
|
||||
SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED,
|
||||
SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION,
|
||||
SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR})
|
||||
SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR
|
||||
})
|
||||
public @interface SoftInputShowHideReason {
|
||||
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
|
||||
int SHOW_SOFT_INPUT = 0;
|
||||
int SHOW_SOFT_INPUT = ImeProtoEnums.REASON_SHOW_SOFT_INPUT;
|
||||
|
||||
/** Show soft input when {@code InputMethodManagerService#attachNewInputLocked} called. */
|
||||
int ATTACH_NEW_INPUT = 1;
|
||||
int ATTACH_NEW_INPUT = ImeProtoEnums.REASON_ATTACH_NEW_INPUT;
|
||||
|
||||
/** Show soft input by {@code InputMethodManagerService#showMySoftInput}. This is triggered when
|
||||
* the IME process try to show the keyboard.
|
||||
*
|
||||
* @see android.inputmethodservice.InputMethodService#requestShowSelf(int)
|
||||
*/
|
||||
int SHOW_SOFT_INPUT_FROM_IME = 2;
|
||||
int SHOW_SOFT_INPUT_FROM_IME = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_FROM_IME;
|
||||
|
||||
/**
|
||||
* Hide soft input by
|
||||
* {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow}.
|
||||
*/
|
||||
int HIDE_SOFT_INPUT = 3;
|
||||
int HIDE_SOFT_INPUT = ImeProtoEnums.REASON_HIDE_SOFT_INPUT;
|
||||
|
||||
/**
|
||||
* Hide soft input by
|
||||
* {@link android.inputmethodservice.InputMethodService#requestHideSelf(int)}.
|
||||
*/
|
||||
int HIDE_SOFT_INPUT_FROM_IME = 4;
|
||||
int HIDE_SOFT_INPUT_FROM_IME = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_FROM_IME;
|
||||
|
||||
/**
|
||||
* Show soft input when navigated forward to the window (with
|
||||
* {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}} which the focused view is text
|
||||
* {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}) which the focused view is text
|
||||
* editor and system will auto-show the IME when the window can resize or running on a large
|
||||
* screen.
|
||||
*/
|
||||
int SHOW_AUTO_EDITOR_FORWARD_NAV = 5;
|
||||
int SHOW_AUTO_EDITOR_FORWARD_NAV = ImeProtoEnums.REASON_SHOW_AUTO_EDITOR_FORWARD_NAV;
|
||||
|
||||
/**
|
||||
* Show soft input when navigated forward to the window with
|
||||
* {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and
|
||||
* {@link LayoutParams#SOFT_INPUT_STATE_VISIBLE}.
|
||||
*/
|
||||
int SHOW_STATE_VISIBLE_FORWARD_NAV = 6;
|
||||
int SHOW_STATE_VISIBLE_FORWARD_NAV = ImeProtoEnums.REASON_SHOW_STATE_VISIBLE_FORWARD_NAV;
|
||||
|
||||
/**
|
||||
* Show soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE}.
|
||||
*/
|
||||
int SHOW_STATE_ALWAYS_VISIBLE = 7;
|
||||
int SHOW_STATE_ALWAYS_VISIBLE = ImeProtoEnums.REASON_SHOW_STATE_ALWAYS_VISIBLE;
|
||||
|
||||
/**
|
||||
* Show soft input during {@code InputMethodManagerService} receive changes from
|
||||
* {@code SettingsProvider}.
|
||||
*/
|
||||
int SHOW_SETTINGS_ON_CHANGE = 8;
|
||||
int SHOW_SETTINGS_ON_CHANGE = ImeProtoEnums.REASON_SHOW_SETTINGS_ON_CHANGE;
|
||||
|
||||
/** Hide soft input during switching user. */
|
||||
int HIDE_SWITCH_USER = 9;
|
||||
int HIDE_SWITCH_USER = ImeProtoEnums.REASON_HIDE_SWITCH_USER;
|
||||
|
||||
/** Hide soft input when the user is invalid. */
|
||||
int HIDE_INVALID_USER = 10;
|
||||
int HIDE_INVALID_USER = ImeProtoEnums.REASON_HIDE_INVALID_USER;
|
||||
|
||||
/**
|
||||
* Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_UNSPECIFIED} which
|
||||
* the focused view is not text editor.
|
||||
*/
|
||||
int HIDE_UNSPECIFIED_WINDOW = 11;
|
||||
int HIDE_UNSPECIFIED_WINDOW = ImeProtoEnums.REASON_HIDE_UNSPECIFIED_WINDOW;
|
||||
|
||||
/**
|
||||
* Hide soft input when navigated forward to the window with
|
||||
* {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and
|
||||
* {@link LayoutParams#SOFT_INPUT_STATE_HIDDEN}.
|
||||
*/
|
||||
int HIDE_STATE_HIDDEN_FORWARD_NAV = 12;
|
||||
int HIDE_STATE_HIDDEN_FORWARD_NAV = ImeProtoEnums.REASON_HIDE_STATE_HIDDEN_FORWARD_NAV;
|
||||
|
||||
/**
|
||||
* Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}.
|
||||
*/
|
||||
int HIDE_ALWAYS_HIDDEN_STATE = 13;
|
||||
int HIDE_ALWAYS_HIDDEN_STATE = ImeProtoEnums.REASON_HIDE_ALWAYS_HIDDEN_STATE;
|
||||
|
||||
/** Hide soft input when "adb shell ime <command>" called. */
|
||||
int HIDE_RESET_SHELL_COMMAND = 14;
|
||||
int HIDE_RESET_SHELL_COMMAND = ImeProtoEnums.REASON_HIDE_RESET_SHELL_COMMAND;
|
||||
|
||||
/**
|
||||
* Hide soft input during {@code InputMethodManagerService} receive changes from
|
||||
* {@code SettingsProvider}.
|
||||
*/
|
||||
int HIDE_SETTINGS_ON_CHANGE = 15;
|
||||
int HIDE_SETTINGS_ON_CHANGE = ImeProtoEnums.REASON_HIDE_SETTINGS_ON_CHANGE;
|
||||
|
||||
/**
|
||||
* Hide soft input from {@link com.android.server.policy.PhoneWindowManager} when setting
|
||||
* {@link com.android.internal.R.integer#config_shortPressOnPowerBehavior} in config.xml as
|
||||
* dismiss IME.
|
||||
*/
|
||||
int HIDE_POWER_BUTTON_GO_HOME = 16;
|
||||
int HIDE_POWER_BUTTON_GO_HOME = ImeProtoEnums.REASON_HIDE_POWER_BUTTON_GO_HOME;
|
||||
|
||||
/** Hide soft input when attaching docked stack. */
|
||||
int HIDE_DOCKED_STACK_ATTACHED = 17;
|
||||
int HIDE_DOCKED_STACK_ATTACHED = ImeProtoEnums.REASON_HIDE_DOCKED_STACK_ATTACHED;
|
||||
|
||||
/**
|
||||
* Hide soft input when {@link com.android.server.wm.RecentsAnimationController} starts
|
||||
* intercept touch from app window.
|
||||
*/
|
||||
int HIDE_RECENTS_ANIMATION = 18;
|
||||
int HIDE_RECENTS_ANIMATION = ImeProtoEnums.REASON_HIDE_RECENTS_ANIMATION;
|
||||
|
||||
/**
|
||||
* Hide soft input when {@link com.android.wm.shell.bubbles.BubbleController} is expanding,
|
||||
* switching, or collapsing Bubbles.
|
||||
*/
|
||||
int HIDE_BUBBLES = 19;
|
||||
int HIDE_BUBBLES = ImeProtoEnums.REASON_HIDE_BUBBLES;
|
||||
|
||||
/**
|
||||
* Hide soft input when focusing the same window (e.g. screen turned-off and turn-on) which no
|
||||
@@ -183,74 +185,78 @@ public @interface SoftInputShowHideReason {
|
||||
* only the dialog focused as it's the latest window with input focus) makes we need to hide
|
||||
* soft-input when the same window focused again to align with the same behavior prior to R.
|
||||
*/
|
||||
int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20;
|
||||
int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR =
|
||||
ImeProtoEnums.REASON_HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR;
|
||||
|
||||
/**
|
||||
* Hide soft input when a {@link com.android.internal.inputmethod.IInputMethodClient} is
|
||||
* removed.
|
||||
*/
|
||||
int HIDE_REMOVE_CLIENT = 21;
|
||||
int HIDE_REMOVE_CLIENT = ImeProtoEnums.REASON_HIDE_REMOVE_CLIENT;
|
||||
|
||||
/**
|
||||
* Show soft input when the system invoking
|
||||
* {@link com.android.server.wm.WindowManagerInternal#shouldRestoreImeVisibility}.
|
||||
*/
|
||||
int SHOW_RESTORE_IME_VISIBILITY = 22;
|
||||
int SHOW_RESTORE_IME_VISIBILITY = ImeProtoEnums.REASON_SHOW_RESTORE_IME_VISIBILITY;
|
||||
|
||||
/**
|
||||
* Show soft input by
|
||||
* {@link android.view.inputmethod.InputMethodManager#toggleSoftInput(int, int)};
|
||||
*/
|
||||
int SHOW_TOGGLE_SOFT_INPUT = 23;
|
||||
int SHOW_TOGGLE_SOFT_INPUT = ImeProtoEnums.REASON_SHOW_TOGGLE_SOFT_INPUT;
|
||||
|
||||
/**
|
||||
* Hide soft input by
|
||||
* {@link android.view.inputmethod.InputMethodManager#toggleSoftInput(int, int)};
|
||||
*/
|
||||
int HIDE_TOGGLE_SOFT_INPUT = 24;
|
||||
int HIDE_TOGGLE_SOFT_INPUT = ImeProtoEnums.REASON_HIDE_TOGGLE_SOFT_INPUT;
|
||||
|
||||
/**
|
||||
* Show soft input by
|
||||
* {@link android.view.InsetsController#show(int)};
|
||||
*/
|
||||
int SHOW_SOFT_INPUT_BY_INSETS_API = 25;
|
||||
int SHOW_SOFT_INPUT_BY_INSETS_API = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_BY_INSETS_API;
|
||||
|
||||
/**
|
||||
* Hide soft input if Ime policy has been set to {@link WindowManager#DISPLAY_IME_POLICY_HIDE}.
|
||||
* See also {@code InputMethodManagerService#mImeHiddenByDisplayPolicy}.
|
||||
*/
|
||||
int HIDE_DISPLAY_IME_POLICY_HIDE = 26;
|
||||
int HIDE_DISPLAY_IME_POLICY_HIDE = ImeProtoEnums.REASON_HIDE_DISPLAY_IME_POLICY_HIDE;
|
||||
|
||||
/**
|
||||
* Hide soft input by {@link android.view.InsetsController#hide(int)}.
|
||||
*/
|
||||
int HIDE_SOFT_INPUT_BY_INSETS_API = 27;
|
||||
int HIDE_SOFT_INPUT_BY_INSETS_API = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_BY_INSETS_API;
|
||||
|
||||
/**
|
||||
* Hide soft input by {@link android.inputmethodservice.InputMethodService#handleBack(boolean)}.
|
||||
*/
|
||||
int HIDE_SOFT_INPUT_BY_BACK_KEY = 28;
|
||||
int HIDE_SOFT_INPUT_BY_BACK_KEY = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_BY_BACK_KEY;
|
||||
|
||||
/**
|
||||
* Hide soft input by
|
||||
* {@link android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int)}.
|
||||
*/
|
||||
int HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT = 29;
|
||||
int HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT =
|
||||
ImeProtoEnums.REASON_HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT;
|
||||
|
||||
/**
|
||||
* Hide soft input by
|
||||
* {@link android.inputmethodservice.InputMethodService#onExtractingInputChanged(EditorInfo)})}.
|
||||
*/
|
||||
int HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED = 30;
|
||||
int HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED =
|
||||
ImeProtoEnums.REASON_HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED;
|
||||
|
||||
/**
|
||||
* Hide soft input by the deprecated
|
||||
* {@link InputMethodManager#hideSoftInputFromInputMethod(IBinder, int)}.
|
||||
*/
|
||||
int HIDE_SOFT_INPUT_IMM_DEPRECATION = 31;
|
||||
int HIDE_SOFT_INPUT_IMM_DEPRECATION = ImeProtoEnums.REASON_HIDE_SOFT_INPUT_IMM_DEPRECATION;
|
||||
|
||||
/**
|
||||
* Hide soft input when the window gained focus without an editor from the IME shown window.
|
||||
*/
|
||||
int HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR = 32;
|
||||
int HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR =
|
||||
ImeProtoEnums.REASON_HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR;
|
||||
}
|
||||
|
||||
94
core/java/com/android/internal/view/IImeTracker.aidl
Normal file
94
core/java/com/android/internal/view/IImeTracker.aidl
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 com.android.internal.view;
|
||||
|
||||
import android.view.inputmethod.ImeTracker;
|
||||
|
||||
/**
|
||||
* Interface to the global Ime tracker, used by all client applications.
|
||||
* {@hide}
|
||||
*/
|
||||
interface IImeTracker {
|
||||
|
||||
/**
|
||||
* Called when an IME show request is created,
|
||||
* returns a new Binder to be associated with the IME tracking token.
|
||||
*
|
||||
* @param uid the uid of the client that requested the IME.
|
||||
* @param origin the origin of the IME show request.
|
||||
* @param reason the reason why the IME show request was created.
|
||||
*/
|
||||
IBinder onRequestShow(int uid, int origin, int reason);
|
||||
|
||||
/**
|
||||
* Called when an IME hide request is created,
|
||||
* returns a new Binder to be associated with the IME tracking token.
|
||||
*
|
||||
* @param uid the uid of the client that requested the IME.
|
||||
* @param origin the origin of the IME hide request.
|
||||
* @param reason the reason why the IME hide request was created.
|
||||
*/
|
||||
IBinder onRequestHide(int uid, int origin, int reason);
|
||||
|
||||
/**
|
||||
* Called when the IME request progresses to a further phase.
|
||||
*
|
||||
* @param statsToken the token tracking the current IME request.
|
||||
* @param phase the new phase the IME request reached.
|
||||
*/
|
||||
oneway void onProgress(in IBinder statsToken, int phase);
|
||||
|
||||
/**
|
||||
* Called when the IME request fails.
|
||||
*
|
||||
* @param statsToken the token tracking the current IME request.
|
||||
* @param phase the phase the IME request failed at.
|
||||
*/
|
||||
oneway void onFailed(in IBinder statsToken, int phase);
|
||||
|
||||
/**
|
||||
* Called when the IME request is cancelled.
|
||||
*
|
||||
* @param statsToken the token tracking the current IME request.
|
||||
* @param phase the phase the IME request was cancelled at.
|
||||
*/
|
||||
oneway void onCancelled(in IBinder statsToken, int phase);
|
||||
|
||||
/**
|
||||
* Called when the IME show request is successful.
|
||||
*
|
||||
* @param statsToken the token tracking the current IME request.
|
||||
*/
|
||||
oneway void onShown(in IBinder statsToken);
|
||||
|
||||
/**
|
||||
* Called when the IME hide request is successful.
|
||||
*
|
||||
* @param statsToken the token tracking the current IME request.
|
||||
*/
|
||||
oneway void onHidden(in IBinder statsToken);
|
||||
|
||||
/**
|
||||
* Checks whether there are any pending IME visibility requests.
|
||||
*
|
||||
* @return {@code true} iff there are pending IME visibility requests.
|
||||
*/
|
||||
@EnforcePermission("TEST_INPUT_METHOD")
|
||||
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
|
||||
+ "android.Manifest.permission.TEST_INPUT_METHOD)")
|
||||
boolean hasPendingImeVisibilityRequests();
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import com.android.internal.inputmethod.IInputMethodClient;
|
||||
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
|
||||
import com.android.internal.inputmethod.IRemoteInputConnection;
|
||||
import com.android.internal.inputmethod.InputBindResult;
|
||||
import com.android.internal.view.IImeTracker;
|
||||
|
||||
/**
|
||||
* Public interface to the global input method manager, used by all client
|
||||
@@ -158,4 +159,10 @@ interface IInputMethodManager {
|
||||
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
|
||||
+ "android.Manifest.permission.TEST_INPUT_METHOD)")
|
||||
void setStylusWindowIdleTimeoutForTest(in IInputMethodClient client, long timeout);
|
||||
|
||||
/**
|
||||
* Returns the singleton instance for the Ime Tracker Service.
|
||||
* {@hide}
|
||||
*/
|
||||
IImeTracker getImeTrackerService();
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
|
||||
import android.view.WindowManager.BadTokenException;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.inputmethod.ImeTracker;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -136,7 +137,8 @@ public class InsetsControllerTest {
|
||||
private boolean mImeRequestedShow;
|
||||
|
||||
@Override
|
||||
public int requestShow(boolean fromController) {
|
||||
public int requestShow(boolean fromController,
|
||||
ImeTracker.Token statsToken) {
|
||||
if (fromController || mImeRequestedShow) {
|
||||
mImeRequestedShow = true;
|
||||
return SHOW_IMMEDIATELY;
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.platform.test.annotations.Presubmit;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.WindowManager.BadTokenException;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.inputmethod.ImeTracker;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -221,7 +222,8 @@ public class InsetsSourceConsumerTest {
|
||||
return new InsetsSourceConsumer(ITYPE_IME, ime(), state,
|
||||
() -> mMockTransaction, controller) {
|
||||
@Override
|
||||
public int requestShow(boolean fromController) {
|
||||
public int requestShow(boolean fromController,
|
||||
ImeTracker.Token statsToken) {
|
||||
return SHOW_IMMEDIATELY;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* 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 com.android.server.inputmethod;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.EnforcePermission;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.view.inputmethod.ImeTracker;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.inputmethod.InputMethodDebug;
|
||||
import com.android.internal.inputmethod.SoftInputShowHideReason;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.internal.view.IImeTracker;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Locale;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Service for managing and logging {@link ImeTracker.Token} instances.
|
||||
*
|
||||
* @implNote Suppresses {@link GuardedBy} warnings, as linter reports that {@link #mHistory}
|
||||
* interactions are guarded by {@code this} instead of {@code ImeTrackerService.this}, which should
|
||||
* be identical.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SuppressWarnings("GuardedBy")
|
||||
public final class ImeTrackerService extends IImeTracker.Stub {
|
||||
|
||||
static final String TAG = "ImeTrackerService";
|
||||
|
||||
/** The threshold in milliseconds after which a history entry is considered timed out. */
|
||||
private static final long TIMEOUT_MS = 10_000;
|
||||
|
||||
/** Handler for registering timeouts for live entries. */
|
||||
private final Handler mHandler =
|
||||
new Handler(Looper.myLooper(), null /* callback */, true /* async */);
|
||||
|
||||
/** Singleton instance of the History. */
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private final History mHistory = new History();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public synchronized IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
final IBinder binder = new Binder();
|
||||
final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_SHOW,
|
||||
ImeTracker.STATUS_RUN, origin, reason);
|
||||
mHistory.addEntry(binder, entry);
|
||||
|
||||
// Register a delayed task to handle the case where the new entry times out.
|
||||
mHandler.postDelayed(() -> {
|
||||
synchronized (ImeTrackerService.this) {
|
||||
mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
|
||||
}
|
||||
}, TIMEOUT_MS);
|
||||
|
||||
return binder;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public synchronized IBinder onRequestHide(int uid, @ImeTracker.Origin int origin,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
final IBinder binder = new Binder();
|
||||
final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_HIDE,
|
||||
ImeTracker.STATUS_RUN, origin, reason);
|
||||
mHistory.addEntry(binder, entry);
|
||||
|
||||
// Register a delayed task to handle the case where the new entry times out.
|
||||
mHandler.postDelayed(() -> {
|
||||
synchronized (ImeTrackerService.this) {
|
||||
mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
|
||||
}
|
||||
}, TIMEOUT_MS);
|
||||
|
||||
return binder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
|
||||
final History.Entry entry = mHistory.getEntry(statsToken);
|
||||
if (entry == null) return;
|
||||
|
||||
entry.mPhase = phase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
|
||||
mHistory.setFinished(statsToken, ImeTracker.STATUS_FAIL, phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
|
||||
mHistory.setFinished(statsToken, ImeTracker.STATUS_CANCEL, phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onShown(@NonNull IBinder statsToken) {
|
||||
mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onHidden(@NonNull IBinder statsToken) {
|
||||
mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the IME request tracking token with new information available in IMMS.
|
||||
*
|
||||
* @param statsToken the token corresponding to the current IME request.
|
||||
* @param requestWindowName the name of the window that created the IME request.
|
||||
*/
|
||||
public synchronized void onImmsUpdate(@NonNull IBinder statsToken,
|
||||
@NonNull String requestWindowName) {
|
||||
final History.Entry entry = mHistory.getEntry(statsToken);
|
||||
if (entry == null) return;
|
||||
|
||||
entry.mRequestWindowName = requestWindowName;
|
||||
}
|
||||
|
||||
/** Dumps the contents of the history. */
|
||||
public synchronized void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
|
||||
mHistory.dump(pw, prefix);
|
||||
}
|
||||
|
||||
@EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
|
||||
@Override
|
||||
public synchronized boolean hasPendingImeVisibilityRequests() {
|
||||
super.hasPendingImeVisibilityRequests_enforcePermission();
|
||||
|
||||
return !mHistory.mLiveEntries.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* A circular buffer storing the most recent few {@link ImeTracker.Token} entries information.
|
||||
*/
|
||||
private static final class History {
|
||||
|
||||
/** The circular buffer's capacity. */
|
||||
private static final int CAPACITY = 100;
|
||||
|
||||
/** Backing store for the circular buffer. */
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private final ArrayDeque<Entry> mEntries = new ArrayDeque<>(CAPACITY);
|
||||
|
||||
/** Backing store for the live entries (i.e. entries that are not finished yet). */
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private final WeakHashMap<IBinder, Entry> mLiveEntries = new WeakHashMap<>();
|
||||
|
||||
/** Latest entry sequence number. */
|
||||
private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
|
||||
|
||||
/** Adds a live entry. */
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private void addEntry(@NonNull IBinder statsToken, @NonNull Entry entry) {
|
||||
mLiveEntries.put(statsToken, entry);
|
||||
}
|
||||
|
||||
/** Gets the entry corresponding to the given IME tracking token, if it exists. */
|
||||
@Nullable
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private Entry getEntry(@NonNull IBinder statsToken) {
|
||||
return mLiveEntries.get(statsToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the live entry corresponding to the tracking token, if it exists, as finished,
|
||||
* and uploads the data for metrics.
|
||||
*
|
||||
* @param statsToken the token corresponding to the current IME request.
|
||||
* @param status the finish status of the IME request.
|
||||
* @param phase the phase the IME request finished at, if it exists
|
||||
* (or {@link ImeTracker#PHASE_NOT_SET} otherwise).
|
||||
*/
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private void setFinished(@NonNull IBinder statsToken, @ImeTracker.Status int status,
|
||||
@ImeTracker.Phase int phase) {
|
||||
final Entry entry = mLiveEntries.remove(statsToken);
|
||||
if (entry == null) return;
|
||||
|
||||
entry.mDuration = System.currentTimeMillis() - entry.mStartTime;
|
||||
entry.mStatus = status;
|
||||
|
||||
if (phase != ImeTracker.PHASE_NOT_SET) {
|
||||
entry.mPhase = phase;
|
||||
}
|
||||
|
||||
// Remove excess entries overflowing capacity (plus one for the new entry).
|
||||
while (mEntries.size() >= CAPACITY) {
|
||||
mEntries.remove();
|
||||
}
|
||||
|
||||
mEntries.offer(entry);
|
||||
|
||||
// Log newly finished entry.
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.IME_REQUEST_FINISHED, entry.mUid,
|
||||
entry.mDuration, entry.mType, entry.mStatus, entry.mReason,
|
||||
entry.mOrigin, entry.mPhase);
|
||||
}
|
||||
|
||||
/** Dumps the contents of the circular buffer. */
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
|
||||
final DateTimeFormatter formatter =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
|
||||
.withZone(ZoneId.systemDefault());
|
||||
|
||||
pw.print(prefix);
|
||||
pw.println("ImeTrackerService#History.mLiveEntries:");
|
||||
|
||||
for (final Entry entry: mLiveEntries.values()) {
|
||||
dumpEntry(entry, pw, prefix, formatter);
|
||||
}
|
||||
|
||||
pw.print(prefix);
|
||||
pw.println("ImeTrackerService#History.mEntries:");
|
||||
|
||||
for (final Entry entry: mEntries) {
|
||||
dumpEntry(entry, pw, prefix, formatter);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("ImeTrackerService.this")
|
||||
private void dumpEntry(@NonNull Entry entry, @NonNull PrintWriter pw,
|
||||
@NonNull String prefix, @NonNull DateTimeFormatter formatter) {
|
||||
pw.print(prefix);
|
||||
pw.println("ImeTrackerService#History #" + entry.mSequenceNumber + ":");
|
||||
|
||||
pw.print(prefix);
|
||||
pw.println(" startTime=" + formatter.format(Instant.ofEpochMilli(entry.mStartTime)));
|
||||
|
||||
pw.print(prefix);
|
||||
pw.println(" duration=" + entry.mDuration + "ms");
|
||||
|
||||
pw.print(prefix);
|
||||
pw.print(" type=" + ImeTracker.Debug.typeToString(entry.mType));
|
||||
|
||||
pw.print(prefix);
|
||||
pw.print(" status=" + ImeTracker.Debug.statusToString(entry.mStatus));
|
||||
|
||||
pw.print(prefix);
|
||||
pw.print(" origin="
|
||||
+ ImeTracker.Debug.originToString(entry.mOrigin));
|
||||
|
||||
pw.print(prefix);
|
||||
pw.print(" reason="
|
||||
+ InputMethodDebug.softInputDisplayReasonToString(entry.mReason));
|
||||
|
||||
pw.print(prefix);
|
||||
pw.print(" phase="
|
||||
+ ImeTracker.Debug.phaseToString(entry.mPhase));
|
||||
|
||||
pw.print(prefix);
|
||||
pw.print(" requestWindowName=" + entry.mRequestWindowName);
|
||||
}
|
||||
|
||||
/** A history entry. */
|
||||
private static final class Entry {
|
||||
|
||||
/** The entry's sequence number in the history. */
|
||||
private final int mSequenceNumber = sSequenceNumber.getAndIncrement();
|
||||
|
||||
/** Uid of the client that requested the IME. */
|
||||
private final int mUid;
|
||||
|
||||
/** Clock time in milliseconds when the IME request was created. */
|
||||
private final long mStartTime = System.currentTimeMillis();
|
||||
|
||||
/** Duration in milliseconds of the IME request from start to end. */
|
||||
private long mDuration = 0;
|
||||
|
||||
/** Type of the IME request. */
|
||||
@ImeTracker.Type
|
||||
private final int mType;
|
||||
|
||||
/** Status of the IME request. */
|
||||
@ImeTracker.Status
|
||||
private int mStatus;
|
||||
|
||||
/** Origin of the IME request. */
|
||||
@ImeTracker.Origin
|
||||
private final int mOrigin;
|
||||
|
||||
/** Reason for creating the IME request. */
|
||||
@SoftInputShowHideReason
|
||||
private final int mReason;
|
||||
|
||||
/** Latest phase of the IME request. */
|
||||
@ImeTracker.Phase
|
||||
private int mPhase = ImeTracker.PHASE_NOT_SET;
|
||||
|
||||
/**
|
||||
* Name of the window that created the IME request.
|
||||
*
|
||||
* Note: This is later set through {@link #onImmsUpdate(IBinder, String)}.
|
||||
*/
|
||||
@NonNull
|
||||
private String mRequestWindowName = "not set";
|
||||
|
||||
private Entry(int uid, @ImeTracker.Type int type, @ImeTracker.Status int status,
|
||||
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
|
||||
mUid = uid;
|
||||
mType = type;
|
||||
mStatus = status;
|
||||
mOrigin = origin;
|
||||
mReason = reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,6 +176,7 @@ import com.android.internal.os.TransferPipe;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.ConcurrentUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.view.IImeTracker;
|
||||
import com.android.internal.view.IInputMethodManager;
|
||||
import com.android.server.AccessibilityManagerInternal;
|
||||
import com.android.server.EventLogTags;
|
||||
@@ -197,11 +198,12 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
@@ -916,8 +918,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
}
|
||||
|
||||
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
|
||||
final SimpleDateFormat dataFormat =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
|
||||
final DateTimeFormatter formatter =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
|
||||
.withZone(ZoneId.systemDefault());
|
||||
|
||||
for (int i = 0; i < mEntries.length; ++i) {
|
||||
final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
|
||||
@@ -928,7 +931,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
pw.println("SoftInputShowHideHistory #" + entry.mSequenceNumber + ":");
|
||||
|
||||
pw.print(prefix);
|
||||
pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
|
||||
pw.println(" time=" + formatter.format(Instant.ofEpochMilli(entry.mWallTime))
|
||||
+ " (timestamp=" + entry.mTimestamp + ")");
|
||||
|
||||
pw.print(prefix);
|
||||
@@ -996,7 +999,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
private static final int ENTRY_SIZE_FOR_HIGH_RAM_DEVICE = 32;
|
||||
|
||||
/**
|
||||
* Entry size for non low-RAM devices.
|
||||
* Entry size for low-RAM devices.
|
||||
*
|
||||
* <p>TODO: Consider to follow what other system services have been doing to manage
|
||||
* constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p>
|
||||
@@ -1012,7 +1015,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
}
|
||||
|
||||
/**
|
||||
* Backing store for the ring bugger.
|
||||
* Backing store for the ring buffer.
|
||||
*/
|
||||
private final Entry[] mEntries = new Entry[getEntrySize()];
|
||||
|
||||
@@ -1092,8 +1095,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
}
|
||||
|
||||
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
|
||||
final SimpleDateFormat dataFormat =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
|
||||
final DateTimeFormatter formatter =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
|
||||
.withZone(ZoneId.systemDefault());
|
||||
|
||||
for (int i = 0; i < mEntries.length; ++i) {
|
||||
final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
|
||||
@@ -1104,7 +1108,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
pw.println("StartInput #" + entry.mSequenceNumber + ":");
|
||||
|
||||
pw.print(prefix);
|
||||
pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
|
||||
pw.println(" time=" + formatter.format(Instant.ofEpochMilli(entry.mWallTime))
|
||||
+ " (timestamp=" + entry.mTimestamp + ")"
|
||||
+ " reason="
|
||||
+ InputMethodDebug.startInputReasonToString(entry.mStartInputReason)
|
||||
@@ -1146,6 +1150,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
private final SoftInputShowHideHistory mSoftInputShowHideHistory =
|
||||
new SoftInputShowHideHistory();
|
||||
|
||||
@NonNull
|
||||
private final ImeTrackerService mImeTrackerService = new ImeTrackerService();
|
||||
|
||||
class SettingsObserver extends ContentObserver {
|
||||
int mUserId;
|
||||
boolean mRegistered = false;
|
||||
@@ -3392,13 +3399,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
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);
|
||||
// TODO(b/261565259): to avoid using null, add package name in ClientState
|
||||
final String packageName = (mCurEditorInfo != null) ? mCurEditorInfo.packageName : null;
|
||||
final int uid = mCurClient != null ? mCurClient.mUid : -1;
|
||||
statsToken = ImeTracker.get().onRequestShow(packageName, uid,
|
||||
ImeTracker.ORIGIN_SERVER_START_INPUT, reason);
|
||||
}
|
||||
|
||||
mShowRequested = true;
|
||||
@@ -3448,7 +3453,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
InputMethodDebug.softInputDisplayReasonToString(reason),
|
||||
InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode));
|
||||
}
|
||||
onShowHideSoftInputRequested(true /* show */, windowToken, reason);
|
||||
onShowHideSoftInputRequested(true /* show */, windowToken, reason, statsToken);
|
||||
}
|
||||
mInputShown = true;
|
||||
return true;
|
||||
@@ -3495,12 +3500,18 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
|
||||
// Create statsToken is none exists.
|
||||
if (statsToken == null) {
|
||||
String packageName = null;
|
||||
if (mCurEditorInfo != null) {
|
||||
packageName = mCurEditorInfo.packageName;
|
||||
// TODO(b/261565259): to avoid using null, add package name in ClientState
|
||||
final String packageName = (mCurEditorInfo != null) ? mCurEditorInfo.packageName : null;
|
||||
final int uid;
|
||||
if (mCurClient != null) {
|
||||
uid = mCurClient.mUid;
|
||||
} else if (mCurFocusedWindowClient != null) {
|
||||
uid = mCurFocusedWindowClient.mUid;
|
||||
} else {
|
||||
uid = -1;
|
||||
}
|
||||
statsToken = new ImeTracker.Token(packageName);
|
||||
ImeTracker.get().onRequestHide(statsToken, ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason);
|
||||
statsToken = ImeTracker.get().onRequestHide(packageName, uid,
|
||||
ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason);
|
||||
}
|
||||
|
||||
if ((flags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
|
||||
@@ -3552,7 +3563,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
InputMethodDebug.softInputDisplayReasonToString(reason),
|
||||
InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode));
|
||||
}
|
||||
onShowHideSoftInputRequested(false /* show */, windowToken, reason);
|
||||
onShowHideSoftInputRequested(false /* show */, windowToken, reason, statsToken);
|
||||
}
|
||||
res = true;
|
||||
} else {
|
||||
@@ -4768,6 +4779,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
|
||||
synchronized (ImfLock.class) {
|
||||
if (!calledWithValidTokenLocked(token)) {
|
||||
ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
|
||||
return;
|
||||
}
|
||||
if (!setVisible) {
|
||||
@@ -4833,7 +4845,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
/** Called right after {@link com.android.internal.inputmethod.IInputMethod#showSoftInput}. */
|
||||
@GuardedBy("ImfLock.class")
|
||||
private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
@SoftInputShowHideReason int reason, @Nullable ImeTracker.Token statsToken) {
|
||||
final WindowManagerInternal.ImeTargetInfo info =
|
||||
mWindowManagerInternal.onToggleImeRequested(
|
||||
show, mCurFocusedWindow, requestToken, mCurTokenDisplayId);
|
||||
@@ -4842,6 +4854,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
|
||||
info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
|
||||
info.imeSurfaceParentName));
|
||||
|
||||
mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName);
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@@ -5981,6 +5995,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
|
||||
p.println(" mSoftInputShowHideHistory:");
|
||||
mSoftInputShowHideHistory.dump(pw, " ");
|
||||
|
||||
p.println(" mImeTrackerService#History:");
|
||||
mImeTrackerService.dump(pw, " ");
|
||||
}
|
||||
|
||||
// Exit here for critical dump, as remaining sections require IPCs to other processes.
|
||||
@@ -6571,6 +6588,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public IImeTracker getImeTrackerService() {
|
||||
return mImeTrackerService;
|
||||
}
|
||||
|
||||
private static final class InputMethodPrivilegedOperationsImpl
|
||||
extends IInputMethodPrivilegedOperations.Stub {
|
||||
private final InputMethodManagerService mImms;
|
||||
|
||||
Reference in New Issue
Block a user