1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view.inputmethod;
18 
19 import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
20 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
21 
22 import static com.android.internal.inputmethod.InputMethodDebug.softInputDisplayReasonToString;
23 import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_HIDE_ANIMATION;
24 import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_SHOW_ANIMATION;
25 import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_HIDDEN;
26 import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_SHOWN;
27 
28 import android.annotation.IntDef;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.app.ActivityThread;
32 import android.content.Context;
33 import android.os.IBinder;
34 import android.os.Parcel;
35 import android.os.Parcelable;
36 import android.os.SystemProperties;
37 import android.util.Log;
38 import android.view.InsetsController.AnimationType;
39 import android.view.SurfaceControl;
40 
41 import com.android.internal.inputmethod.InputMethodDebug;
42 import com.android.internal.inputmethod.SoftInputShowHideReason;
43 import com.android.internal.jank.InteractionJankMonitor;
44 import com.android.internal.jank.InteractionJankMonitor.Configuration;
45 import com.android.internal.util.LatencyTracker;
46 
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 import java.lang.reflect.Field;
50 import java.util.Arrays;
51 import java.util.Locale;
52 import java.util.Map;
53 import java.util.concurrent.ThreadLocalRandom;
54 import java.util.stream.Collectors;
55 
56 /** @hide */
57 public interface ImeTracker {
58 
59     String TAG = "ImeTracker";
60 
61     /** The debug flag for IME visibility event log. */
62     boolean DEBUG_IME_VISIBILITY = SystemProperties.getBoolean("persist.debug.imf_event", false);
63 
64     /** The message to indicate if there is no valid {@link Token}. */
65     String TOKEN_NONE = "TOKEN_NONE";
66 
67     /** The type of the IME request. */
68     @IntDef(prefix = { "TYPE_" }, value = {
69             TYPE_SHOW,
70             TYPE_HIDE
71     })
72     @Retention(RetentionPolicy.SOURCE)
73     @interface Type {}
74 
75     /** IME show request type. */
76     int TYPE_SHOW = ImeProtoEnums.TYPE_SHOW;
77 
78     /** IME hide request type. */
79     int TYPE_HIDE = ImeProtoEnums.TYPE_HIDE;
80 
81     /** The status of the IME request. */
82     @IntDef(prefix = { "STATUS_" }, value = {
83             STATUS_RUN,
84             STATUS_CANCEL,
85             STATUS_FAIL,
86             STATUS_SUCCESS,
87             STATUS_TIMEOUT
88     })
89     @Retention(RetentionPolicy.SOURCE)
90     @interface Status {}
91 
92     /** IME request running. */
93     int STATUS_RUN = ImeProtoEnums.STATUS_RUN;
94 
95     /** IME request cancelled. */
96     int STATUS_CANCEL = ImeProtoEnums.STATUS_CANCEL;
97 
98     /** IME request failed. */
99     int STATUS_FAIL = ImeProtoEnums.STATUS_FAIL;
100 
101     /** IME request succeeded. */
102     int STATUS_SUCCESS = ImeProtoEnums.STATUS_SUCCESS;
103 
104     /** IME request timed out. */
105     int STATUS_TIMEOUT = ImeProtoEnums.STATUS_TIMEOUT;
106 
107     /**
108      * The origin of the IME request
109      *
110      * The name follows the format {@code PHASE_x_...} where {@code x} denotes
111      * where the origin is (i.e. {@code PHASE_SERVER_...} occurs in the server).
112      */
113     @IntDef(prefix = { "ORIGIN_" }, value = {
114             ORIGIN_CLIENT_SHOW_SOFT_INPUT,
115             ORIGIN_CLIENT_HIDE_SOFT_INPUT,
116             ORIGIN_SERVER_START_INPUT,
117             ORIGIN_SERVER_HIDE_INPUT
118     })
119     @Retention(RetentionPolicy.SOURCE)
120     @interface Origin {}
121 
122     /** The IME show request originated in the client. */
123     int ORIGIN_CLIENT_SHOW_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_SHOW_SOFT_INPUT;
124 
125     /** The IME hide request originated in the client. */
126     int ORIGIN_CLIENT_HIDE_SOFT_INPUT = ImeProtoEnums.ORIGIN_CLIENT_HIDE_SOFT_INPUT;
127 
128     /** The IME show request originated in the server. */
129     int ORIGIN_SERVER_START_INPUT = ImeProtoEnums.ORIGIN_SERVER_START_INPUT;
130 
131     /** The IME hide request originated in the server. */
132     int ORIGIN_SERVER_HIDE_INPUT = ImeProtoEnums.ORIGIN_SERVER_HIDE_INPUT;
133 
134     /**
135      * The current phase of the IME request.
136      *
137      * The name follows the format {@code PHASE_x_...} where {@code x} denotes
138      * where the phase is (i.e. {@code PHASE_SERVER_...} occurs in the server).
139      */
140     @IntDef(prefix = { "PHASE_" }, value = {
141             PHASE_NOT_SET,
142             PHASE_CLIENT_VIEW_SERVED,
143             PHASE_SERVER_CLIENT_KNOWN,
144             PHASE_SERVER_CLIENT_FOCUSED,
145             PHASE_SERVER_ACCESSIBILITY,
146             PHASE_SERVER_SYSTEM_READY,
147             PHASE_SERVER_HIDE_IMPLICIT,
148             PHASE_SERVER_HIDE_NOT_ALWAYS,
149             PHASE_SERVER_WAIT_IME,
150             PHASE_SERVER_HAS_IME,
151             PHASE_SERVER_SHOULD_HIDE,
152             PHASE_IME_WRAPPER,
153             PHASE_IME_WRAPPER_DISPATCH,
154             PHASE_IME_SHOW_SOFT_INPUT,
155             PHASE_IME_HIDE_SOFT_INPUT,
156             PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE,
157             PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER,
158             PHASE_SERVER_APPLY_IME_VISIBILITY,
159             PHASE_WM_SHOW_IME_RUNNER,
160             PHASE_WM_SHOW_IME_READY,
161             PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET,
162             PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS,
163             PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS,
164             PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS,
165             PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS,
166             PHASE_WM_REMOTE_INSETS_CONTROLLER,
167             PHASE_WM_ANIMATION_CREATE,
168             PHASE_WM_ANIMATION_RUNNING,
169             PHASE_CLIENT_SHOW_INSETS,
170             PHASE_CLIENT_HIDE_INSETS,
171             PHASE_CLIENT_HANDLE_SHOW_INSETS,
172             PHASE_CLIENT_HANDLE_HIDE_INSETS,
173             PHASE_CLIENT_APPLY_ANIMATION,
174             PHASE_CLIENT_CONTROL_ANIMATION,
175             PHASE_CLIENT_DISABLED_USER_ANIMATION,
176             PHASE_CLIENT_COLLECT_SOURCE_CONTROLS,
177             PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW,
178             PHASE_CLIENT_REQUEST_IME_SHOW,
179             PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN,
180             PHASE_CLIENT_ANIMATION_RUNNING,
181             PHASE_CLIENT_ANIMATION_CANCEL,
182             PHASE_CLIENT_ANIMATION_FINISHED_SHOW,
183             PHASE_CLIENT_ANIMATION_FINISHED_HIDE
184     })
185     @Retention(RetentionPolicy.SOURCE)
186     @interface Phase {}
187 
188     int PHASE_NOT_SET = ImeProtoEnums.PHASE_NOT_SET;
189 
190     /** The view that requested the IME has been served by the IMM. */
191     int PHASE_CLIENT_VIEW_SERVED = ImeProtoEnums.PHASE_CLIENT_VIEW_SERVED;
192 
193     /** The IME client that requested the IME has window manager focus. */
194     int PHASE_SERVER_CLIENT_KNOWN = ImeProtoEnums.PHASE_SERVER_CLIENT_KNOWN;
195 
196     /** The IME client that requested the IME has IME focus. */
197     int PHASE_SERVER_CLIENT_FOCUSED = ImeProtoEnums.PHASE_SERVER_CLIENT_FOCUSED;
198 
199     /** The IME request complies with the current accessibility settings. */
200     int PHASE_SERVER_ACCESSIBILITY = ImeProtoEnums.PHASE_SERVER_ACCESSIBILITY;
201 
202     /** The server is ready to run third party code. */
203     int PHASE_SERVER_SYSTEM_READY = ImeProtoEnums.PHASE_SERVER_SYSTEM_READY;
204 
205     /** Checked the implicit hide request against any explicit show requests. */
206     int PHASE_SERVER_HIDE_IMPLICIT = ImeProtoEnums.PHASE_SERVER_HIDE_IMPLICIT;
207 
208     /** Checked the not-always hide request against any forced show requests. */
209     int PHASE_SERVER_HIDE_NOT_ALWAYS = ImeProtoEnums.PHASE_SERVER_HIDE_NOT_ALWAYS;
210 
211     /** The server is waiting for a connection to the IME. */
212     int PHASE_SERVER_WAIT_IME = ImeProtoEnums.PHASE_SERVER_WAIT_IME;
213 
214     /** The server has a connection to the IME. */
215     int PHASE_SERVER_HAS_IME = ImeProtoEnums.PHASE_SERVER_HAS_IME;
216 
217     /** The server decided the IME should be hidden. */
218     int PHASE_SERVER_SHOULD_HIDE = ImeProtoEnums.PHASE_SERVER_SHOULD_HIDE;
219 
220     /** Reached the IME wrapper. */
221     int PHASE_IME_WRAPPER = ImeProtoEnums.PHASE_IME_WRAPPER;
222 
223     /** Dispatched from the IME wrapper to the IME. */
224     int PHASE_IME_WRAPPER_DISPATCH = ImeProtoEnums.PHASE_IME_WRAPPER_DISPATCH;
225 
226     /** Reached the IME' showSoftInput method. */
227     int PHASE_IME_SHOW_SOFT_INPUT = ImeProtoEnums.PHASE_IME_SHOW_SOFT_INPUT;
228 
229     /** Reached the IME' hideSoftInput method. */
230     int PHASE_IME_HIDE_SOFT_INPUT = ImeProtoEnums.PHASE_IME_HIDE_SOFT_INPUT;
231 
232     /** The server decided the IME should be shown. */
233     int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = ImeProtoEnums.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE;
234 
235     /** Requested applying the IME visibility in the insets source consumer. */
236     int PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER =
237             ImeProtoEnums.PHASE_IME_APPLY_VISIBILITY_INSETS_CONSUMER;
238 
239     /** Applied the IME visibility. */
240     int PHASE_SERVER_APPLY_IME_VISIBILITY = ImeProtoEnums.PHASE_SERVER_APPLY_IME_VISIBILITY;
241 
242     /** Created the show IME runner. */
243     int PHASE_WM_SHOW_IME_RUNNER = ImeProtoEnums.PHASE_WM_SHOW_IME_RUNNER;
244 
245     /** Ready to show IME. */
246     int PHASE_WM_SHOW_IME_READY = ImeProtoEnums.PHASE_WM_SHOW_IME_READY;
247 
248     /** The Window Manager has a connection to the IME insets control target. */
249     int PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET =
250             ImeProtoEnums.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET;
251 
252     /** Reached the window insets control target's show insets method. */
253     int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS =
254             ImeProtoEnums.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS;
255 
256     /** Reached the window insets control target's hide insets method. */
257     int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS =
258             ImeProtoEnums.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS;
259 
260     /** Reached the remote insets control target's show insets method. */
261     int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS =
262             ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS;
263 
264     /** Reached the remote insets control target's hide insets method. */
265     int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS =
266             ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS;
267 
268     /** Reached the remote insets controller. */
269     int PHASE_WM_REMOTE_INSETS_CONTROLLER = ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROLLER;
270 
271     /** Created the IME window insets show animation. */
272     int PHASE_WM_ANIMATION_CREATE = ImeProtoEnums.PHASE_WM_ANIMATION_CREATE;
273 
274     /** Started the IME window insets show animation. */
275     int PHASE_WM_ANIMATION_RUNNING = ImeProtoEnums.PHASE_WM_ANIMATION_RUNNING;
276 
277     /** Reached the client's show insets method. */
278     int PHASE_CLIENT_SHOW_INSETS = ImeProtoEnums.PHASE_CLIENT_SHOW_INSETS;
279 
280     /** Reached the client's hide insets method. */
281     int PHASE_CLIENT_HIDE_INSETS = ImeProtoEnums.PHASE_CLIENT_HIDE_INSETS;
282 
283     /** Handling the IME window insets show request. */
284     int PHASE_CLIENT_HANDLE_SHOW_INSETS = ImeProtoEnums.PHASE_CLIENT_HANDLE_SHOW_INSETS;
285 
286     /** Handling the IME window insets hide request. */
287     int PHASE_CLIENT_HANDLE_HIDE_INSETS = ImeProtoEnums.PHASE_CLIENT_HANDLE_HIDE_INSETS;
288 
289     /** Applied the IME window insets show animation. */
290     int PHASE_CLIENT_APPLY_ANIMATION = ImeProtoEnums.PHASE_CLIENT_APPLY_ANIMATION;
291 
292     /** Started the IME window insets show animation. */
293     int PHASE_CLIENT_CONTROL_ANIMATION = ImeProtoEnums.PHASE_CLIENT_CONTROL_ANIMATION;
294 
295     /** Checked that the IME is controllable. */
296     int PHASE_CLIENT_DISABLED_USER_ANIMATION = ImeProtoEnums.PHASE_CLIENT_DISABLED_USER_ANIMATION;
297 
298     /** Collecting insets source controls. */
299     int PHASE_CLIENT_COLLECT_SOURCE_CONTROLS = ImeProtoEnums.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS;
300 
301     /** Reached the insets source consumer's show request method. */
302     int PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW =
303             ImeProtoEnums.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW;
304 
305     /** Reached input method manager's request IME show method. */
306     int PHASE_CLIENT_REQUEST_IME_SHOW = ImeProtoEnums.PHASE_CLIENT_REQUEST_IME_SHOW;
307 
308     /** Reached the insets source consumer's notify hidden method. */
309     int PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN =
310             ImeProtoEnums.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN;
311 
312     /** Queued the IME window insets show animation. */
313     int PHASE_CLIENT_ANIMATION_RUNNING = ImeProtoEnums.PHASE_CLIENT_ANIMATION_RUNNING;
314 
315     /** Cancelled the IME window insets show animation. */
316     int PHASE_CLIENT_ANIMATION_CANCEL = ImeProtoEnums.PHASE_CLIENT_ANIMATION_CANCEL;
317 
318     /** Finished the IME window insets show animation. */
319     int PHASE_CLIENT_ANIMATION_FINISHED_SHOW = ImeProtoEnums.PHASE_CLIENT_ANIMATION_FINISHED_SHOW;
320 
321     /** Finished the IME window insets hide animation. */
322     int PHASE_CLIENT_ANIMATION_FINISHED_HIDE = ImeProtoEnums.PHASE_CLIENT_ANIMATION_FINISHED_HIDE;
323 
324     /**
325      * Creates an IME show request tracking token.
326      *
327      * @param component the name of the component that created the IME request, or {@code null}
328      *                  otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
329      * @param uid the uid of the client that requested the IME.
330      * @param origin the origin of the IME show request.
331      * @param reason the reason why the IME show request was created.
332      *
333      * @return An IME tracking token.
334      */
335     @NonNull
onRequestShow(@ullable String component, int uid, @Origin int origin, @SoftInputShowHideReason int reason)336     Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
337             @SoftInputShowHideReason int reason);
338 
339     /**
340      * Creates an IME hide request tracking token.
341      *
342      * @param component the name of the component that created the IME request, or {@code null}
343      *                  otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
344      * @param uid the uid of the client that requested the IME.
345      * @param origin the origin of the IME hide request.
346      * @param reason the reason why the IME hide request was created.
347      *
348      * @return An IME tracking token.
349      */
350     @NonNull
onRequestHide(@ullable String component, int uid, @Origin int origin, @SoftInputShowHideReason int reason)351     Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
352             @SoftInputShowHideReason int reason);
353 
354     /**
355      * Called when an IME request progresses to a further phase.
356      *
357      * @param token the token tracking the current IME request or {@code null} otherwise.
358      * @param phase the new phase the IME request reached.
359      */
onProgress(@ullable Token token, @Phase int phase)360     void onProgress(@Nullable Token token, @Phase int phase);
361 
362     /**
363      * Called when an IME request fails.
364      *
365      * @param token the token tracking the current IME request or {@code null} otherwise.
366      * @param phase the phase the IME request failed at.
367      */
onFailed(@ullable Token token, @Phase int phase)368     void onFailed(@Nullable Token token, @Phase int phase);
369 
370     /**
371      * Called when an IME request reached a flow that is not yet implemented.
372      *
373      * @param token the token tracking the current IME request or {@code null} otherwise.
374      * @param phase the phase the IME request was currently at.
375      */
onTodo(@ullable Token token, @Phase int phase)376     void onTodo(@Nullable Token token, @Phase int phase);
377 
378     /**
379      * Called when an IME request is cancelled.
380      *
381      * @param token the token tracking the current IME request or {@code null} otherwise.
382      * @param phase the phase the IME request was cancelled at.
383      */
onCancelled(@ullable Token token, @Phase int phase)384     void onCancelled(@Nullable Token token, @Phase int phase);
385 
386     /**
387      * Called when the IME show request is successful.
388      *
389      * @param token the token tracking the current IME show request or {@code null} otherwise.
390      */
onShown(@ullable Token token)391     void onShown(@Nullable Token token);
392 
393     /**
394      * Called when the IME hide request is successful.
395      *
396      * @param token the token tracking the current IME hide request or {@code null} otherwise.
397      */
onHidden(@ullable Token token)398     void onHidden(@Nullable Token token);
399 
400     /**
401      * Get the singleton request tracker instance.
402      *
403      * @return the singleton request tracker instance
404      */
405     @NonNull
forLogging()406     static ImeTracker forLogging() {
407         return LOGGER;
408     }
409 
410     /**
411      * Get the singleton jank tracker instance.
412      *
413      * @return the singleton jank tracker instance
414      */
415     @NonNull
forJank()416     static ImeJankTracker forJank() {
417         return JANK_TRACKER;
418     }
419 
420     /**
421      * Get the singleton latency tracker instance.
422      *
423      * @return the singleton latency tracker instance
424      */
425     @NonNull
forLatency()426     static ImeLatencyTracker forLatency() {
427         return LATENCY_TRACKER;
428     }
429 
430     /** The singleton IME tracker instance. */
431     @NonNull
432     ImeTracker LOGGER = new ImeTracker() {
433 
434         {
435             // Set logging flag initial value.
436             mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false);
437             // Update logging flag dynamically.
438             SystemProperties.addChangeCallback(() ->
439                     mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false));
440         }
441 
442         /** Whether progress should be logged. */
443         private boolean mLogProgress;
444 
445         @NonNull
446         @Override
447         public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
448                 @SoftInputShowHideReason int reason) {
449             final var tag = getTag(component);
450             final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin,
451                     reason);
452 
453             Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
454                     + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
455 
456             return token;
457         }
458 
459         @NonNull
460         @Override
461         public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
462                 @SoftInputShowHideReason int reason) {
463             final var tag = getTag(component);
464             final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin,
465                     reason);
466 
467             Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
468                     + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
469 
470             return token;
471         }
472 
473         @Override
474         public void onProgress(@Nullable Token token, @Phase int phase) {
475             if (token == null) return;
476             IInputMethodManagerGlobalInvoker.onProgress(token.mBinder, phase);
477 
478             if (mLogProgress) {
479                 Log.i(TAG, token.mTag + ": onProgress at " + Debug.phaseToString(phase));
480             }
481         }
482 
483         @Override
484         public void onFailed(@Nullable Token token, @Phase int phase) {
485             if (token == null) return;
486             IInputMethodManagerGlobalInvoker.onFailed(token, phase);
487 
488             Log.i(TAG, token.mTag + ": onFailed at " + Debug.phaseToString(phase));
489         }
490 
491         @Override
492         public void onTodo(@Nullable Token token, @Phase int phase) {
493             if (token == null) return;
494             Log.i(TAG, token.mTag + ": onTodo at " + Debug.phaseToString(phase));
495         }
496 
497         @Override
498         public void onCancelled(@Nullable Token token, @Phase int phase) {
499             if (token == null) return;
500             IInputMethodManagerGlobalInvoker.onCancelled(token, phase);
501 
502             Log.i(TAG, token.mTag + ": onCancelled at " + Debug.phaseToString(phase));
503         }
504 
505         @Override
506         public void onShown(@Nullable Token token) {
507             if (token == null) return;
508             IInputMethodManagerGlobalInvoker.onShown(token);
509 
510             Log.i(TAG, token.mTag + ": onShown");
511         }
512 
513         @Override
514         public void onHidden(@Nullable Token token) {
515             if (token == null) return;
516             IInputMethodManagerGlobalInvoker.onHidden(token);
517 
518             Log.i(TAG, token.mTag + ": onHidden");
519         }
520 
521         /**
522          * Returns a logging tag using the given component name.
523          *
524          * @param component the name of the component that created the IME request, or {@code null}
525          *                  otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
526          */
527         @NonNull
528         private String getTag(@Nullable String component) {
529             if (component == null) {
530                 component = ActivityThread.currentProcessName();
531             }
532             return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
533         }
534     };
535 
536     /** The singleton IME tracker instance for instrumenting jank metrics. */
537     ImeJankTracker JANK_TRACKER = new ImeJankTracker();
538 
539     /** The singleton IME tracker instance for instrumenting latency metrics. */
540     ImeLatencyTracker LATENCY_TRACKER = new ImeLatencyTracker();
541 
542     /** A token that tracks the progress of an IME request. */
543     final class Token implements Parcelable {
544 
545         /** The binder used to identify this token. */
546         @NonNull
547         private final IBinder mBinder;
548 
549         /** Logging tag, of the shape "component:random_hexadecimal". */
550         @NonNull
551         private final String mTag;
552 
Token(@onNull IBinder binder, @NonNull String tag)553         public Token(@NonNull IBinder binder, @NonNull String tag) {
554             mBinder = binder;
555             mTag = tag;
556         }
557 
Token(@onNull Parcel in)558         private Token(@NonNull Parcel in) {
559             mBinder = in.readStrongBinder();
560             mTag = in.readString8();
561         }
562 
563         @NonNull
getBinder()564         public IBinder getBinder() {
565             return mBinder;
566         }
567 
568         @NonNull
getTag()569         public String getTag() {
570             return mTag;
571         }
572 
573         /** For Parcelable, no special marshalled objects. */
574         @Override
describeContents()575         public int describeContents() {
576             return 0;
577         }
578 
579         @Override
writeToParcel(@onNull Parcel dest, int flags)580         public void writeToParcel(@NonNull Parcel dest, int flags) {
581             dest.writeStrongBinder(mBinder);
582             dest.writeString8(mTag);
583         }
584 
585         @NonNull
586         public static final Creator<Token> CREATOR = new Creator<>() {
587             @NonNull
588             @Override
589             public Token createFromParcel(@NonNull Parcel in) {
590                 return new Token(in);
591             }
592 
593             @NonNull
594             @Override
595             public Token[] newArray(int size) {
596                 return new Token[size];
597             }
598         };
599     }
600 
601     /**
602      * Utilities for mapping IntDef values to their names.
603      *
604      * Note: This is held in a separate class so that it only gets initialized when actually needed.
605      */
606     final class Debug {
607 
608         @NonNull
609         private static final Map<Integer, String> sTypes =
610                 getFieldMapping(ImeTracker.class, "TYPE_");
611         @NonNull
612         private static final Map<Integer, String> sStatus =
613                 getFieldMapping(ImeTracker.class, "STATUS_");
614         @NonNull
615         private static final Map<Integer, String> sOrigins =
616                 getFieldMapping(ImeTracker.class, "ORIGIN_");
617         @NonNull
618         private static final Map<Integer, String> sPhases =
619                 getFieldMapping(ImeTracker.class, "PHASE_");
620 
621         @NonNull
typeToString(@ype int type)622         public static String typeToString(@Type int type) {
623             return sTypes.getOrDefault(type, "TYPE_" + type);
624         }
625 
626         @NonNull
statusToString(@tatus int status)627         public static String statusToString(@Status int status) {
628             return sStatus.getOrDefault(status, "STATUS_" + status);
629         }
630 
631         @NonNull
originToString(@rigin int origin)632         public static String originToString(@Origin int origin) {
633             return sOrigins.getOrDefault(origin, "ORIGIN_" + origin);
634         }
635 
636         @NonNull
phaseToString(@hase int phase)637         public static String phaseToString(@Phase int phase) {
638             return sPhases.getOrDefault(phase, "PHASE_" + phase);
639         }
640 
641         @NonNull
getFieldMapping(Class<?> cls, @NonNull String fieldPrefix)642         private static Map<Integer, String> getFieldMapping(Class<?> cls,
643                 @NonNull String fieldPrefix) {
644             return Arrays.stream(cls.getDeclaredFields())
645                     .filter(field -> field.getName().startsWith(fieldPrefix))
646                     .collect(Collectors.toMap(Debug::getFieldValue, Field::getName));
647         }
648 
getFieldValue(@onNull Field field)649         private static int getFieldValue(@NonNull Field field) {
650             try {
651                 return field.getInt(null);
652             } catch (IllegalAccessException e) {
653                 throw new RuntimeException(e);
654             }
655         }
656     }
657 
658     /**
659      * Context related to {@link InteractionJankMonitor}.
660      */
661     interface InputMethodJankContext {
662         /**
663          * @return a context associated with a display
664          */
getDisplayContext()665         Context getDisplayContext();
666 
667         /**
668          * @return a SurfaceControl that is going to be monitored
669          */
getTargetSurfaceControl()670         SurfaceControl getTargetSurfaceControl();
671 
672         /**
673          * @return the package name of the host
674          */
getHostPackageName()675         String getHostPackageName();
676     }
677 
678     /**
679      * Context related to {@link LatencyTracker}.
680      */
681     interface InputMethodLatencyContext {
682         /**
683          * @return a context associated with current application
684          */
getAppContext()685         Context getAppContext();
686     }
687 
688     /**
689      * A tracker instance which is in charge of communicating with {@link InteractionJankMonitor}.
690      * This class disallows instantiating from outside, use {@link #forJank()} to get the singleton.
691      */
692     final class ImeJankTracker {
693 
694         /**
695          * This class disallows instantiating from outside.
696          */
ImeJankTracker()697         private ImeJankTracker() {
698         }
699 
700         /**
701          * Called when the animation, which is going to be monitored, starts.
702          *
703          * @param jankContext context which is needed by {@link InteractionJankMonitor}.
704          * @param animType the animation type.
705          * @param useSeparatedThread {@code true} if the animation is handled by the app,
706          *                           {@code false} if the animation will be scheduled on the
707          *                           {@link android.view.InsetsAnimationThread}.
708          */
onRequestAnimation(@onNull InputMethodJankContext jankContext, @AnimationType int animType, boolean useSeparatedThread)709         public void onRequestAnimation(@NonNull InputMethodJankContext jankContext,
710                 @AnimationType int animType, boolean useSeparatedThread) {
711             final int cujType = getImeInsetsCujFromAnimation(animType);
712             if (jankContext.getDisplayContext() == null
713                     || jankContext.getTargetSurfaceControl() == null
714                     || cujType == -1) {
715                 return;
716             }
717             final Configuration.Builder builder = Configuration.Builder.withSurface(
718                             cujType,
719                             jankContext.getDisplayContext(),
720                             jankContext.getTargetSurfaceControl())
721                     .setTag(String.format(Locale.US, "%d@%d@%s", animType,
722                             useSeparatedThread ? 0 : 1, jankContext.getHostPackageName()));
723             InteractionJankMonitor.getInstance().begin(builder);
724         }
725 
726         /**
727          * Called when the animation, which is going to be monitored, cancels.
728          *
729          * @param animType the animation type.
730          */
onCancelAnimation(@nimationType int animType)731         public void onCancelAnimation(@AnimationType int animType) {
732             final int cujType = getImeInsetsCujFromAnimation(animType);
733             if (cujType == -1) {
734                 InteractionJankMonitor.getInstance().cancel(cujType);
735             }
736         }
737 
738         /**
739          * Called when the animation, which is going to be monitored, ends.
740          *
741          * @param animType the animation type.
742          */
onFinishAnimation(@nimationType int animType)743         public void onFinishAnimation(@AnimationType int animType) {
744             final int cujType = getImeInsetsCujFromAnimation(animType);
745             if (cujType != -1) {
746                 InteractionJankMonitor.getInstance().end(cujType);
747             }
748         }
749 
750         /**
751          * A helper method to translate animation type to CUJ type for IME animations.
752          *
753          * @param animType the animation type.
754          * @return the integer in {@link com.android.internal.jank.InteractionJankMonitor.CujType},
755          * or {@code -1} if the animation type is not supported for tracking yet.
756          */
getImeInsetsCujFromAnimation(@nimationType int animType)757         private static int getImeInsetsCujFromAnimation(@AnimationType int animType) {
758             switch (animType) {
759                 case ANIMATION_TYPE_SHOW:
760                     return CUJ_IME_INSETS_SHOW_ANIMATION;
761                 case ANIMATION_TYPE_HIDE:
762                     return CUJ_IME_INSETS_HIDE_ANIMATION;
763                 default:
764                     return -1;
765             }
766         }
767     }
768 
769     /**
770      * A tracker instance which is in charge of communicating with {@link LatencyTracker}.
771      * This class disallows instantiating from outside, use {@link #forLatency()}
772      * to get the singleton.
773      */
774     final class ImeLatencyTracker {
775 
776         /**
777          * This class disallows instantiating from outside.
778          */
ImeLatencyTracker()779         private ImeLatencyTracker() {
780         }
781 
shouldMonitorLatency(@oftInputShowHideReason int reason)782         private boolean shouldMonitorLatency(@SoftInputShowHideReason int reason) {
783             return reason == SoftInputShowHideReason.SHOW_SOFT_INPUT
784                     || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT
785                     || reason == SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API
786                     || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API
787                     || reason == SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME
788                     || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME;
789         }
790 
onRequestShow(@ullable Token token, @Origin int origin, @SoftInputShowHideReason int reason, @NonNull InputMethodLatencyContext latencyContext)791         public void onRequestShow(@Nullable Token token, @Origin int origin,
792                 @SoftInputShowHideReason int reason,
793                 @NonNull InputMethodLatencyContext latencyContext) {
794             if (!shouldMonitorLatency(reason)) return;
795             LatencyTracker.getInstance(latencyContext.getAppContext())
796                     .onActionStart(
797                             ACTION_REQUEST_IME_SHOWN,
798                             softInputDisplayReasonToString(reason));
799         }
800 
onRequestHide(@ullable Token token, @Origin int origin, @SoftInputShowHideReason int reason, @NonNull InputMethodLatencyContext latencyContext)801         public void onRequestHide(@Nullable Token token, @Origin int origin,
802                 @SoftInputShowHideReason int reason,
803                 @NonNull InputMethodLatencyContext latencyContext) {
804             if (!shouldMonitorLatency(reason)) return;
805             LatencyTracker.getInstance(latencyContext.getAppContext())
806                     .onActionStart(
807                             ACTION_REQUEST_IME_HIDDEN,
808                             softInputDisplayReasonToString(reason));
809         }
810 
onShowFailed(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)811         public void onShowFailed(@Nullable Token token, @Phase int phase,
812                 @NonNull InputMethodLatencyContext latencyContext) {
813             onShowCancelled(token, phase, latencyContext);
814         }
815 
onHideFailed(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)816         public void onHideFailed(@Nullable Token token, @Phase int phase,
817                 @NonNull InputMethodLatencyContext latencyContext) {
818             onHideCancelled(token, phase, latencyContext);
819         }
820 
onShowCancelled(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)821         public void onShowCancelled(@Nullable Token token, @Phase int phase,
822                 @NonNull InputMethodLatencyContext latencyContext) {
823             LatencyTracker.getInstance(latencyContext.getAppContext())
824                     .onActionCancel(ACTION_REQUEST_IME_SHOWN);
825         }
826 
onHideCancelled(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)827         public void onHideCancelled(@Nullable Token token, @Phase int phase,
828                 @NonNull InputMethodLatencyContext latencyContext) {
829             LatencyTracker.getInstance(latencyContext.getAppContext())
830                     .onActionCancel(ACTION_REQUEST_IME_HIDDEN);
831         }
832 
onShown(@ullable Token token, @NonNull InputMethodLatencyContext latencyContext)833         public void onShown(@Nullable Token token,
834                 @NonNull InputMethodLatencyContext latencyContext) {
835             LatencyTracker.getInstance(latencyContext.getAppContext())
836                     .onActionEnd(ACTION_REQUEST_IME_SHOWN);
837         }
838 
onHidden(@ullable Token token, @NonNull InputMethodLatencyContext latencyContext)839         public void onHidden(@Nullable Token token,
840                 @NonNull InputMethodLatencyContext latencyContext) {
841             LatencyTracker.getInstance(latencyContext.getAppContext())
842                     .onActionEnd(ACTION_REQUEST_IME_HIDDEN);
843         }
844     }
845 }
846