• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Cuj.CUJ_IME_INSETS_HIDE_ANIMATION;
24 import static com.android.internal.jank.Cuj.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.content.Context;
32 import android.os.Binder;
33 import android.os.IBinder;
34 import android.os.Parcel;
35 import android.os.Parcelable;
36 import android.os.Process;
37 import android.os.SystemProperties;
38 import android.util.Log;
39 import android.view.InsetsController.AnimationType;
40 import android.view.SurfaceControl;
41 import android.view.View;
42 
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.annotations.VisibleForTesting.Visibility;
45 import com.android.internal.inputmethod.InputMethodDebug;
46 import com.android.internal.inputmethod.SoftInputShowHideReason;
47 import com.android.internal.jank.InteractionJankMonitor;
48 import com.android.internal.jank.InteractionJankMonitor.Configuration;
49 import com.android.internal.util.LatencyTracker;
50 
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.lang.reflect.Field;
54 import java.util.Arrays;
55 import java.util.Locale;
56 import java.util.Map;
57 import java.util.concurrent.ThreadLocalRandom;
58 import java.util.stream.Collectors;
59 
60 /** @hide */
61 public interface ImeTracker {
62 
63     String TAG = "ImeTracker";
64 
65     /** The debug flag for IME visibility event log. */
66     boolean DEBUG_IME_VISIBILITY = SystemProperties.getBoolean("persist.debug.imf_event", false);
67 
68     /** The message to indicate if there is no valid {@link Token}. */
69     String TOKEN_NONE = "TOKEN_NONE";
70 
71     /** The type of the IME request. */
72     @IntDef(prefix = { "TYPE_" }, value = {
73             TYPE_SHOW,
74             TYPE_HIDE,
75             TYPE_USER,
76     })
77     @Retention(RetentionPolicy.SOURCE)
78     @interface Type {}
79 
80     /**
81      * IME show request type.
82      *
83      * @see android.view.InsetsController#ANIMATION_TYPE_SHOW
84      */
85     int TYPE_SHOW = ImeProtoEnums.TYPE_SHOW;
86 
87     /**
88      * IME hide request type.
89      *
90      * @see android.view.InsetsController#ANIMATION_TYPE_HIDE
91      */
92     int TYPE_HIDE = ImeProtoEnums.TYPE_HIDE;
93 
94     /**
95      * IME user-controlled animation request type.
96      *
97      * @see android.view.InsetsController#ANIMATION_TYPE_USER
98      */
99     int TYPE_USER = ImeProtoEnums.TYPE_USER;
100 
101     /** The status of the IME request. */
102     @IntDef(prefix = { "STATUS_" }, value = {
103             STATUS_RUN,
104             STATUS_CANCEL,
105             STATUS_FAIL,
106             STATUS_SUCCESS,
107             STATUS_TIMEOUT,
108     })
109     @Retention(RetentionPolicy.SOURCE)
110     @interface Status {}
111 
112     /** IME request running. */
113     int STATUS_RUN = ImeProtoEnums.STATUS_RUN;
114 
115     /** IME request cancelled. */
116     int STATUS_CANCEL = ImeProtoEnums.STATUS_CANCEL;
117 
118     /** IME request failed. */
119     int STATUS_FAIL = ImeProtoEnums.STATUS_FAIL;
120 
121     /** IME request succeeded. */
122     int STATUS_SUCCESS = ImeProtoEnums.STATUS_SUCCESS;
123 
124     /** IME request timed out. */
125     int STATUS_TIMEOUT = ImeProtoEnums.STATUS_TIMEOUT;
126 
127     /**
128      * The origin of the IME request
129      *
130      * <p> The name follows the format {@code ORIGIN_x_...} where {@code x} denotes
131      * where the origin is (i.e. {@code ORIGIN_SERVER} occurs in the server).
132      */
133     @IntDef(prefix = { "ORIGIN_" }, value = {
134             ORIGIN_CLIENT,
135             ORIGIN_SERVER,
136             ORIGIN_IME,
137     })
138     @Retention(RetentionPolicy.SOURCE)
139     @interface Origin {}
140 
141     /** The IME request originated in the client. */
142     int ORIGIN_CLIENT = ImeProtoEnums.ORIGIN_CLIENT;
143 
144     /** The IME request originated in the server. */
145     int ORIGIN_SERVER = ImeProtoEnums.ORIGIN_SERVER;
146 
147     /** The IME request originated in the IME. */
148     int ORIGIN_IME = ImeProtoEnums.ORIGIN_IME;
149     /** The IME request originated in the WindowManager Shell. */
150     int ORIGIN_WM_SHELL = ImeProtoEnums.ORIGIN_WM_SHELL;
151 
152     /**
153      * The current phase of the IME request.
154      *
155      * <p> The name follows the format {@code PHASE_x_...} where {@code x} denotes
156      * where the phase is (i.e. {@code PHASE_SERVER_...} occurs in the server).
157      */
158     @IntDef(prefix = { "PHASE_" }, value = {
159             PHASE_NOT_SET,
160             PHASE_CLIENT_VIEW_SERVED,
161             PHASE_SERVER_CLIENT_KNOWN,
162             PHASE_SERVER_CLIENT_FOCUSED,
163             PHASE_SERVER_ACCESSIBILITY,
164             PHASE_SERVER_SYSTEM_READY,
165             PHASE_SERVER_HIDE_IMPLICIT,
166             PHASE_SERVER_HIDE_NOT_ALWAYS,
167             PHASE_SERVER_WAIT_IME,
168             PHASE_SERVER_HAS_IME,
169             PHASE_SERVER_SHOULD_HIDE,
170             PHASE_IME_WRAPPER,
171             PHASE_IME_WRAPPER_DISPATCH,
172             PHASE_IME_SHOW_SOFT_INPUT,
173             PHASE_IME_HIDE_SOFT_INPUT,
174             PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE,
175             PHASE_SERVER_APPLY_IME_VISIBILITY,
176             PHASE_WM_SHOW_IME_RUNNER,
177             PHASE_WM_SHOW_IME_READY,
178             PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET,
179             PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS,
180             PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS,
181             PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS,
182             PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS,
183             PHASE_WM_REMOTE_INSETS_CONTROLLER,
184             PHASE_WM_ANIMATION_CREATE,
185             PHASE_WM_ANIMATION_RUNNING,
186             PHASE_CLIENT_SHOW_INSETS,
187             PHASE_CLIENT_HIDE_INSETS,
188             PHASE_CLIENT_HANDLE_SHOW_INSETS,
189             PHASE_CLIENT_HANDLE_HIDE_INSETS,
190             PHASE_CLIENT_APPLY_ANIMATION,
191             PHASE_CLIENT_CONTROL_ANIMATION,
192             PHASE_CLIENT_COLLECT_SOURCE_CONTROLS,
193             PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW,
194             PHASE_CLIENT_REQUEST_IME_SHOW,
195             PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN,
196             PHASE_CLIENT_ANIMATION_RUNNING,
197             PHASE_CLIENT_ANIMATION_CANCEL,
198             PHASE_CLIENT_ANIMATION_FINISHED_SHOW,
199             PHASE_CLIENT_ANIMATION_FINISHED_HIDE,
200             PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT,
201             PHASE_IME_SHOW_WINDOW,
202             PHASE_IME_HIDE_WINDOW,
203             PHASE_IME_PRIVILEGED_OPERATIONS,
204             PHASE_SERVER_CURRENT_ACTIVE_IME,
205     })
206     @Retention(RetentionPolicy.SOURCE)
207     @interface Phase {}
208 
209     int PHASE_NOT_SET = ImeProtoEnums.PHASE_NOT_SET;
210 
211     /** The view that requested the IME has been served by the IMM. */
212     int PHASE_CLIENT_VIEW_SERVED = ImeProtoEnums.PHASE_CLIENT_VIEW_SERVED;
213 
214     /** The IME client that requested the IME has window manager focus. */
215     int PHASE_SERVER_CLIENT_KNOWN = ImeProtoEnums.PHASE_SERVER_CLIENT_KNOWN;
216 
217     /** The IME client that requested the IME has IME focus. */
218     int PHASE_SERVER_CLIENT_FOCUSED = ImeProtoEnums.PHASE_SERVER_CLIENT_FOCUSED;
219 
220     /** The IME request complies with the current accessibility settings. */
221     int PHASE_SERVER_ACCESSIBILITY = ImeProtoEnums.PHASE_SERVER_ACCESSIBILITY;
222 
223     /** The server is ready to run third party code. */
224     int PHASE_SERVER_SYSTEM_READY = ImeProtoEnums.PHASE_SERVER_SYSTEM_READY;
225 
226     /** Checked the implicit hide request against any explicit show requests. */
227     int PHASE_SERVER_HIDE_IMPLICIT = ImeProtoEnums.PHASE_SERVER_HIDE_IMPLICIT;
228 
229     /** Checked the not-always hide request against any forced show requests. */
230     int PHASE_SERVER_HIDE_NOT_ALWAYS = ImeProtoEnums.PHASE_SERVER_HIDE_NOT_ALWAYS;
231 
232     /** The server is waiting for a connection to the IME. */
233     int PHASE_SERVER_WAIT_IME = ImeProtoEnums.PHASE_SERVER_WAIT_IME;
234 
235     /** The server has a connection to the IME. */
236     int PHASE_SERVER_HAS_IME = ImeProtoEnums.PHASE_SERVER_HAS_IME;
237 
238     /** The server decided the IME should be hidden. */
239     int PHASE_SERVER_SHOULD_HIDE = ImeProtoEnums.PHASE_SERVER_SHOULD_HIDE;
240 
241     /** Reached the IME wrapper. */
242     int PHASE_IME_WRAPPER = ImeProtoEnums.PHASE_IME_WRAPPER;
243 
244     /** Dispatched from the IME wrapper to the IME. */
245     int PHASE_IME_WRAPPER_DISPATCH = ImeProtoEnums.PHASE_IME_WRAPPER_DISPATCH;
246 
247     /** Reached the IME's showSoftInput method. */
248     int PHASE_IME_SHOW_SOFT_INPUT = ImeProtoEnums.PHASE_IME_SHOW_SOFT_INPUT;
249 
250     /** Reached the IME's hideSoftInput method. */
251     int PHASE_IME_HIDE_SOFT_INPUT = ImeProtoEnums.PHASE_IME_HIDE_SOFT_INPUT;
252 
253     /** The server decided the IME should be shown. */
254     int PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE = ImeProtoEnums.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE;
255 
256     /** Applied the IME visibility. */
257     int PHASE_SERVER_APPLY_IME_VISIBILITY = ImeProtoEnums.PHASE_SERVER_APPLY_IME_VISIBILITY;
258 
259     /** Started the show IME runner. */
260     int PHASE_WM_SHOW_IME_RUNNER = ImeProtoEnums.PHASE_WM_SHOW_IME_RUNNER;
261 
262     /** Ready to show IME. */
263     int PHASE_WM_SHOW_IME_READY = ImeProtoEnums.PHASE_WM_SHOW_IME_READY;
264 
265     /** The Window Manager has a connection to the IME insets control target. */
266     int PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET =
267             ImeProtoEnums.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET;
268 
269     /** Reached the window insets control target's show insets method. */
270     int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS =
271             ImeProtoEnums.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS;
272 
273     /** Reached the window insets control target's hide insets method. */
274     int PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS =
275             ImeProtoEnums.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS;
276 
277     /** Reached the remote insets control target's show insets method. */
278     int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS =
279             ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SHOW_INSETS;
280 
281     /** Reached the remote insets control target's hide insets method. */
282     int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS =
283             ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_HIDE_INSETS;
284 
285     /** Reached the remote insets controller. */
286     int PHASE_WM_REMOTE_INSETS_CONTROLLER = ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROLLER;
287 
288     /** Created the IME window insets show animation. */
289     int PHASE_WM_ANIMATION_CREATE = ImeProtoEnums.PHASE_WM_ANIMATION_CREATE;
290 
291     /** Started the IME window insets show animation. */
292     int PHASE_WM_ANIMATION_RUNNING = ImeProtoEnums.PHASE_WM_ANIMATION_RUNNING;
293 
294     /** Reached the client's show insets method. */
295     int PHASE_CLIENT_SHOW_INSETS = ImeProtoEnums.PHASE_CLIENT_SHOW_INSETS;
296 
297     /** Reached the client's hide insets method. */
298     int PHASE_CLIENT_HIDE_INSETS = ImeProtoEnums.PHASE_CLIENT_HIDE_INSETS;
299 
300     /** Handling the IME window insets show request. */
301     int PHASE_CLIENT_HANDLE_SHOW_INSETS = ImeProtoEnums.PHASE_CLIENT_HANDLE_SHOW_INSETS;
302 
303     /** Handling the IME window insets hide request. */
304     int PHASE_CLIENT_HANDLE_HIDE_INSETS = ImeProtoEnums.PHASE_CLIENT_HANDLE_HIDE_INSETS;
305 
306     /** Applied the IME window insets show animation. */
307     int PHASE_CLIENT_APPLY_ANIMATION = ImeProtoEnums.PHASE_CLIENT_APPLY_ANIMATION;
308 
309     /** Started the IME window insets show animation. */
310     int PHASE_CLIENT_CONTROL_ANIMATION = ImeProtoEnums.PHASE_CLIENT_CONTROL_ANIMATION;
311 
312     /** Collecting insets source controls. */
313     int PHASE_CLIENT_COLLECT_SOURCE_CONTROLS = ImeProtoEnums.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS;
314 
315     /** Reached the insets source consumer's show request method. */
316     int PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW =
317             ImeProtoEnums.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW;
318 
319     /** Reached input method manager's request IME show method. */
320     int PHASE_CLIENT_REQUEST_IME_SHOW = ImeProtoEnums.PHASE_CLIENT_REQUEST_IME_SHOW;
321 
322     /** Reached the insets source consumer's notify hidden method. */
323     int PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN =
324             ImeProtoEnums.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN;
325 
326     /** Queued the IME window insets show animation. */
327     int PHASE_CLIENT_ANIMATION_RUNNING = ImeProtoEnums.PHASE_CLIENT_ANIMATION_RUNNING;
328 
329     /** Cancelled the IME window insets show animation. */
330     int PHASE_CLIENT_ANIMATION_CANCEL = ImeProtoEnums.PHASE_CLIENT_ANIMATION_CANCEL;
331 
332     /** Finished the IME window insets show animation. */
333     int PHASE_CLIENT_ANIMATION_FINISHED_SHOW = ImeProtoEnums.PHASE_CLIENT_ANIMATION_FINISHED_SHOW;
334 
335     /** Finished the IME window insets hide animation. */
336     int PHASE_CLIENT_ANIMATION_FINISHED_HIDE = ImeProtoEnums.PHASE_CLIENT_ANIMATION_FINISHED_HIDE;
337 
338     /** Aborted the request to show the IME post layout. */
339     int PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT =
340             ImeProtoEnums.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT;
341 
342     /** Reached the IME's showWindow method. */
343     int PHASE_IME_SHOW_WINDOW = ImeProtoEnums.PHASE_IME_SHOW_WINDOW;
344 
345     /** Reached the IME's hideWindow method. */
346     int PHASE_IME_HIDE_WINDOW = ImeProtoEnums.PHASE_IME_HIDE_WINDOW;
347 
348     /** Reached the InputMethodPrivilegedOperations handler. */
349     int PHASE_IME_PRIVILEGED_OPERATIONS = ImeProtoEnums.PHASE_IME_PRIVILEGED_OPERATIONS;
350 
351     /** Checked that the calling IME is the currently active IME. */
352     int PHASE_SERVER_CURRENT_ACTIVE_IME = ImeProtoEnums.PHASE_SERVER_CURRENT_ACTIVE_IME;
353 
354     /**
355      * Called when an IME request is started.
356      *
357      * @param component the name of the component that started the request.
358      * @param uid the uid of the client that started the request.
359      * @param type the type of the request.
360      * @param origin the origin of the request.
361      * @param reason the reason for starting the request.
362      * @param fromUser whether this request was created directly from user interaction.
363      *
364      * @return An IME request tracking token.
365      */
366     @NonNull
onStart(@onNull String component, int uid, @Type int type, @Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser)367     Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
368             @SoftInputShowHideReason int reason, boolean fromUser);
369 
370     /**
371      * Called when an IME request is started for the current process.
372      *
373      * @param type the type of the request.
374      * @param origin the origin of the request.
375      * @param reason the reason for starting the request.
376      * @param fromUser whether this request was created directly from user interaction.
377      *
378      * @return An IME request tracking token.
379      */
380     @NonNull
onStart(@ype int type, @Origin int origin, @SoftInputShowHideReason int reason, boolean fromUser)381     default Token onStart(@Type int type, @Origin int origin, @SoftInputShowHideReason int reason,
382             boolean fromUser) {
383         return onStart(Process.myProcessName(), Process.myUid(), type, origin, reason, fromUser);
384     }
385 
386     /**
387      * Called when an IME request progresses to a further phase.
388      *
389      * @param token the token tracking the current IME request or {@code null} otherwise.
390      * @param phase the new phase the IME request reached.
391      */
onProgress(@ullable Token token, @Phase int phase)392     void onProgress(@Nullable Token token, @Phase int phase);
393 
394     /**
395      * Called when an IME request fails.
396      *
397      * @param token the token tracking the current IME request or {@code null} otherwise.
398      * @param phase the phase the IME request failed at.
399      */
onFailed(@ullable Token token, @Phase int phase)400     void onFailed(@Nullable Token token, @Phase int phase);
401 
402     /**
403      * Called when an IME request reached a flow that is not yet implemented.
404      *
405      * @param token the token tracking the current IME request or {@code null} otherwise.
406      * @param phase the phase the IME request was currently at.
407      */
onTodo(@ullable Token token, @Phase int phase)408     void onTodo(@Nullable Token token, @Phase int phase);
409 
410     /**
411      * Called when an IME request is cancelled.
412      *
413      * @param token the token tracking the current IME request or {@code null} otherwise.
414      * @param phase the phase the IME request was cancelled at.
415      */
onCancelled(@ullable Token token, @Phase int phase)416     void onCancelled(@Nullable Token token, @Phase int phase);
417 
418     /**
419      * Called when the show IME request is successful.
420      *
421      * @param token the token tracking the current IME request or {@code null} otherwise.
422      */
onShown(@ullable Token token)423     void onShown(@Nullable Token token);
424 
425     /**
426      * Called when the hide IME request is successful.
427      *
428      * @param token the token tracking the current IME request or {@code null} otherwise.
429      */
onHidden(@ullable Token token)430     void onHidden(@Nullable Token token);
431 
432     /**
433      * Called when the user-controlled IME request was dispatched to the requesting app. The
434      * user animation can take an undetermined amount of time, so it shouldn't be tracked.
435      *
436      * @param token the token tracking the current IME request or {@code null} otherwise.
437      */
onDispatched(@ullable Token token)438     void onDispatched(@Nullable Token token);
439 
440     /**
441      * Called when the animation of the user-controlled IME request finished.
442      *
443      * @param token the token tracking the current IME request or {@code null} otherwise.
444      * @param shown whether the end state of the animation was shown or hidden.
445      */
onUserFinished(@ullable Token token, boolean shown)446     void onUserFinished(@Nullable Token token, boolean shown);
447 
448     /**
449      * Returns whether the current IME request was created due to a user interaction. This can
450      * only be {@code true} when running on the view's UI thread.
451      *
452      * @param view the view for which the IME was requested.
453      * @return {@code true} if this request is coming from a user interaction,
454      * {@code false} otherwise.
455      */
isFromUser(@ullable View view)456     static boolean isFromUser(@Nullable View view) {
457         if (view == null) {
458             return false;
459         }
460         final var handler = view.getHandler();
461         // Early return if not on the UI thread, to ensure safe access to getViewRootImpl() below.
462         if (handler == null || handler.getLooper() == null
463                 || !handler.getLooper().isCurrentThread()) {
464             return false;
465         }
466         final var viewRootImpl = view.getViewRootImpl();
467         return viewRootImpl != null && viewRootImpl.isHandlingPointerEvent();
468     }
469 
470     /**
471      * Get the singleton request tracker instance.
472      *
473      * @return the singleton request tracker instance
474      */
475     @NonNull
forLogging()476     static ImeTracker forLogging() {
477         return LOGGER;
478     }
479 
480     /**
481      * Get the singleton jank tracker instance.
482      *
483      * @return the singleton jank tracker instance
484      */
485     @NonNull
forJank()486     static ImeJankTracker forJank() {
487         return JANK_TRACKER;
488     }
489 
490     /**
491      * Get the singleton latency tracker instance.
492      *
493      * @return the singleton latency tracker instance
494      */
495     @NonNull
forLatency()496     static ImeLatencyTracker forLatency() {
497         return LATENCY_TRACKER;
498     }
499 
500     /** The singleton IME tracker instance. */
501     @NonNull
502     ImeTracker LOGGER = new ImeTracker() {
503 
504         {
505             // Read initial system properties.
506             reloadSystemProperties();
507             // Update when system properties change.
508             SystemProperties.addChangeCallback(this::reloadSystemProperties);
509         }
510 
511         /** Whether {@link #onProgress} calls should be logged. */
512         private boolean mLogProgress;
513 
514         /** Whether the stack trace at the request call site should be logged. */
515         private boolean mLogStackTrace;
516 
517         @NonNull
518         @Override
519         public Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
520                 @SoftInputShowHideReason int reason, boolean fromUser) {
521             final var tag = Token.createTag(component);
522             final var token = IInputMethodManagerGlobalInvoker.onStart(tag, uid, type,
523                     origin, reason, fromUser);
524 
525             Log.i(TAG, token.mTag + ": " + getOnStartPrefix(type)
526                     + " at " + Debug.originToString(origin)
527                     + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
528                     + " fromUser " + fromUser,
529                     mLogStackTrace ? new Throwable() : null);
530             return token;
531         }
532 
533         @Override
534         public void onProgress(@Nullable Token token, @Phase int phase) {
535             if (token == null) return;
536             IInputMethodManagerGlobalInvoker.onProgress(token.mBinder, phase);
537 
538             if (mLogProgress) {
539                 Log.i(TAG, token.mTag + ": onProgress at " + Debug.phaseToString(phase));
540             }
541         }
542 
543         @Override
544         public void onFailed(@Nullable Token token, @Phase int phase) {
545             if (token == null) return;
546             IInputMethodManagerGlobalInvoker.onFailed(token, phase);
547 
548             Log.i(TAG, token.mTag + ": onFailed at " + Debug.phaseToString(phase));
549         }
550 
551         @Override
552         public void onTodo(@Nullable Token token, @Phase int phase) {
553             if (token == null) return;
554             Log.i(TAG, token.mTag + ": onTodo at " + Debug.phaseToString(phase));
555         }
556 
557         @Override
558         public void onCancelled(@Nullable Token token, @Phase int phase) {
559             if (token == null) return;
560             IInputMethodManagerGlobalInvoker.onCancelled(token, phase);
561 
562             Log.i(TAG, token.mTag + ": onCancelled at " + Debug.phaseToString(phase));
563         }
564 
565         @Override
566         public void onShown(@Nullable Token token) {
567             if (token == null) return;
568             IInputMethodManagerGlobalInvoker.onShown(token);
569 
570             Log.i(TAG, token.mTag + ": onShown");
571         }
572 
573         @Override
574         public void onHidden(@Nullable Token token) {
575             if (token == null) return;
576             IInputMethodManagerGlobalInvoker.onHidden(token);
577 
578             Log.i(TAG, token.mTag + ": onHidden");
579         }
580 
581         @Override
582         public void onDispatched(@Nullable Token token) {
583             if (token == null) return;
584             IInputMethodManagerGlobalInvoker.onDispatched(token);
585 
586             Log.i(TAG, token.mTag + ": onDispatched");
587         }
588 
589         @Override
590         public void onUserFinished(@Nullable Token token, boolean shown) {
591             if (token == null) return;
592             // This is already sent to ImeTrackerService to mark it finished during onDispatched.
593 
594             Log.i(TAG, token.mTag + ": onUserFinished " + (shown ? "shown" : "hidden"));
595         }
596 
597         /**
598          * Gets the prefix string for {@link #onStart} based on the given request type.
599          *
600          * @param type request type for which to create the prefix string with.
601          */
602         @NonNull
603         private static String getOnStartPrefix(@Type int type) {
604             return switch (type) {
605                 case TYPE_SHOW -> "onRequestShow";
606                 case TYPE_HIDE -> "onRequestHide";
607                 case TYPE_USER -> "onRequestUser";
608                 default -> "onRequestUnknown";
609             };
610         }
611 
612         /** Reloads the system properties related to this class. */
613         private void reloadSystemProperties() {
614             mLogProgress = SystemProperties.getBoolean(
615                     "persist.debug.imetracker", false);
616             mLogStackTrace = SystemProperties.getBoolean(
617                     "persist.debug.imerequest.logstacktrace", false);
618         }
619     };
620 
621     /** The singleton IME tracker instance for instrumenting jank metrics. */
622     ImeJankTracker JANK_TRACKER = new ImeJankTracker();
623 
624     /** The singleton IME tracker instance for instrumenting latency metrics. */
625     ImeLatencyTracker LATENCY_TRACKER = new ImeLatencyTracker();
626 
627     /** A token that tracks the progress of an IME request. */
628     final class Token implements Parcelable {
629 
630         /** Empty binder, lazily initialized, used for empty token instantiation. */
631         @Nullable
632         private static IBinder sEmptyBinder;
633 
634         /** The binder used to identify this token. */
635         @NonNull
636         private final IBinder mBinder;
637 
638         /** Logging tag, of the shape "component:random_hexadecimal". */
639         @NonNull
640         private final String mTag;
641 
Token(@onNull IBinder binder, @NonNull String tag)642         public Token(@NonNull IBinder binder, @NonNull String tag) {
643             mBinder = binder;
644             mTag = tag;
645         }
646 
Token(@onNull Parcel in)647         private Token(@NonNull Parcel in) {
648             mBinder = in.readStrongBinder();
649             mTag = in.readString8();
650         }
651 
652         /** Returns the binder used to identify this token. */
653         @NonNull
getBinder()654         public IBinder getBinder() {
655             return mBinder;
656         }
657 
658         /** Returns the logging tag of this token. */
659         @NonNull
getTag()660         public String getTag() {
661             return mTag;
662         }
663 
664         /**
665          * Creates a logging tag.
666          *
667          * @param component the name of the component that created the IME request.
668          */
669         @NonNull
createTag(@onNull String component)670         private static String createTag(@NonNull String component) {
671             return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
672         }
673 
674         /** Returns a new token with an empty binder. */
675         @NonNull
676         @VisibleForTesting(visibility = Visibility.PACKAGE)
empty()677         public static Token empty() {
678             final var tag = createTag(Process.myProcessName());
679             return empty(tag);
680         }
681 
682         /** Returns a new token with an empty binder and the given logging tag. */
683         @NonNull
empty(@onNull String tag)684         static Token empty(@NonNull String tag) {
685             return new Token(getEmptyBinder(), tag);
686         }
687 
688         /** Returns the empty binder instance for empty token creation, lazily initializing it. */
689         @NonNull
getEmptyBinder()690         private static IBinder getEmptyBinder() {
691             if (sEmptyBinder == null) {
692                 sEmptyBinder = new Binder();
693             }
694             return sEmptyBinder;
695         }
696 
697         @Override
toString()698         public String toString() {
699             return super.toString() + "(tag: " + mTag + ")";
700         }
701 
702         /** For Parcelable, no special marshalled objects. */
703         @Override
describeContents()704         public int describeContents() {
705             return 0;
706         }
707 
708         @Override
writeToParcel(@onNull Parcel dest, int flags)709         public void writeToParcel(@NonNull Parcel dest, int flags) {
710             dest.writeStrongBinder(mBinder);
711             dest.writeString8(mTag);
712         }
713 
714         @NonNull
715         public static final Creator<Token> CREATOR = new Creator<>() {
716             @NonNull
717             @Override
718             public Token createFromParcel(@NonNull Parcel in) {
719                 return new Token(in);
720             }
721 
722             @NonNull
723             @Override
724             public Token[] newArray(int size) {
725                 return new Token[size];
726             }
727         };
728     }
729 
730     /**
731      * Utilities for mapping IntDef values to their names.
732      *
733      * Note: This is held in a separate class so that it only gets initialized when actually needed.
734      */
735     final class Debug {
736 
737         @NonNull
738         private static final Map<Integer, String> sTypes =
739                 getFieldMapping(ImeTracker.class, "TYPE_");
740         @NonNull
741         private static final Map<Integer, String> sStatus =
742                 getFieldMapping(ImeTracker.class, "STATUS_");
743         @NonNull
744         private static final Map<Integer, String> sOrigins =
745                 getFieldMapping(ImeTracker.class, "ORIGIN_");
746         @NonNull
747         private static final Map<Integer, String> sPhases =
748                 getFieldMapping(ImeTracker.class, "PHASE_");
749 
750         @NonNull
typeToString(@ype int type)751         public static String typeToString(@Type int type) {
752             return sTypes.getOrDefault(type, "TYPE_" + type);
753         }
754 
755         @NonNull
statusToString(@tatus int status)756         public static String statusToString(@Status int status) {
757             return sStatus.getOrDefault(status, "STATUS_" + status);
758         }
759 
760         @NonNull
originToString(@rigin int origin)761         public static String originToString(@Origin int origin) {
762             return sOrigins.getOrDefault(origin, "ORIGIN_" + origin);
763         }
764 
765         @NonNull
phaseToString(@hase int phase)766         public static String phaseToString(@Phase int phase) {
767             return sPhases.getOrDefault(phase, "PHASE_" + phase);
768         }
769 
770         @NonNull
getFieldMapping(Class<?> cls, @NonNull String fieldPrefix)771         private static Map<Integer, String> getFieldMapping(Class<?> cls,
772                 @NonNull String fieldPrefix) {
773             return Arrays.stream(cls.getDeclaredFields())
774                     .filter(field -> field.getName().startsWith(fieldPrefix))
775                     .collect(Collectors.toMap(Debug::getFieldValue, Field::getName));
776         }
777 
getFieldValue(@onNull Field field)778         private static int getFieldValue(@NonNull Field field) {
779             try {
780                 return field.getInt(null);
781             } catch (IllegalAccessException e) {
782                 throw new RuntimeException(e);
783             }
784         }
785     }
786 
787     /**
788      * Context related to {@link InteractionJankMonitor}.
789      */
790     interface InputMethodJankContext {
791         /**
792          * @return a context associated with a display
793          */
getDisplayContext()794         Context getDisplayContext();
795 
796         /**
797          * @return a SurfaceControl that is going to be monitored
798          */
getTargetSurfaceControl()799         SurfaceControl getTargetSurfaceControl();
800 
801         /**
802          * @return the package name of the host
803          */
getHostPackageName()804         String getHostPackageName();
805     }
806 
807     /**
808      * Context related to {@link LatencyTracker}.
809      */
810     interface InputMethodLatencyContext {
811         /**
812          * @return a context associated with current application
813          */
getAppContext()814         Context getAppContext();
815     }
816 
817     /**
818      * A tracker instance which is in charge of communicating with {@link InteractionJankMonitor}.
819      * This class disallows instantiating from outside, use {@link #forJank()} to get the singleton.
820      */
821     final class ImeJankTracker {
822 
823         /**
824          * This class disallows instantiating from outside.
825          */
ImeJankTracker()826         private ImeJankTracker() {
827         }
828 
829         /**
830          * Called when the animation, which is going to be monitored, starts.
831          *
832          * @param jankContext context which is needed by {@link InteractionJankMonitor}.
833          * @param animType the animation type.
834          * @param useSeparatedThread {@code true} if the animation is handled by the app,
835          *                           {@code false} if the animation will be scheduled on the
836          *                           {@link android.view.InsetsAnimationThread}.
837          */
onRequestAnimation(@onNull InputMethodJankContext jankContext, @AnimationType int animType, boolean useSeparatedThread)838         public void onRequestAnimation(@NonNull InputMethodJankContext jankContext,
839                 @AnimationType int animType, boolean useSeparatedThread) {
840             final int cujType = getImeInsetsCujFromAnimation(animType);
841             if (jankContext.getDisplayContext() == null
842                     || jankContext.getTargetSurfaceControl() == null
843                     || cujType == -1) {
844                 return;
845             }
846             final Configuration.Builder builder = Configuration.Builder.withSurface(
847                             cujType,
848                             jankContext.getDisplayContext(),
849                             jankContext.getTargetSurfaceControl())
850                     .setTag(String.format(Locale.US, "%d@%d@%s", animType,
851                             useSeparatedThread ? 0 : 1, jankContext.getHostPackageName()));
852             InteractionJankMonitor.getInstance().begin(builder);
853         }
854 
855         /**
856          * Called when the animation, which is going to be monitored, cancels.
857          *
858          * @param animType the animation type.
859          */
onCancelAnimation(@nimationType int animType)860         public void onCancelAnimation(@AnimationType int animType) {
861             final int cujType = getImeInsetsCujFromAnimation(animType);
862             if (cujType != -1) {
863                 InteractionJankMonitor.getInstance().cancel(cujType);
864             }
865         }
866 
867         /**
868          * Called when the animation, which is going to be monitored, ends.
869          *
870          * @param animType the animation type.
871          */
onFinishAnimation(@nimationType int animType)872         public void onFinishAnimation(@AnimationType int animType) {
873             final int cujType = getImeInsetsCujFromAnimation(animType);
874             if (cujType != -1) {
875                 InteractionJankMonitor.getInstance().end(cujType);
876             }
877         }
878 
879         /**
880          * A helper method to translate animation type to CUJ type for IME animations.
881          *
882          * @param animType the animation type.
883          * @return the integer in {@link com.android.internal.jank.Cuj.CujType},
884          * or {@code -1} if the animation type is not supported for tracking yet.
885          */
getImeInsetsCujFromAnimation(@nimationType int animType)886         private static int getImeInsetsCujFromAnimation(@AnimationType int animType) {
887             switch (animType) {
888                 case ANIMATION_TYPE_SHOW:
889                     return CUJ_IME_INSETS_SHOW_ANIMATION;
890                 case ANIMATION_TYPE_HIDE:
891                     return CUJ_IME_INSETS_HIDE_ANIMATION;
892                 default:
893                     return -1;
894             }
895         }
896     }
897 
898     /**
899      * A tracker instance which is in charge of communicating with {@link LatencyTracker}.
900      * This class disallows instantiating from outside, use {@link #forLatency()}
901      * to get the singleton.
902      */
903     final class ImeLatencyTracker {
904 
905         /**
906          * This class disallows instantiating from outside.
907          */
ImeLatencyTracker()908         private ImeLatencyTracker() {
909         }
910 
shouldMonitorLatency(@oftInputShowHideReason int reason)911         private boolean shouldMonitorLatency(@SoftInputShowHideReason int reason) {
912             return reason == SoftInputShowHideReason.SHOW_SOFT_INPUT
913                     || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT
914                     || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW
915                     || reason == SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API
916                     || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API
917                     || reason == SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME
918                     || reason == SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME;
919         }
920 
onRequestShow(@ullable Token token, @Origin int origin, @SoftInputShowHideReason int reason, @NonNull InputMethodLatencyContext latencyContext)921         public void onRequestShow(@Nullable Token token, @Origin int origin,
922                 @SoftInputShowHideReason int reason,
923                 @NonNull InputMethodLatencyContext latencyContext) {
924             if (!shouldMonitorLatency(reason)) return;
925             LatencyTracker.getInstance(latencyContext.getAppContext())
926                     .onActionStart(
927                             ACTION_REQUEST_IME_SHOWN,
928                             softInputDisplayReasonToString(reason));
929         }
930 
onRequestHide(@ullable Token token, @Origin int origin, @SoftInputShowHideReason int reason, @NonNull InputMethodLatencyContext latencyContext)931         public void onRequestHide(@Nullable Token token, @Origin int origin,
932                 @SoftInputShowHideReason int reason,
933                 @NonNull InputMethodLatencyContext latencyContext) {
934             if (!shouldMonitorLatency(reason)) return;
935             LatencyTracker.getInstance(latencyContext.getAppContext())
936                     .onActionStart(
937                             ACTION_REQUEST_IME_HIDDEN,
938                             softInputDisplayReasonToString(reason));
939         }
940 
onShowFailed(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)941         public void onShowFailed(@Nullable Token token, @Phase int phase,
942                 @NonNull InputMethodLatencyContext latencyContext) {
943             onShowCancelled(token, phase, latencyContext);
944         }
945 
onHideFailed(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)946         public void onHideFailed(@Nullable Token token, @Phase int phase,
947                 @NonNull InputMethodLatencyContext latencyContext) {
948             onHideCancelled(token, phase, latencyContext);
949         }
950 
onShowCancelled(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)951         public void onShowCancelled(@Nullable Token token, @Phase int phase,
952                 @NonNull InputMethodLatencyContext latencyContext) {
953             LatencyTracker.getInstance(latencyContext.getAppContext())
954                     .onActionCancel(ACTION_REQUEST_IME_SHOWN);
955         }
956 
onHideCancelled(@ullable Token token, @Phase int phase, @NonNull InputMethodLatencyContext latencyContext)957         public void onHideCancelled(@Nullable Token token, @Phase int phase,
958                 @NonNull InputMethodLatencyContext latencyContext) {
959             LatencyTracker.getInstance(latencyContext.getAppContext())
960                     .onActionCancel(ACTION_REQUEST_IME_HIDDEN);
961         }
962 
onShown(@ullable Token token, @NonNull InputMethodLatencyContext latencyContext)963         public void onShown(@Nullable Token token,
964                 @NonNull InputMethodLatencyContext latencyContext) {
965             LatencyTracker.getInstance(latencyContext.getAppContext())
966                     .onActionEnd(ACTION_REQUEST_IME_SHOWN);
967         }
968 
onHidden(@ullable Token token, @NonNull InputMethodLatencyContext latencyContext)969         public void onHidden(@Nullable Token token,
970                 @NonNull InputMethodLatencyContext latencyContext) {
971             LatencyTracker.getInstance(latencyContext.getAppContext())
972                     .onActionEnd(ACTION_REQUEST_IME_HIDDEN);
973         }
974     }
975 }
976