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