Merge changes I59db19c5,I49ceeefd,I3e3bc097 into udc-dev

* changes:
  Move IImeTracker from view to inputmethod
  Fix ImeTrackerService dump formatting
  Add logs for ImeTrackerService#History#setFinished
This commit is contained in:
Cosmin Băieș
2023-02-24 15:34:11 +00:00
committed by Android (Google) Code Review
6 changed files with 193 additions and 142 deletions

View File

@@ -25,6 +25,7 @@ import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission; import android.annotation.RequiresPermission;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.content.Context; import android.content.Context;
import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ResultReceiver; import android.os.ResultReceiver;
@@ -33,6 +34,7 @@ import android.view.WindowManager;
import android.window.ImeOnBackInvokedDispatcher; import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.DirectBootAwareness; import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient; import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection; import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -40,7 +42,6 @@ import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.StartInputReason;
import com.android.internal.view.IImeTracker;
import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodManager;
import java.util.ArrayList; import java.util.ArrayList;
@@ -581,51 +582,57 @@ final class IInputMethodManagerGlobalInvoker {
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#onRequestShow */
@AnyThread @AnyThread
@Nullable @NonNull
static IBinder onRequestShow(int uid, @ImeTracker.Origin int origin, static ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
@SoftInputShowHideReason int reason) { @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final IImeTracker service = getImeTrackerService(); final IImeTracker service = getImeTrackerService();
if (service == null) { if (service == null) {
return null; // Create token with "fake" binder if the service was not found.
return new ImeTracker.Token(new Binder(), tag);
} }
try { try {
return service.onRequestShow(uid, origin, reason); return service.onRequestShow(tag, uid, origin, reason);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#onRequestHide */
@AnyThread @AnyThread
@Nullable @NonNull
static IBinder onRequestHide(int uid, @ImeTracker.Origin int origin, static ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
@SoftInputShowHideReason int reason) { @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final IImeTracker service = getImeTrackerService(); final IImeTracker service = getImeTrackerService();
if (service == null) { if (service == null) {
return null; // Create token with "fake" binder if the service was not found.
return new ImeTracker.Token(new Binder(), tag);
} }
try { try {
return service.onRequestHide(uid, origin, reason); return service.onRequestHide(tag, uid, origin, reason);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#onProgress */
@AnyThread @AnyThread
static void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) { static void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
final IImeTracker service = getImeTrackerService(); final IImeTracker service = getImeTrackerService();
if (service == null) { if (service == null) {
return; return;
} }
try { try {
service.onProgress(statsToken, phase); service.onProgress(binder, phase);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#onFailed */
@AnyThread @AnyThread
static void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) { static void onFailed(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
final IImeTracker service = getImeTrackerService(); final IImeTracker service = getImeTrackerService();
if (service == null) { if (service == null) {
return; return;
@@ -637,8 +644,9 @@ final class IInputMethodManagerGlobalInvoker {
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#onCancelled */
@AnyThread @AnyThread
static void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) { static void onCancelled(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
final IImeTracker service = getImeTrackerService(); final IImeTracker service = getImeTrackerService();
if (service == null) { if (service == null) {
return; return;
@@ -650,8 +658,9 @@ final class IInputMethodManagerGlobalInvoker {
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#onShown */
@AnyThread @AnyThread
static void onShown(@NonNull IBinder statsToken) { static void onShown(@NonNull ImeTracker.Token statsToken) {
final IImeTracker service = getImeTrackerService(); final IImeTracker service = getImeTrackerService();
if (service == null) { if (service == null) {
return; return;
@@ -663,8 +672,9 @@ final class IInputMethodManagerGlobalInvoker {
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#onHidden */
@AnyThread @AnyThread
static void onHidden(@NonNull IBinder statsToken) { static void onHidden(@NonNull ImeTracker.Token statsToken) {
final IImeTracker service = getImeTrackerService(); final IImeTracker service = getImeTrackerService();
if (service == null) { if (service == null) {
return; return;
@@ -676,6 +686,7 @@ final class IInputMethodManagerGlobalInvoker {
} }
} }
/** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */
@AnyThread @AnyThread
@RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
static boolean hasPendingImeVisibilityRequests() { static boolean hasPendingImeVisibilityRequests() {

View File

@@ -26,7 +26,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityThread; import android.app.ActivityThread;
import android.content.Context; import android.content.Context;
import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@@ -47,7 +46,7 @@ import java.lang.reflect.Field;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** @hide */ /** @hide */
@@ -321,9 +320,8 @@ public interface ImeTracker {
/** /**
* Creates an IME show request tracking token. * Creates an IME show request tracking token.
* *
* @param component the component name where the IME show request was created, * @param component the name of the component that created the IME request, or {@code null}
* or {@code null} otherwise * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
* (defaulting to {@link ActivityThread#currentProcessName()}).
* @param uid the uid of the client that requested the IME. * @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME show request. * @param origin the origin of the IME show request.
* @param reason the reason why the IME show request was created. * @param reason the reason why the IME show request was created.
@@ -337,9 +335,8 @@ public interface ImeTracker {
/** /**
* Creates an IME hide request tracking token. * Creates an IME hide request tracking token.
* *
* @param component the component name where the IME hide request was created, * @param component the name of the component that created the IME request, or {@code null}
* or {@code null} otherwise * otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
* (defaulting to {@link ActivityThread#currentProcessName()}).
* @param uid the uid of the client that requested the IME. * @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME hide request. * @param origin the origin of the IME hide request.
* @param reason the reason why the IME hide request was created. * @param reason the reason why the IME hide request was created.
@@ -435,8 +432,7 @@ public interface ImeTracker {
mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false); mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false);
// Update logging flag dynamically. // Update logging flag dynamically.
SystemProperties.addChangeCallback(() -> SystemProperties.addChangeCallback(() ->
mLogProgress = mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false));
SystemProperties.getBoolean("persist.debug.imetracker", false));
} }
/** Whether progress should be logged. */ /** Whether progress should be logged. */
@@ -446,10 +442,9 @@ public interface ImeTracker {
@Override @Override
public Token onRequestShow(@Nullable String component, int uid, @Origin int origin, public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
@SoftInputShowHideReason int reason) { @SoftInputShowHideReason int reason) {
IBinder binder = IInputMethodManagerGlobalInvoker.onRequestShow(uid, origin, reason); final var tag = getTag(component);
if (binder == null) binder = new Binder(); final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin,
reason);
final Token token = Token.build(binder, component);
Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin) Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)); + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
@@ -461,10 +456,9 @@ public interface ImeTracker {
@Override @Override
public Token onRequestHide(@Nullable String component, int uid, @Origin int origin, public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
@SoftInputShowHideReason int reason) { @SoftInputShowHideReason int reason) {
IBinder binder = IInputMethodManagerGlobalInvoker.onRequestHide(uid, origin, reason); final var tag = getTag(component);
if (binder == null) binder = new Binder(); final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin,
reason);
final Token token = Token.build(binder, component);
Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin) Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)); + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
@@ -485,7 +479,7 @@ public interface ImeTracker {
@Override @Override
public void onFailed(@Nullable Token token, @Phase int phase) { public void onFailed(@Nullable Token token, @Phase int phase) {
if (token == null) return; if (token == null) return;
IInputMethodManagerGlobalInvoker.onFailed(token.mBinder, phase); IInputMethodManagerGlobalInvoker.onFailed(token, phase);
Log.i(TAG, token.mTag + ": onFailed at " + Debug.phaseToString(phase)); Log.i(TAG, token.mTag + ": onFailed at " + Debug.phaseToString(phase));
} }
@@ -499,7 +493,7 @@ public interface ImeTracker {
@Override @Override
public void onCancelled(@Nullable Token token, @Phase int phase) { public void onCancelled(@Nullable Token token, @Phase int phase) {
if (token == null) return; if (token == null) return;
IInputMethodManagerGlobalInvoker.onCancelled(token.mBinder, phase); IInputMethodManagerGlobalInvoker.onCancelled(token, phase);
Log.i(TAG, token.mTag + ": onCancelled at " + Debug.phaseToString(phase)); Log.i(TAG, token.mTag + ": onCancelled at " + Debug.phaseToString(phase));
} }
@@ -507,7 +501,7 @@ public interface ImeTracker {
@Override @Override
public void onShown(@Nullable Token token) { public void onShown(@Nullable Token token) {
if (token == null) return; if (token == null) return;
IInputMethodManagerGlobalInvoker.onShown(token.mBinder); IInputMethodManagerGlobalInvoker.onShown(token);
Log.i(TAG, token.mTag + ": onShown"); Log.i(TAG, token.mTag + ": onShown");
} }
@@ -515,10 +509,24 @@ public interface ImeTracker {
@Override @Override
public void onHidden(@Nullable Token token) { public void onHidden(@Nullable Token token) {
if (token == null) return; if (token == null) return;
IInputMethodManagerGlobalInvoker.onHidden(token.mBinder); IInputMethodManagerGlobalInvoker.onHidden(token);
Log.i(TAG, token.mTag + ": onHidden"); Log.i(TAG, token.mTag + ": onHidden");
} }
/**
* Returns a logging tag using the given component name.
*
* @param component the name of the component that created the IME request, or {@code null}
* otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
*/
@NonNull
private String getTag(@Nullable String component) {
if (component == null) {
component = ActivityThread.currentProcessName();
}
return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
}
}; };
/** The singleton IME tracker instance for instrumenting jank metrics. */ /** The singleton IME tracker instance for instrumenting jank metrics. */
@@ -528,28 +536,31 @@ public interface ImeTracker {
ImeLatencyTracker LATENCY_TRACKER = new ImeLatencyTracker(); ImeLatencyTracker LATENCY_TRACKER = new ImeLatencyTracker();
/** A token that tracks the progress of an IME request. */ /** A token that tracks the progress of an IME request. */
class Token implements Parcelable { final class Token implements Parcelable {
/** The binder used to identify this token. */
@NonNull @NonNull
public final IBinder mBinder; private final IBinder mBinder;
/** Logging tag, of the shape "component:random_hexadecimal". */
@NonNull @NonNull
private final String mTag; private final String mTag;
@NonNull public Token(@NonNull IBinder binder, @NonNull String tag) {
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);
}
private Token(@NonNull IBinder binder, @NonNull String tag) {
mBinder = binder; mBinder = binder;
mTag = tag; mTag = tag;
} }
/** Returns the {@link Token#mTag} */ private Token(@NonNull Parcel in) {
mBinder = in.readStrongBinder();
mTag = in.readString8();
}
@NonNull
public IBinder getBinder() {
return mBinder;
}
@NonNull @NonNull
public String getTag() { public String getTag() {
return mTag; return mTag;
@@ -562,7 +573,7 @@ public interface ImeTracker {
} }
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongBinder(mBinder); dest.writeStrongBinder(mBinder);
dest.writeString8(mTag); dest.writeString8(mTag);
} }
@@ -571,12 +582,11 @@ public interface ImeTracker {
public static final Creator<Token> CREATOR = new Creator<>() { public static final Creator<Token> CREATOR = new Creator<>() {
@NonNull @NonNull
@Override @Override
public Token createFromParcel(Parcel source) { public Token createFromParcel(@NonNull Parcel in) {
final IBinder binder = source.readStrongBinder(); return new Token(in);
final String tag = source.readString8();
return new Token(binder, tag);
} }
@NonNull
@Override @Override
public Token[] newArray(int size) { public Token[] newArray(int size) {
return new Token[size]; return new Token[size];
@@ -589,40 +599,50 @@ public interface ImeTracker {
* *
* Note: This is held in a separate class so that it only gets initialized when actually needed. * Note: This is held in a separate class so that it only gets initialized when actually needed.
*/ */
class Debug { final class Debug {
@NonNull
private static final Map<Integer, String> sTypes = private static final Map<Integer, String> sTypes =
getFieldMapping(ImeTracker.class, "TYPE_"); getFieldMapping(ImeTracker.class, "TYPE_");
@NonNull
private static final Map<Integer, String> sStatus = private static final Map<Integer, String> sStatus =
getFieldMapping(ImeTracker.class, "STATUS_"); getFieldMapping(ImeTracker.class, "STATUS_");
@NonNull
private static final Map<Integer, String> sOrigins = private static final Map<Integer, String> sOrigins =
getFieldMapping(ImeTracker.class, "ORIGIN_"); getFieldMapping(ImeTracker.class, "ORIGIN_");
@NonNull
private static final Map<Integer, String> sPhases = private static final Map<Integer, String> sPhases =
getFieldMapping(ImeTracker.class, "PHASE_"); getFieldMapping(ImeTracker.class, "PHASE_");
@NonNull
public static String typeToString(@Type int type) { public static String typeToString(@Type int type) {
return sTypes.getOrDefault(type, "TYPE_" + type); return sTypes.getOrDefault(type, "TYPE_" + type);
} }
@NonNull
public static String statusToString(@Status int status) { public static String statusToString(@Status int status) {
return sStatus.getOrDefault(status, "STATUS_" + status); return sStatus.getOrDefault(status, "STATUS_" + status);
} }
@NonNull
public static String originToString(@Origin int origin) { public static String originToString(@Origin int origin) {
return sOrigins.getOrDefault(origin, "ORIGIN_" + origin); return sOrigins.getOrDefault(origin, "ORIGIN_" + origin);
} }
@NonNull
public static String phaseToString(@Phase int phase) { public static String phaseToString(@Phase int phase) {
return sPhases.getOrDefault(phase, "PHASE_" + phase); return sPhases.getOrDefault(phase, "PHASE_" + phase);
} }
private static Map<Integer, String> getFieldMapping(Class<?> cls, String fieldPrefix) { @NonNull
private static Map<Integer, String> getFieldMapping(Class<?> cls,
@NonNull String fieldPrefix) {
return Arrays.stream(cls.getDeclaredFields()) return Arrays.stream(cls.getDeclaredFields())
.filter(field -> field.getName().startsWith(fieldPrefix)) .filter(field -> field.getName().startsWith(fieldPrefix))
.collect(Collectors.toMap(Debug::getFieldValue, Field::getName)); .collect(Collectors.toMap(Debug::getFieldValue, Field::getName));
} }
private static int getFieldValue(Field field) { private static int getFieldValue(@NonNull Field field) {
try { try {
return field.getInt(null); return field.getInt(null);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {

View File

@@ -14,43 +14,45 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.internal.view; package com.android.internal.inputmethod;
import android.view.inputmethod.ImeTracker; import android.view.inputmethod.ImeTracker;
/** /**
* Interface to the global Ime tracker, used by all client applications. * Interface to the global IME tracker service, used by all client applications.
* {@hide} * {@hide}
*/ */
interface IImeTracker { interface IImeTracker {
/** /**
* Called when an IME show request is created, * Called when an IME show request is created.
* returns a new Binder to be associated with the IME tracking token.
* *
* @param tag the logging tag.
* @param uid the uid of the client that requested the IME. * @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME show request. * @param origin the origin of the IME show request.
* @param reason the reason why the IME show request was created. * @param reason the reason why the IME show request was created.
* @return A new IME tracking token.
*/ */
IBinder onRequestShow(int uid, int origin, int reason); ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason);
/** /**
* Called when an IME hide request is created, * Called when an IME hide request is created.
* returns a new Binder to be associated with the IME tracking token.
* *
* @param tag the logging tag.
* @param uid the uid of the client that requested the IME. * @param uid the uid of the client that requested the IME.
* @param origin the origin of the IME hide request. * @param origin the origin of the IME hide request.
* @param reason the reason why the IME hide request was created. * @param reason the reason why the IME hide request was created.
* @return A new IME tracking token.
*/ */
IBinder onRequestHide(int uid, int origin, int reason); ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason);
/** /**
* Called when the IME request progresses to a further phase. * Called when the IME request progresses to a further phase.
* *
* @param statsToken the token tracking the current IME request. * @param binder the binder of token tracking the current IME request.
* @param phase the new phase the IME request reached. * @param phase the new phase the IME request reached.
*/ */
oneway void onProgress(in IBinder statsToken, int phase); oneway void onProgress(in IBinder binder, int phase);
/** /**
* Called when the IME request fails. * Called when the IME request fails.
@@ -58,7 +60,7 @@ interface IImeTracker {
* @param statsToken the token tracking the current IME request. * @param statsToken the token tracking the current IME request.
* @param phase the phase the IME request failed at. * @param phase the phase the IME request failed at.
*/ */
oneway void onFailed(in IBinder statsToken, int phase); oneway void onFailed(in ImeTracker.Token statsToken, int phase);
/** /**
* Called when the IME request is cancelled. * Called when the IME request is cancelled.
@@ -66,21 +68,21 @@ interface IImeTracker {
* @param statsToken the token tracking the current IME request. * @param statsToken the token tracking the current IME request.
* @param phase the phase the IME request was cancelled at. * @param phase the phase the IME request was cancelled at.
*/ */
oneway void onCancelled(in IBinder statsToken, int phase); oneway void onCancelled(in ImeTracker.Token statsToken, int phase);
/** /**
* Called when the IME show request is successful. * Called when the IME show request is successful.
* *
* @param statsToken the token tracking the current IME request. * @param statsToken the token tracking the current IME request.
*/ */
oneway void onShown(in IBinder statsToken); oneway void onShown(in ImeTracker.Token statsToken);
/** /**
* Called when the IME hide request is successful. * Called when the IME hide request is successful.
* *
* @param statsToken the token tracking the current IME request. * @param statsToken the token tracking the current IME request.
*/ */
oneway void onHidden(in IBinder statsToken); oneway void onHidden(in ImeTracker.Token statsToken);
/** /**
* Checks whether there are any pending IME visibility requests. * Checks whether there are any pending IME visibility requests.

View File

@@ -23,11 +23,11 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.window.ImeOnBackInvokedDispatcher; import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient; import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection; import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.InputBindResult; import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.view.IImeTracker;
/** /**
* Public interface to the global input method manager, used by all client * Public interface to the global input method manager, used by all client

View File

@@ -24,13 +24,14 @@ import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.util.Log;
import android.view.inputmethod.ImeTracker; import android.view.inputmethod.ImeTracker;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.view.IImeTracker;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.time.Instant; import java.time.Instant;
@@ -53,7 +54,7 @@ import java.util.concurrent.atomic.AtomicInteger;
@SuppressWarnings("GuardedBy") @SuppressWarnings("GuardedBy")
public final class ImeTrackerService extends IImeTracker.Stub { public final class ImeTrackerService extends IImeTracker.Stub {
static final String TAG = "ImeTrackerService"; private static final String TAG = ImeTracker.TAG;
/** The threshold in milliseconds after which a history entry is considered timed out. */ /** The threshold in milliseconds after which a history entry is considered timed out. */
private static final long TIMEOUT_MS = 10_000; private static final long TIMEOUT_MS = 10_000;
@@ -71,67 +72,71 @@ public final class ImeTrackerService extends IImeTracker.Stub {
@NonNull @NonNull
@Override @Override
public synchronized IBinder onRequestShow(int uid, @ImeTracker.Origin int origin, public synchronized ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
@SoftInputShowHideReason int reason) { @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final IBinder binder = new Binder(); final var binder = new Binder();
final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_SHOW, final var token = new ImeTracker.Token(binder, tag);
ImeTracker.STATUS_RUN, origin, reason); final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN,
origin, reason);
mHistory.addEntry(binder, entry); mHistory.addEntry(binder, entry);
// Register a delayed task to handle the case where the new entry times out. // Register a delayed task to handle the case where the new entry times out.
mHandler.postDelayed(() -> { mHandler.postDelayed(() -> {
synchronized (ImeTrackerService.this) { synchronized (ImeTrackerService.this) {
mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET); mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
} }
}, TIMEOUT_MS); }, TIMEOUT_MS);
return binder; return token;
} }
@NonNull @NonNull
@Override @Override
public synchronized IBinder onRequestHide(int uid, @ImeTracker.Origin int origin, public synchronized ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
@SoftInputShowHideReason int reason) { @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
final IBinder binder = new Binder(); final var binder = new Binder();
final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_HIDE, final var token = new ImeTracker.Token(binder, tag);
ImeTracker.STATUS_RUN, origin, reason); final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN,
origin, reason);
mHistory.addEntry(binder, entry); mHistory.addEntry(binder, entry);
// Register a delayed task to handle the case where the new entry times out. // Register a delayed task to handle the case where the new entry times out.
mHandler.postDelayed(() -> { mHandler.postDelayed(() -> {
synchronized (ImeTrackerService.this) { synchronized (ImeTrackerService.this) {
mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET); mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
} }
}, TIMEOUT_MS); }, TIMEOUT_MS);
return binder; return token;
} }
@Override @Override
public synchronized void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) { public synchronized void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
final History.Entry entry = mHistory.getEntry(statsToken); final var entry = mHistory.getEntry(binder);
if (entry == null) return; if (entry == null) return;
entry.mPhase = phase; entry.mPhase = phase;
} }
@Override @Override
public synchronized void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) { public synchronized void onFailed(@NonNull ImeTracker.Token statsToken,
@ImeTracker.Phase int phase) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_FAIL, phase); mHistory.setFinished(statsToken, ImeTracker.STATUS_FAIL, phase);
} }
@Override @Override
public synchronized void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) { public synchronized void onCancelled(@NonNull ImeTracker.Token statsToken,
@ImeTracker.Phase int phase) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_CANCEL, phase); mHistory.setFinished(statsToken, ImeTracker.STATUS_CANCEL, phase);
} }
@Override @Override
public synchronized void onShown(@NonNull IBinder statsToken) { public synchronized void onShown(@NonNull ImeTracker.Token statsToken) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET); mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
} }
@Override @Override
public synchronized void onHidden(@NonNull IBinder statsToken) { public synchronized void onHidden(@NonNull ImeTracker.Token statsToken) {
mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET); mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
} }
@@ -141,9 +146,9 @@ public final class ImeTrackerService extends IImeTracker.Stub {
* @param statsToken the token corresponding to the current IME request. * @param statsToken the token corresponding to the current IME request.
* @param requestWindowName the name of the window that created the IME request. * @param requestWindowName the name of the window that created the IME request.
*/ */
public synchronized void onImmsUpdate(@NonNull IBinder statsToken, public synchronized void onImmsUpdate(@NonNull ImeTracker.Token statsToken,
@NonNull String requestWindowName) { @NonNull String requestWindowName) {
final History.Entry entry = mHistory.getEntry(statsToken); final var entry = mHistory.getEntry(statsToken.getBinder());
if (entry == null) return; if (entry == null) return;
entry.mRequestWindowName = requestWindowName; entry.mRequestWindowName = requestWindowName;
@@ -181,17 +186,17 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/** Latest entry sequence number. */ /** Latest entry sequence number. */
private static final AtomicInteger sSequenceNumber = new AtomicInteger(0); private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
/** Adds a live entry. */ /** Adds a live entry corresponding to the given IME tracking token's binder. */
@GuardedBy("ImeTrackerService.this") @GuardedBy("ImeTrackerService.this")
private void addEntry(@NonNull IBinder statsToken, @NonNull Entry entry) { private void addEntry(@NonNull IBinder binder, @NonNull Entry entry) {
mLiveEntries.put(statsToken, entry); mLiveEntries.put(binder, entry);
} }
/** Gets the entry corresponding to the given IME tracking token, if it exists. */ /** Gets the entry corresponding to the given IME tracking token's binder, if it exists. */
@Nullable @Nullable
@GuardedBy("ImeTrackerService.this") @GuardedBy("ImeTrackerService.this")
private Entry getEntry(@NonNull IBinder statsToken) { private Entry getEntry(@NonNull IBinder binder) {
return mLiveEntries.get(statsToken); return mLiveEntries.get(binder);
} }
/** /**
@@ -204,10 +209,21 @@ public final class ImeTrackerService extends IImeTracker.Stub {
* (or {@link ImeTracker#PHASE_NOT_SET} otherwise). * (or {@link ImeTracker#PHASE_NOT_SET} otherwise).
*/ */
@GuardedBy("ImeTrackerService.this") @GuardedBy("ImeTrackerService.this")
private void setFinished(@NonNull IBinder statsToken, @ImeTracker.Status int status, private void setFinished(@NonNull ImeTracker.Token statsToken,
@ImeTracker.Phase int phase) { @ImeTracker.Status int status, @ImeTracker.Phase int phase) {
final Entry entry = mLiveEntries.remove(statsToken); final var entry = mLiveEntries.remove(statsToken.getBinder());
if (entry == null) return; if (entry == null) {
// This will be unconditionally called through the postDelayed above to handle
// potential timeouts, and is thus intentionally dropped to avoid having to manually
// save and remove the registered callback. Only timeout calls are expected.
if (status != ImeTracker.STATUS_TIMEOUT) {
Log.i(TAG, statsToken.getTag()
+ ": setFinished on previously finished token at "
+ ImeTracker.Debug.phaseToString(phase) + " with "
+ ImeTracker.Debug.statusToString(status));
}
return;
}
entry.mDuration = System.currentTimeMillis() - entry.mStartTime; entry.mDuration = System.currentTimeMillis() - entry.mStartTime;
entry.mStatus = status; entry.mStatus = status;
@@ -216,6 +232,13 @@ public final class ImeTrackerService extends IImeTracker.Stub {
entry.mPhase = phase; entry.mPhase = phase;
} }
if (status == ImeTracker.STATUS_TIMEOUT) {
// All events other than timeouts are already logged in the client-side ImeTracker.
Log.i(TAG, statsToken.getTag() + ": setFinished at "
+ ImeTracker.Debug.phaseToString(entry.mPhase) + " with "
+ ImeTracker.Debug.statusToString(status));
}
// Remove excess entries overflowing capacity (plus one for the new entry). // Remove excess entries overflowing capacity (plus one for the new entry).
while (mEntries.size() >= CAPACITY) { while (mEntries.size() >= CAPACITY) {
mEntries.remove(); mEntries.remove();
@@ -232,21 +255,22 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/** Dumps the contents of the circular buffer. */ /** Dumps the contents of the circular buffer. */
@GuardedBy("ImeTrackerService.this") @GuardedBy("ImeTrackerService.this")
private void dump(@NonNull PrintWriter pw, @NonNull String prefix) { private void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
final DateTimeFormatter formatter = final var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US) .withZone(ZoneId.systemDefault());
.withZone(ZoneId.systemDefault());
pw.print(prefix); pw.print(prefix);
pw.println("ImeTrackerService#History.mLiveEntries:"); pw.println("ImeTrackerService#History.mLiveEntries: "
+ mLiveEntries.size() + " elements");
for (final Entry entry: mLiveEntries.values()) { for (final var entry: mLiveEntries.values()) {
dumpEntry(entry, pw, prefix, formatter); dumpEntry(entry, pw, prefix, formatter);
} }
pw.print(prefix); pw.print(prefix);
pw.println("ImeTrackerService#History.mEntries:"); pw.println("ImeTrackerService#History.mEntries: "
+ mEntries.size() + " elements");
for (final Entry entry: mEntries) { for (final var entry: mEntries) {
dumpEntry(entry, pw, prefix, formatter); dumpEntry(entry, pw, prefix, formatter);
} }
} }
@@ -255,34 +279,22 @@ public final class ImeTrackerService extends IImeTracker.Stub {
private void dumpEntry(@NonNull Entry entry, @NonNull PrintWriter pw, private void dumpEntry(@NonNull Entry entry, @NonNull PrintWriter pw,
@NonNull String prefix, @NonNull DateTimeFormatter formatter) { @NonNull String prefix, @NonNull DateTimeFormatter formatter) {
pw.print(prefix); pw.print(prefix);
pw.println("ImeTrackerService#History #" + entry.mSequenceNumber + ":"); pw.print(" #" + entry.mSequenceNumber);
pw.print(" " + ImeTracker.Debug.typeToString(entry.mType));
pw.print(" - " + ImeTracker.Debug.statusToString(entry.mStatus));
pw.print(" - " + entry.mTag);
pw.println(" (" + entry.mDuration + "ms):");
pw.print(prefix); pw.print(prefix);
pw.println(" startTime=" + formatter.format(Instant.ofEpochMilli(entry.mStartTime))); pw.print(" startTime=" + formatter.format(Instant.ofEpochMilli(entry.mStartTime)));
pw.println(" " + ImeTracker.Debug.originToString(entry.mOrigin));
pw.print(prefix); pw.print(prefix);
pw.println(" duration=" + entry.mDuration + "ms"); pw.print(" reason=" + InputMethodDebug.softInputDisplayReasonToString(entry.mReason));
pw.println(" " + ImeTracker.Debug.phaseToString(entry.mPhase));
pw.print(prefix); pw.print(prefix);
pw.print(" type=" + ImeTracker.Debug.typeToString(entry.mType)); pw.println(" requestWindowName=" + entry.mRequestWindowName);
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. */ /** A history entry. */
@@ -291,6 +303,10 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/** The entry's sequence number in the history. */ /** The entry's sequence number in the history. */
private final int mSequenceNumber = sSequenceNumber.getAndIncrement(); private final int mSequenceNumber = sSequenceNumber.getAndIncrement();
/** Logging tag, of the shape "component:random_hexadecimal". */
@NonNull
private final String mTag;
/** Uid of the client that requested the IME. */ /** Uid of the client that requested the IME. */
private final int mUid; private final int mUid;
@@ -323,13 +339,15 @@ public final class ImeTrackerService extends IImeTracker.Stub {
/** /**
* Name of the window that created the IME request. * Name of the window that created the IME request.
* *
* Note: This is later set through {@link #onImmsUpdate(IBinder, String)}. * Note: This is later set through {@link #onImmsUpdate}.
*/ */
@NonNull @NonNull
private String mRequestWindowName = "not set"; private String mRequestWindowName = "not set";
private Entry(int uid, @ImeTracker.Type int type, @ImeTracker.Status int status, private Entry(@NonNull String tag, int uid, @ImeTracker.Type int type,
@ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) { @ImeTracker.Status int status, @ImeTracker.Origin int origin,
@SoftInputShowHideReason int reason) {
mTag = tag;
mUid = uid; mUid = uid;
mType = type; mType = type;
mStatus = status; mStatus = status;

View File

@@ -145,6 +145,7 @@ import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.DirectBootAwareness; import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession; import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethod; import com.android.internal.inputmethod.IInputMethod;
@@ -168,7 +169,6 @@ import com.android.internal.os.TransferPipe;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils;
import com.android.internal.view.IImeTracker;
import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodManager;
import com.android.server.AccessibilityManagerInternal; import com.android.server.AccessibilityManagerInternal;
import com.android.server.EventLogTags; import com.android.server.EventLogTags;
@@ -4715,7 +4715,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
info.imeSurfaceParentName)); info.imeSurfaceParentName));
if (statsToken != null) { if (statsToken != null) {
mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName); mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName);
} }
} }