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