1 /* 2 * Copyright (C) 2006 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; 18 19 import static android.view.Display.DEFAULT_DISPLAY; 20 import static android.view.Display.INVALID_DISPLAY; 21 import static android.view.InputDevice.SOURCE_CLASS_NONE; 22 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 23 import static android.view.InsetsState.ITYPE_STATUS_BAR; 24 import static android.view.InsetsState.SIZE; 25 import static android.view.View.PFLAG_DRAW_ANIMATION; 26 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; 27 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 28 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE; 29 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; 30 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 31 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; 32 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; 33 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 34 import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE; 35 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 36 import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; 37 import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; 38 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; 39 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 40 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; 41 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; 42 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; 43 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 44 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; 45 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; 46 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 47 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 48 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 49 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 50 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 51 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 52 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED; 53 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; 54 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED; 55 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; 56 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 57 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; 58 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; 59 import static android.view.WindowManager.LayoutParams.TYPE_TOAST; 60 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; 61 62 import android.Manifest; 63 import android.animation.LayoutTransition; 64 import android.annotation.AnyThread; 65 import android.annotation.NonNull; 66 import android.annotation.Nullable; 67 import android.app.ActivityManager; 68 import android.app.ActivityThread; 69 import android.app.ResourcesManager; 70 import android.compat.annotation.UnsupportedAppUsage; 71 import android.content.ClipData; 72 import android.content.ClipDescription; 73 import android.content.Context; 74 import android.content.pm.ActivityInfo; 75 import android.content.pm.PackageManager; 76 import android.content.res.CompatibilityInfo; 77 import android.content.res.Configuration; 78 import android.content.res.Resources; 79 import android.content.res.TypedArray; 80 import android.graphics.BLASTBufferQueue; 81 import android.graphics.Canvas; 82 import android.graphics.Color; 83 import android.graphics.FrameInfo; 84 import android.graphics.HardwareRenderer.FrameDrawingCallback; 85 import android.graphics.Insets; 86 import android.graphics.Matrix; 87 import android.graphics.PixelFormat; 88 import android.graphics.Point; 89 import android.graphics.PointF; 90 import android.graphics.PorterDuff; 91 import android.graphics.RecordingCanvas; 92 import android.graphics.Rect; 93 import android.graphics.Region; 94 import android.graphics.RenderNode; 95 import android.graphics.drawable.Drawable; 96 import android.hardware.display.DisplayManager; 97 import android.hardware.display.DisplayManager.DisplayListener; 98 import android.hardware.input.InputManager; 99 import android.media.AudioManager; 100 import android.os.Binder; 101 import android.os.Build; 102 import android.os.Bundle; 103 import android.os.Debug; 104 import android.os.Handler; 105 import android.os.IBinder; 106 import android.os.Looper; 107 import android.os.Message; 108 import android.os.ParcelFileDescriptor; 109 import android.os.Process; 110 import android.os.RemoteException; 111 import android.os.SystemClock; 112 import android.os.SystemProperties; 113 import android.os.Trace; 114 import android.os.UserHandle; 115 import android.sysprop.DisplayProperties; 116 import android.util.AndroidRuntimeException; 117 import android.util.DisplayMetrics; 118 import android.util.Log; 119 import android.util.LongArray; 120 import android.util.MergedConfiguration; 121 import android.util.Slog; 122 import android.util.SparseArray; 123 import android.util.TimeUtils; 124 import android.util.TypedValue; 125 import android.view.InputDevice.InputSourceClass; 126 import android.view.InsetsState.InternalInsetsType; 127 import android.view.Surface.OutOfResourcesException; 128 import android.view.SurfaceControl.Transaction; 129 import android.view.View.AttachInfo; 130 import android.view.View.FocusDirection; 131 import android.view.View.MeasureSpec; 132 import android.view.Window.OnContentApplyWindowInsetsListener; 133 import android.view.WindowInsets.Type; 134 import android.view.WindowInsets.Type.InsetsType; 135 import android.view.WindowManager.LayoutParams; 136 import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 137 import android.view.accessibility.AccessibilityEvent; 138 import android.view.accessibility.AccessibilityManager; 139 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; 140 import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener; 141 import android.view.accessibility.AccessibilityNodeIdManager; 142 import android.view.accessibility.AccessibilityNodeInfo; 143 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 144 import android.view.accessibility.AccessibilityNodeProvider; 145 import android.view.accessibility.AccessibilityWindowInfo; 146 import android.view.accessibility.IAccessibilityEmbeddedConnection; 147 import android.view.accessibility.IAccessibilityInteractionConnection; 148 import android.view.accessibility.IAccessibilityInteractionConnectionCallback; 149 import android.view.animation.AccelerateDecelerateInterpolator; 150 import android.view.animation.Interpolator; 151 import android.view.autofill.AutofillId; 152 import android.view.autofill.AutofillManager; 153 import android.view.contentcapture.ContentCaptureManager; 154 import android.view.contentcapture.ContentCaptureSession; 155 import android.view.contentcapture.MainContentCaptureSession; 156 import android.view.inputmethod.InputMethodManager; 157 import android.widget.Scroller; 158 159 import com.android.internal.R; 160 import com.android.internal.annotations.GuardedBy; 161 import com.android.internal.annotations.VisibleForTesting; 162 import com.android.internal.os.IResultReceiver; 163 import com.android.internal.os.SomeArgs; 164 import com.android.internal.policy.DecorView; 165 import com.android.internal.policy.PhoneFallbackEventHandler; 166 import com.android.internal.util.Preconditions; 167 import com.android.internal.view.BaseSurfaceHolder; 168 import com.android.internal.view.RootViewSurfaceTaker; 169 import com.android.internal.view.SurfaceCallbackHelper; 170 171 import java.io.FileDescriptor; 172 import java.io.IOException; 173 import java.io.OutputStream; 174 import java.io.PrintWriter; 175 import java.lang.ref.WeakReference; 176 import java.util.ArrayList; 177 import java.util.HashSet; 178 import java.util.LinkedList; 179 import java.util.List; 180 import java.util.Queue; 181 import java.util.concurrent.CountDownLatch; 182 183 /** 184 * The top of a view hierarchy, implementing the needed protocol between View 185 * and the WindowManager. This is for the most part an internal implementation 186 * detail of {@link WindowManagerGlobal}. 187 * 188 * {@hide} 189 */ 190 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) 191 public final class ViewRootImpl implements ViewParent, 192 View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks { 193 private static final String TAG = "ViewRootImpl"; 194 private static final boolean DBG = false; 195 private static final boolean LOCAL_LOGV = false; 196 /** @noinspection PointlessBooleanExpression*/ 197 private static final boolean DEBUG_DRAW = false || LOCAL_LOGV; 198 private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV; 199 private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV; 200 private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV; 201 private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV; 202 private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV; 203 private static final boolean DEBUG_IMF = false || LOCAL_LOGV; 204 private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV; 205 private static final boolean DEBUG_FPS = false; 206 private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV; 207 private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV; 208 private static final boolean DEBUG_CONTENT_CAPTURE = false || LOCAL_LOGV; 209 private static final boolean DEBUG_SCROLL_CAPTURE = false || LOCAL_LOGV; 210 211 /** 212 * Set to false if we do not want to use the multi threaded renderer even though 213 * threaded renderer (aka hardware renderering) is used. Note that by disabling 214 * this, WindowCallbacks will not fire. 215 */ 216 private static final boolean MT_RENDERER_AVAILABLE = true; 217 218 /** 219 * If set to 2, the view system will switch from using rectangles retrieved from window to 220 * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets 221 * directly from the full configuration, enabling richer information about the insets state, as 222 * well as new APIs to control it frame-by-frame, and synchronize animations with it. 223 * <p> 224 * Only set this to 2 once the new insets system is productionized and the old APIs are 225 * fully migrated over. 226 * <p> 227 * If set to 1, this will switch to a mode where we only use the new approach for IME, but not 228 * for the status/navigation bar. 229 */ 230 private static final String USE_NEW_INSETS_PROPERTY = "persist.debug.new_insets"; 231 232 /** 233 * @see #USE_NEW_INSETS_PROPERTY 234 * @hide 235 */ 236 public static final int NEW_INSETS_MODE_NONE = 0; 237 238 /** 239 * @see #USE_NEW_INSETS_PROPERTY 240 * @hide 241 */ 242 public static final int NEW_INSETS_MODE_IME = 1; 243 244 /** 245 * @see #USE_NEW_INSETS_PROPERTY 246 * @hide 247 */ 248 public static final int NEW_INSETS_MODE_FULL = 2; 249 250 /** 251 * @see #USE_NEW_INSETS_PROPERTY 252 * @hide 253 */ 254 public static int sNewInsetsMode = 255 SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, NEW_INSETS_MODE_FULL); 256 257 /** 258 * Set this system property to true to force the view hierarchy to render 259 * at 60 Hz. This can be used to measure the potential framerate. 260 */ 261 private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering"; 262 263 /** 264 * Maximum time we allow the user to roll the trackball enough to generate 265 * a key event, before resetting the counters. 266 */ 267 static final int MAX_TRACKBALL_DELAY = 250; 268 269 /** 270 * Initial value for {@link #mContentCaptureEnabled}. 271 */ 272 private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0; 273 274 /** 275 * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}. 276 */ 277 private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1; 278 279 /** 280 * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}. 281 */ 282 private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2; 283 284 @UnsupportedAppUsage 285 static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); 286 287 static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<>(); 288 static boolean sFirstDrawComplete = false; 289 290 /** 291 * Callback for notifying about global configuration changes. 292 */ 293 public interface ConfigChangedCallback { 294 295 /** Notifies about global config change. */ onConfigurationChanged(Configuration globalConfig)296 void onConfigurationChanged(Configuration globalConfig); 297 } 298 299 private static final ArrayList<ConfigChangedCallback> sConfigCallbacks = new ArrayList<>(); 300 301 /** 302 * Callback for notifying activities about override configuration changes. 303 */ 304 public interface ActivityConfigCallback { 305 306 /** 307 * Notifies about override config change and/or move to different display. 308 * @param overrideConfig New override config to apply to activity. 309 * @param newDisplayId New display id, {@link Display#INVALID_DISPLAY} if not changed. 310 */ onConfigurationChanged(Configuration overrideConfig, int newDisplayId)311 void onConfigurationChanged(Configuration overrideConfig, int newDisplayId); 312 } 313 314 /** 315 * Callback used to notify corresponding activity about override configuration change and make 316 * sure that all resources are set correctly before updating the ViewRootImpl's internal state. 317 */ 318 private ActivityConfigCallback mActivityConfigCallback; 319 320 /** 321 * Used when configuration change first updates the config of corresponding activity. 322 * In that case we receive a call back from {@link ActivityThread} and this flag is used to 323 * preserve the initial value. 324 * 325 * @see #performConfigurationChange(Configuration, Configuration, boolean, int) 326 */ 327 private boolean mForceNextConfigUpdate; 328 329 private boolean mUseBLASTAdapter; 330 private boolean mForceDisableBLAST; 331 private boolean mEnableTripleBuffering; 332 333 /** 334 * Signals that compatibility booleans have been initialized according to 335 * target SDK versions. 336 */ 337 private static boolean sCompatibilityDone = false; 338 339 /** 340 * Always assign focus if a focusable View is available. 341 */ 342 private static boolean sAlwaysAssignFocus; 343 344 /** 345 * This list must only be modified by the main thread, so a lock is only needed when changing 346 * the list or when accessing the list from a non-main thread. 347 */ 348 @GuardedBy("mWindowCallbacks") 349 final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>(); 350 @UnsupportedAppUsage 351 public final Context mContext; 352 353 @UnsupportedAppUsage 354 final IWindowSession mWindowSession; 355 @NonNull Display mDisplay; 356 final DisplayManager mDisplayManager; 357 final String mBasePackageName; 358 359 final int[] mTmpLocation = new int[2]; 360 361 final TypedValue mTmpValue = new TypedValue(); 362 363 final Thread mThread; 364 365 final WindowLeaked mLocation; 366 367 public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams(); 368 369 final W mWindow; 370 371 final IBinder mLeashToken; 372 373 final int mTargetSdkVersion; 374 375 int mSeq; 376 377 @UnsupportedAppUsage 378 View mView; 379 380 View mAccessibilityFocusedHost; 381 AccessibilityNodeInfo mAccessibilityFocusedVirtualView; 382 383 // True if the window currently has pointer capture enabled. 384 boolean mPointerCapture; 385 386 int mViewVisibility; 387 boolean mAppVisible = true; 388 // For recents to freeform transition we need to keep drawing after the app receives information 389 // that it became invisible. This will ignore that information and depend on the decor view 390 // visibility to control drawing. The decor view visibility will get adjusted when the app get 391 // stopped and that's when the app will stop drawing further frames. 392 private boolean mForceDecorViewVisibility = false; 393 // Used for tracking app visibility updates separately in case we get double change. This will 394 // make sure that we always call relayout for the corresponding window. 395 private boolean mAppVisibilityChanged; 396 int mOrigWindowType = -1; 397 398 /** Whether the window had focus during the most recent traversal. */ 399 boolean mHadWindowFocus; 400 401 /** 402 * Whether the window lost focus during a previous traversal and has not 403 * yet gained it back. Used to determine whether a WINDOW_STATE_CHANGE 404 * accessibility events should be sent during traversal. 405 */ 406 boolean mLostWindowFocus; 407 408 // Set to true if the owner of this window is in the stopped state, 409 // so the window should no longer be active. 410 @UnsupportedAppUsage 411 boolean mStopped = false; 412 413 // Set to true if the owner of this window is in ambient mode, 414 // which means it won't receive input events. 415 boolean mIsAmbientMode = false; 416 417 // Set to true to stop input during an Activity Transition. 418 boolean mPausedForTransition = false; 419 420 boolean mLastInCompatMode = false; 421 422 SurfaceHolder.Callback2 mSurfaceHolderCallback; 423 BaseSurfaceHolder mSurfaceHolder; 424 boolean mIsCreating; 425 boolean mDrawingAllowed; 426 427 final Region mTransparentRegion; 428 final Region mPreviousTransparentRegion; 429 430 @UnsupportedAppUsage 431 int mWidth; 432 @UnsupportedAppUsage 433 int mHeight; 434 @UnsupportedAppUsage 435 Rect mDirty; 436 public boolean mIsAnimating; 437 438 private boolean mUseMTRenderer; 439 private boolean mDragResizing; 440 private boolean mInvalidateRootRequested; 441 private int mResizeMode; 442 private int mCanvasOffsetX; 443 private int mCanvasOffsetY; 444 private boolean mActivityRelaunched; 445 446 CompatibilityInfo.Translator mTranslator; 447 448 @UnsupportedAppUsage 449 final View.AttachInfo mAttachInfo; 450 final SystemUiVisibilityInfo mCompatibleVisibilityInfo; 451 int mDispatchedSystemUiVisibility = 452 ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL ? 0 : -1; 453 InputQueue.Callback mInputQueueCallback; 454 InputQueue mInputQueue; 455 @UnsupportedAppUsage 456 FallbackEventHandler mFallbackEventHandler; 457 final Choreographer mChoreographer; 458 459 // used in relayout to get SurfaceControl size 460 // for BLAST adapter surface setup 461 private final Point mSurfaceSize = new Point(); 462 463 final Rect mTempRect; // used in the transaction to not thrash the heap. 464 final Rect mVisRect; // used to retrieve visible rect of focused view. 465 private final Rect mTempBoundsRect = new Rect(); // used to set the size of the bounds surface. 466 467 // This is used to reduce the race between window focus changes being dispatched from 468 // the window manager and input events coming through the input system. 469 @GuardedBy("this") 470 boolean mWindowFocusChanged; 471 @GuardedBy("this") 472 boolean mUpcomingWindowFocus; 473 @GuardedBy("this") 474 boolean mUpcomingInTouchMode; 475 476 public boolean mTraversalScheduled; 477 int mTraversalBarrier; 478 boolean mWillDrawSoon; 479 /** Set to true while in performTraversals for detecting when die(true) is called from internal 480 * callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */ 481 boolean mIsInTraversal; 482 boolean mApplyInsetsRequested; 483 boolean mLayoutRequested; 484 boolean mFirst; 485 486 @Nullable 487 int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED; 488 boolean mPerformContentCapture; 489 490 boolean mReportNextDraw; 491 boolean mFullRedrawNeeded; 492 boolean mNewSurfaceNeeded; 493 boolean mForceNextWindowRelayout; 494 CountDownLatch mWindowDrawCountDown; 495 496 boolean mIsDrawing; 497 int mLastSystemUiVisibility; 498 int mClientWindowLayoutFlags; 499 500 // Pool of queued input events. 501 private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10; 502 private QueuedInputEvent mQueuedInputEventPool; 503 private int mQueuedInputEventPoolSize; 504 505 /* Input event queue. 506 * Pending input events are input events waiting to be delivered to the input stages 507 * and handled by the application. 508 */ 509 QueuedInputEvent mPendingInputEventHead; 510 QueuedInputEvent mPendingInputEventTail; 511 int mPendingInputEventCount; 512 boolean mProcessInputEventsScheduled; 513 boolean mUnbufferedInputDispatch; 514 @InputSourceClass 515 int mUnbufferedInputSource = SOURCE_CLASS_NONE; 516 517 String mPendingInputEventQueueLengthCounterName = "pq"; 518 519 InputStage mFirstInputStage; 520 InputStage mFirstPostImeInputStage; 521 InputStage mSyntheticInputStage; 522 523 private final UnhandledKeyManager mUnhandledKeyManager = new UnhandledKeyManager(); 524 525 boolean mWindowAttributesChanged = false; 526 527 // These can be accessed by any thread, must be protected with a lock. 528 // Surface can never be reassigned or cleared (use Surface.clear()). 529 @UnsupportedAppUsage 530 public final Surface mSurface = new Surface(); 531 private final SurfaceControl mSurfaceControl = new SurfaceControl(); 532 private SurfaceControl mBlastSurfaceControl = new SurfaceControl(); 533 534 private BLASTBufferQueue mBlastBufferQueue; 535 536 /** 537 * Transaction object that can be used to synchronize child SurfaceControl changes with 538 * ViewRootImpl SurfaceControl changes by the server. The object is passed along with 539 * the SurfaceChangedCallback. 540 */ 541 private final Transaction mSurfaceChangedTransaction = new Transaction(); 542 /** 543 * Child container layer of {@code mSurface} with the same bounds as its parent, and cropped to 544 * the surface insets. This surface is created only if a client requests it via {@link 545 * #getBoundsLayer()}. By parenting to this bounds surface, child surfaces can ensure they do 546 * not draw into the surface inset region set by the parent window. 547 */ 548 private SurfaceControl mBoundsLayer; 549 private final SurfaceSession mSurfaceSession = new SurfaceSession(); 550 private final Transaction mTransaction = new Transaction(); 551 552 @UnsupportedAppUsage 553 boolean mAdded; 554 boolean mAddedTouchMode; 555 556 final Rect mTmpFrame = new Rect(); 557 final Rect mTmpRect = new Rect(); 558 559 // These are accessed by multiple threads. 560 final Rect mWinFrame; // frame given by window manager. 561 562 final Rect mPendingBackDropFrame = new Rect(); 563 final DisplayCutout.ParcelableWrapper mPendingDisplayCutout = 564 new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); 565 boolean mPendingAlwaysConsumeSystemBars; 566 private final InsetsState mTempInsets = new InsetsState(); 567 private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[SIZE]; 568 final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets 569 = new ViewTreeObserver.InternalInsetsInfo(); 570 571 private WindowInsets mLastWindowInsets; 572 573 // Insets types hidden by legacy window flags or system UI flags. 574 private @InsetsType int mTypesHiddenByFlags = 0; 575 576 /** Last applied configuration obtained from resources. */ 577 private final Configuration mLastConfigurationFromResources = new Configuration(); 578 /** Last configuration reported from WM or via {@link #MSG_UPDATE_CONFIGURATION}. */ 579 private final MergedConfiguration mLastReportedMergedConfiguration = new MergedConfiguration(); 580 /** Configurations waiting to be applied. */ 581 private final MergedConfiguration mPendingMergedConfiguration = new MergedConfiguration(); 582 583 boolean mScrollMayChange; 584 @SoftInputModeFlags 585 int mSoftInputMode; 586 @UnsupportedAppUsage 587 WeakReference<View> mLastScrolledFocus; 588 int mScrollY; 589 int mCurScrollY; 590 Scroller mScroller; 591 static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); 592 private ArrayList<LayoutTransition> mPendingTransitions; 593 594 final ViewConfiguration mViewConfiguration; 595 596 /* Drag/drop */ 597 ClipDescription mDragDescription; 598 View mCurrentDragView; 599 volatile Object mLocalDragState; 600 final PointF mDragPoint = new PointF(); 601 final PointF mLastTouchPoint = new PointF(); 602 int mLastTouchSource; 603 604 private boolean mProfileRendering; 605 private Choreographer.FrameCallback mRenderProfiler; 606 private boolean mRenderProfilingEnabled; 607 608 // Variables to track frames per second, enabled via DEBUG_FPS flag 609 private long mFpsStartTime = -1; 610 private long mFpsPrevTime = -1; 611 private int mFpsNumFrames; 612 613 private int mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; 614 private PointerIcon mCustomPointerIcon = null; 615 616 /** 617 * see {@link #playSoundEffect(int)} 618 */ 619 AudioManager mAudioManager; 620 621 final AccessibilityManager mAccessibilityManager; 622 623 AccessibilityInteractionController mAccessibilityInteractionController; 624 625 final AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager = 626 new AccessibilityInteractionConnectionManager(); 627 final HighContrastTextManager mHighContrastTextManager; 628 629 SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent; 630 631 HashSet<View> mTempHashSet; 632 633 private final int mDensity; 634 private final int mNoncompatDensity; 635 636 private boolean mInLayout = false; 637 ArrayList<View> mLayoutRequesters = new ArrayList<View>(); 638 boolean mHandlingLayoutInLayoutRequest = false; 639 640 private int mViewLayoutDirectionInitial; 641 642 /** Set to true once doDie() has been called. */ 643 private boolean mRemoved; 644 645 private boolean mNeedsRendererSetup; 646 647 private final InputEventCompatProcessor mInputCompatProcessor; 648 649 /** 650 * Consistency verifier for debugging purposes. 651 */ 652 protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier = 653 InputEventConsistencyVerifier.isInstrumentationEnabled() ? 654 new InputEventConsistencyVerifier(this, 0) : null; 655 656 private final InsetsController mInsetsController; 657 private final ImeFocusController mImeFocusController; 658 659 private ScrollCaptureClient mScrollCaptureClient; 660 661 /** 662 * @return {@link ImeFocusController} for this instance. 663 */ 664 @NonNull getImeFocusController()665 public ImeFocusController getImeFocusController() { 666 return mImeFocusController; 667 } 668 669 /** @return The current {@link ScrollCaptureClient} for this instance, if any is active. */ 670 @Nullable getScrollCaptureClient()671 public ScrollCaptureClient getScrollCaptureClient() { 672 return mScrollCaptureClient; 673 } 674 675 private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker(); 676 677 private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; 678 679 static final class SystemUiVisibilityInfo { 680 int seq; 681 int globalVisibility; 682 int localValue; 683 int localChanges; 684 } 685 686 // If set, ViewRootImpl will call BLASTBufferQueue::setNextTransaction with 687 // mRtBLASTSyncTransaction, prior to invoking draw. This provides a way 688 // to redirect the buffers in to transactions. 689 private boolean mNextDrawUseBLASTSyncTransaction; 690 // Set when calling setNextTransaction, we can't just reuse mNextDrawUseBLASTSyncTransaction 691 // because, imagine this scenario: 692 // 1. First draw is using BLAST, mNextDrawUseBLAST = true 693 // 2. We call perform draw and are waiting on the callback 694 // 3. After the first perform draw but before the first callback and the 695 // second perform draw, a second draw sets mNextDrawUseBLAST = true (it already was) 696 // 4. At this point the callback fires and we set mNextDrawUseBLAST = false; 697 // 5. We get to performDraw and fail to sync as we intended because mNextDrawUseBLAST 698 // is now false. 699 // This is why we use a two-step latch with the two booleans, one consumed from 700 // performDraw and one consumed from finishBLASTSync() 701 private boolean mNextReportConsumeBLAST; 702 // Be very careful with the threading here. This is used from the render thread while 703 // the UI thread is paused and then applied and cleared from the UI thread right after 704 // draw returns. 705 private SurfaceControl.Transaction mRtBLASTSyncTransaction = new SurfaceControl.Transaction(); 706 707 // Keeps track of whether the WM requested us to use BLAST Sync when calling relayout. 708 // We use this to make sure we don't send the WM transactions from an internal BLAST sync 709 // (e.g. SurfaceView) 710 private boolean mSendNextFrameToWm = false; 711 712 private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks; 713 714 private String mTag = TAG; 715 ViewRootImpl(Context context, Display display)716 public ViewRootImpl(Context context, Display display) { 717 this(context, display, WindowManagerGlobal.getWindowSession(), 718 false /* useSfChoreographer */); 719 } 720 ViewRootImpl(Context context, Display display, IWindowSession session)721 public ViewRootImpl(Context context, Display display, IWindowSession session) { 722 this(context, display, session, false /* useSfChoreographer */); 723 } 724 ViewRootImpl(Context context, Display display, IWindowSession session, boolean useSfChoreographer)725 public ViewRootImpl(Context context, Display display, IWindowSession session, 726 boolean useSfChoreographer) { 727 mContext = context; 728 mWindowSession = session; 729 mDisplay = display; 730 mBasePackageName = context.getBasePackageName(); 731 mThread = Thread.currentThread(); 732 mLocation = new WindowLeaked(null); 733 mLocation.fillInStackTrace(); 734 mWidth = -1; 735 mHeight = -1; 736 mDirty = new Rect(); 737 mTempRect = new Rect(); 738 mVisRect = new Rect(); 739 mWinFrame = new Rect(); 740 mWindow = new W(this); 741 mLeashToken = new Binder(); 742 mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; 743 mViewVisibility = View.GONE; 744 mTransparentRegion = new Region(); 745 mPreviousTransparentRegion = new Region(); 746 mFirst = true; // true for the first time the view is added 747 mPerformContentCapture = true; // also true for the first time the view is added 748 mAdded = false; 749 mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, 750 context); 751 mCompatibleVisibilityInfo = new SystemUiVisibilityInfo(); 752 mAccessibilityManager = AccessibilityManager.getInstance(context); 753 mAccessibilityManager.addAccessibilityStateChangeListener( 754 mAccessibilityInteractionConnectionManager, mHandler); 755 mHighContrastTextManager = new HighContrastTextManager(); 756 mAccessibilityManager.addHighTextContrastStateChangeListener( 757 mHighContrastTextManager, mHandler); 758 mViewConfiguration = ViewConfiguration.get(context); 759 mDensity = context.getResources().getDisplayMetrics().densityDpi; 760 mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; 761 mFallbackEventHandler = new PhoneFallbackEventHandler(context); 762 mChoreographer = useSfChoreographer 763 ? Choreographer.getSfInstance() : Choreographer.getInstance(); 764 mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); 765 mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this)); 766 767 String processorOverrideName = context.getResources().getString( 768 R.string.config_inputEventCompatProcessorOverrideClassName); 769 if (processorOverrideName.isEmpty()) { 770 // No compatibility processor override, using default. 771 mInputCompatProcessor = new InputEventCompatProcessor(context); 772 } else { 773 InputEventCompatProcessor compatProcessor = null; 774 try { 775 final Class<? extends InputEventCompatProcessor> klass = 776 (Class<? extends InputEventCompatProcessor>) Class.forName( 777 processorOverrideName); 778 compatProcessor = klass.getConstructor(Context.class).newInstance(context); 779 } catch (Exception e) { 780 Log.e(TAG, "Unable to create the InputEventCompatProcessor. ", e); 781 } finally { 782 mInputCompatProcessor = compatProcessor; 783 } 784 } 785 786 if (!sCompatibilityDone) { 787 sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P; 788 789 sCompatibilityDone = true; 790 } 791 792 loadSystemProperties(); 793 mImeFocusController = new ImeFocusController(this); 794 } 795 796 public static void addFirstDrawHandler(Runnable callback) { 797 synchronized (sFirstDrawHandlers) { 798 if (!sFirstDrawComplete) { 799 sFirstDrawHandlers.add(callback); 800 } 801 } 802 } 803 804 /** Add static config callback to be notified about global config changes. */ 805 @UnsupportedAppUsage 806 public static void addConfigCallback(ConfigChangedCallback callback) { 807 synchronized (sConfigCallbacks) { 808 sConfigCallbacks.add(callback); 809 } 810 } 811 812 /** Add activity config callback to be notified about override config changes. */ 813 public void setActivityConfigCallback(ActivityConfigCallback callback) { 814 mActivityConfigCallback = callback; 815 } 816 817 public void setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener listener) { 818 mAttachInfo.mContentOnApplyWindowInsetsListener = listener; 819 820 // System windows will be fitted on first traversal, so no reason to request additional 821 // (possibly getting executed after the first traversal). 822 if (!mFirst) { 823 requestFitSystemWindows(); 824 } 825 } 826 827 public void addWindowCallbacks(WindowCallbacks callback) { 828 synchronized (mWindowCallbacks) { 829 mWindowCallbacks.add(callback); 830 } 831 } 832 833 public void removeWindowCallbacks(WindowCallbacks callback) { 834 synchronized (mWindowCallbacks) { 835 mWindowCallbacks.remove(callback); 836 } 837 } 838 839 public void reportDrawFinish() { 840 if (mWindowDrawCountDown != null) { 841 mWindowDrawCountDown.countDown(); 842 } 843 } 844 845 // FIXME for perf testing only 846 private boolean mProfile = false; 847 848 /** 849 * Call this to profile the next traversal call. 850 * FIXME for perf testing only. Remove eventually 851 */ 852 public void profile() { 853 mProfile = true; 854 } 855 856 /** 857 * Indicates whether we are in touch mode. Calling this method triggers an IPC 858 * call and should be avoided whenever possible. 859 * 860 * @return True, if the device is in touch mode, false otherwise. 861 * 862 * @hide 863 */ 864 static boolean isInTouchMode() { 865 IWindowSession windowSession = WindowManagerGlobal.peekWindowSession(); 866 if (windowSession != null) { 867 try { 868 return windowSession.getInTouchMode(); 869 } catch (RemoteException e) { 870 } 871 } 872 return false; 873 } 874 875 /** 876 * Notifies us that our child has been rebuilt, following 877 * a window preservation operation. In these cases we 878 * keep the same DecorView, but the activity controlling it 879 * is a different instance, and we need to update our 880 * callbacks. 881 * 882 * @hide 883 */ 884 public void notifyChildRebuilt() { 885 if (mView instanceof RootViewSurfaceTaker) { 886 if (mSurfaceHolderCallback != null) { 887 mSurfaceHolder.removeCallback(mSurfaceHolderCallback); 888 } 889 890 mSurfaceHolderCallback = 891 ((RootViewSurfaceTaker)mView).willYouTakeTheSurface(); 892 893 if (mSurfaceHolderCallback != null) { 894 mSurfaceHolder = new TakenSurfaceHolder(); 895 mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); 896 mSurfaceHolder.addCallback(mSurfaceHolderCallback); 897 } else { 898 mSurfaceHolder = null; 899 } 900 901 mInputQueueCallback = 902 ((RootViewSurfaceTaker)mView).willYouTakeTheInputQueue(); 903 if (mInputQueueCallback != null) { 904 mInputQueueCallback.onInputQueueCreated(mInputQueue); 905 } 906 } 907 } 908 909 /** 910 * We have one child 911 */ 912 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 913 setView(view, attrs, panelParentView, UserHandle.myUserId()); 914 } 915 916 /** 917 * We have one child 918 */ 919 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, 920 int userId) { 921 synchronized (this) { 922 if (mView == null) { 923 mView = view; 924 925 mAttachInfo.mDisplayState = mDisplay.getState(); 926 mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); 927 928 mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); 929 mFallbackEventHandler.setView(view); 930 mWindowAttributes.copyFrom(attrs); 931 if (mWindowAttributes.packageName == null) { 932 mWindowAttributes.packageName = mBasePackageName; 933 } 934 mWindowAttributes.privateFlags |= 935 WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; 936 937 attrs = mWindowAttributes; 938 setTag(); 939 940 if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags 941 & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0 942 && (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) { 943 Slog.d(mTag, "setView: FLAG_KEEP_SCREEN_ON changed from true to false!"); 944 } 945 // Keep track of the actual window flags supplied by the client. 946 mClientWindowLayoutFlags = attrs.flags; 947 948 setAccessibilityFocus(null, null); 949 950 if (view instanceof RootViewSurfaceTaker) { 951 mSurfaceHolderCallback = 952 ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); 953 if (mSurfaceHolderCallback != null) { 954 mSurfaceHolder = new TakenSurfaceHolder(); 955 mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); 956 mSurfaceHolder.addCallback(mSurfaceHolderCallback); 957 } 958 } 959 960 // Compute surface insets required to draw at specified Z value. 961 // TODO: Use real shadow insets for a constant max Z. 962 if (!attrs.hasManualSurfaceInsets) { 963 attrs.setSurfaceInsets(view, false /*manual*/, true /*preservePrevious*/); 964 } 965 966 CompatibilityInfo compatibilityInfo = 967 mDisplay.getDisplayAdjustments().getCompatibilityInfo(); 968 mTranslator = compatibilityInfo.getTranslator(); 969 970 // If the application owns the surface, don't enable hardware acceleration 971 if (mSurfaceHolder == null) { 972 // While this is supposed to enable only, it can effectively disable 973 // the acceleration too. 974 enableHardwareAcceleration(attrs); 975 final boolean useMTRenderer = MT_RENDERER_AVAILABLE 976 && mAttachInfo.mThreadedRenderer != null; 977 if (mUseMTRenderer != useMTRenderer) { 978 // Shouldn't be resizing, as it's done only in window setup, 979 // but end just in case. 980 endDragResizing(); 981 mUseMTRenderer = useMTRenderer; 982 } 983 } 984 985 boolean restore = false; 986 if (mTranslator != null) { 987 mSurface.setCompatibilityTranslator(mTranslator); 988 restore = true; 989 attrs.backup(); 990 mTranslator.translateWindowLayout(attrs); 991 } 992 if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs); 993 994 if (!compatibilityInfo.supportsScreen()) { 995 attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; 996 mLastInCompatMode = true; 997 } 998 999 mSoftInputMode = attrs.softInputMode; 1000 mWindowAttributesChanged = true; 1001 mAttachInfo.mRootView = view; 1002 mAttachInfo.mScalingRequired = mTranslator != null; 1003 mAttachInfo.mApplicationScale = 1004 mTranslator == null ? 1.0f : mTranslator.applicationScale; 1005 if (panelParentView != null) { 1006 mAttachInfo.mPanelParentWindowToken 1007 = panelParentView.getApplicationWindowToken(); 1008 } 1009 mAdded = true; 1010 int res; /* = WindowManagerImpl.ADD_OKAY; */ 1011 1012 // Schedule the first layout -before- adding to the window 1013 // manager, to make sure we do the relayout before receiving 1014 // any other events from the system. 1015 requestLayout(); 1016 InputChannel inputChannel = null; 1017 if ((mWindowAttributes.inputFeatures 1018 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 1019 inputChannel = new InputChannel(); 1020 } 1021 mForceDecorViewVisibility = (mWindowAttributes.privateFlags 1022 & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0; 1023 try { 1024 mOrigWindowType = mWindowAttributes.type; 1025 mAttachInfo.mRecomputeGlobalAttributes = true; 1026 collectViewAttributes(); 1027 adjustLayoutParamsForCompatibility(mWindowAttributes); 1028 res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes, 1029 getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame, 1030 mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 1031 mAttachInfo.mDisplayCutout, inputChannel, 1032 mTempInsets, mTempControls); 1033 setFrame(mTmpFrame); 1034 } catch (RemoteException e) { 1035 mAdded = false; 1036 mView = null; 1037 mAttachInfo.mRootView = null; 1038 inputChannel = null; 1039 mFallbackEventHandler.setView(null); 1040 unscheduleTraversals(); 1041 setAccessibilityFocus(null, null); 1042 throw new RuntimeException("Adding window failed", e); 1043 } finally { 1044 if (restore) { 1045 attrs.restore(); 1046 } 1047 } 1048 1049 if (mTranslator != null) { 1050 mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); 1051 } 1052 mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout); 1053 mAttachInfo.mAlwaysConsumeSystemBars = 1054 (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0; 1055 mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars; 1056 mInsetsController.onStateChanged(mTempInsets); 1057 mInsetsController.onControlsChanged(mTempControls); 1058 if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); 1059 if (res < WindowManagerGlobal.ADD_OKAY) { 1060 mAttachInfo.mRootView = null; 1061 mAdded = false; 1062 mFallbackEventHandler.setView(null); 1063 unscheduleTraversals(); 1064 setAccessibilityFocus(null, null); 1065 switch (res) { 1066 case WindowManagerGlobal.ADD_BAD_APP_TOKEN: 1067 case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN: 1068 throw new WindowManager.BadTokenException( 1069 "Unable to add window -- token " + attrs.token 1070 + " is not valid; is your activity running?"); 1071 case WindowManagerGlobal.ADD_NOT_APP_TOKEN: 1072 throw new WindowManager.BadTokenException( 1073 "Unable to add window -- token " + attrs.token 1074 + " is not for an application"); 1075 case WindowManagerGlobal.ADD_APP_EXITING: 1076 throw new WindowManager.BadTokenException( 1077 "Unable to add window -- app for token " + attrs.token 1078 + " is exiting"); 1079 case WindowManagerGlobal.ADD_DUPLICATE_ADD: 1080 throw new WindowManager.BadTokenException( 1081 "Unable to add window -- window " + mWindow 1082 + " has already been added"); 1083 case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED: 1084 // Silently ignore -- we would have just removed it 1085 // right away, anyway. 1086 return; 1087 case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON: 1088 throw new WindowManager.BadTokenException("Unable to add window " 1089 + mWindow + " -- another window of type " 1090 + mWindowAttributes.type + " already exists"); 1091 case WindowManagerGlobal.ADD_PERMISSION_DENIED: 1092 throw new WindowManager.BadTokenException("Unable to add window " 1093 + mWindow + " -- permission denied for window type " 1094 + mWindowAttributes.type); 1095 case WindowManagerGlobal.ADD_INVALID_DISPLAY: 1096 throw new WindowManager.InvalidDisplayException("Unable to add window " 1097 + mWindow + " -- the specified display can not be found"); 1098 case WindowManagerGlobal.ADD_INVALID_TYPE: 1099 throw new WindowManager.InvalidDisplayException("Unable to add window " 1100 + mWindow + " -- the specified window type " 1101 + mWindowAttributes.type + " is not valid"); 1102 case WindowManagerGlobal.ADD_INVALID_USER: 1103 throw new WindowManager.BadTokenException("Unable to add Window " 1104 + mWindow + " -- requested userId is not valid"); 1105 } 1106 throw new RuntimeException( 1107 "Unable to add window -- unknown error code " + res); 1108 } 1109 1110 if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) { 1111 mUseBLASTAdapter = true; 1112 } 1113 if ((res & WindowManagerGlobal.ADD_FLAG_USE_TRIPLE_BUFFERING) != 0) { 1114 mEnableTripleBuffering = true; 1115 } 1116 1117 if (view instanceof RootViewSurfaceTaker) { 1118 mInputQueueCallback = 1119 ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); 1120 } 1121 if (inputChannel != null) { 1122 if (mInputQueueCallback != null) { 1123 mInputQueue = new InputQueue(); 1124 mInputQueueCallback.onInputQueueCreated(mInputQueue); 1125 } 1126 mInputEventReceiver = new WindowInputEventReceiver(inputChannel, 1127 Looper.myLooper()); 1128 } 1129 1130 view.assignParent(this); 1131 mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0; 1132 mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0; 1133 1134 if (mAccessibilityManager.isEnabled()) { 1135 mAccessibilityInteractionConnectionManager.ensureConnection(); 1136 } 1137 1138 if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 1139 view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 1140 } 1141 1142 // Set up the input pipeline. 1143 CharSequence counterSuffix = attrs.getTitle(); 1144 mSyntheticInputStage = new SyntheticInputStage(); 1145 InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); 1146 InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, 1147 "aq:native-post-ime:" + counterSuffix); 1148 InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); 1149 InputStage imeStage = new ImeInputStage(earlyPostImeStage, 1150 "aq:ime:" + counterSuffix); 1151 InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); 1152 InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, 1153 "aq:native-pre-ime:" + counterSuffix); 1154 1155 mFirstInputStage = nativePreImeStage; 1156 mFirstPostImeInputStage = earlyPostImeStage; 1157 mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix; 1158 1159 if (mView instanceof RootViewSurfaceTaker) { 1160 PendingInsetsController pendingInsetsController = 1161 ((RootViewSurfaceTaker) mView).providePendingInsetsController(); 1162 if (pendingInsetsController != null) { 1163 pendingInsetsController.replayAndAttach(mInsetsController); 1164 } 1165 } 1166 } 1167 } 1168 } 1169 1170 private void setTag() { 1171 final String[] split = mWindowAttributes.getTitle().toString().split("\\."); 1172 if (split.length > 0) { 1173 mTag = TAG + "[" + split[split.length - 1] + "]"; 1174 } 1175 } 1176 1177 @UnsupportedAppUsage 1178 public int getWindowFlags() { 1179 return mWindowAttributes.flags; 1180 } 1181 1182 public int getDisplayId() { 1183 return mDisplay.getDisplayId(); 1184 } 1185 1186 public CharSequence getTitle() { 1187 return mWindowAttributes.getTitle(); 1188 } 1189 1190 /** 1191 * @return the width of the root view. Note that this will return {@code -1} until the first 1192 * layout traversal, when the width is set. 1193 * 1194 * @hide 1195 */ 1196 public int getWidth() { 1197 return mWidth; 1198 } 1199 1200 /** 1201 * @return the height of the root view. Note that this will return {@code -1} until the first 1202 * layout traversal, when the height is set. 1203 * 1204 * @hide 1205 */ 1206 public int getHeight() { 1207 return mHeight; 1208 } 1209 1210 /** 1211 * Destroys hardware rendering resources for this ViewRootImpl 1212 * 1213 * May be called on any thread 1214 */ 1215 @AnyThread 1216 void destroyHardwareResources() { 1217 final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer; 1218 if (renderer != null) { 1219 // This is called by WindowManagerGlobal which may or may not be on the right thread 1220 if (Looper.myLooper() != mAttachInfo.mHandler.getLooper()) { 1221 mAttachInfo.mHandler.postAtFrontOfQueue(this::destroyHardwareResources); 1222 return; 1223 } 1224 renderer.destroyHardwareResources(mView); 1225 renderer.destroy(); 1226 } 1227 } 1228 1229 @UnsupportedAppUsage 1230 public void detachFunctor(long functor) { 1231 if (mAttachInfo.mThreadedRenderer != null) { 1232 // Fence so that any pending invokeFunctor() messages will be processed 1233 // before we return from detachFunctor. 1234 mAttachInfo.mThreadedRenderer.stopDrawing(); 1235 } 1236 } 1237 1238 /** 1239 * Schedules the functor for execution in either kModeProcess or 1240 * kModeProcessNoContext, depending on whether or not there is an EGLContext. 1241 * 1242 * @param functor The native functor to invoke 1243 * @param waitForCompletion If true, this will not return until the functor 1244 * has invoked. If false, the functor may be invoked 1245 * asynchronously. 1246 */ 1247 @UnsupportedAppUsage 1248 public static void invokeFunctor(long functor, boolean waitForCompletion) { 1249 ThreadedRenderer.invokeFunctor(functor, waitForCompletion); 1250 } 1251 1252 /** 1253 * @param animator animator to register with the hardware renderer 1254 */ 1255 public void registerAnimatingRenderNode(RenderNode animator) { 1256 if (mAttachInfo.mThreadedRenderer != null) { 1257 mAttachInfo.mThreadedRenderer.registerAnimatingRenderNode(animator); 1258 } else { 1259 if (mAttachInfo.mPendingAnimatingRenderNodes == null) { 1260 mAttachInfo.mPendingAnimatingRenderNodes = new ArrayList<RenderNode>(); 1261 } 1262 mAttachInfo.mPendingAnimatingRenderNodes.add(animator); 1263 } 1264 } 1265 1266 /** 1267 * @param animator animator to register with the hardware renderer 1268 */ 1269 public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) { 1270 if (mAttachInfo.mThreadedRenderer != null) { 1271 mAttachInfo.mThreadedRenderer.registerVectorDrawableAnimator(animator); 1272 } 1273 } 1274 1275 /** 1276 * Registers a callback to be executed when the next frame is being drawn on RenderThread. This 1277 * callback will be executed on a RenderThread worker thread, and only used for the next frame 1278 * and thus it will only fire once. 1279 * 1280 * @param callback The callback to register. 1281 */ 1282 public void registerRtFrameCallback(@NonNull FrameDrawingCallback callback) { 1283 if (mAttachInfo.mThreadedRenderer != null) { 1284 mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frame -> { 1285 try { 1286 callback.onFrameDraw(frame); 1287 } catch (Exception e) { 1288 Log.e(TAG, "Exception while executing onFrameDraw", e); 1289 } 1290 }); 1291 } 1292 } 1293 1294 @UnsupportedAppUsage enableHardwareAcceleration(WindowManager.LayoutParams attrs)1295 private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { 1296 mAttachInfo.mHardwareAccelerated = false; 1297 mAttachInfo.mHardwareAccelerationRequested = false; 1298 1299 // Don't enable hardware acceleration when the application is in compatibility mode 1300 if (mTranslator != null) return; 1301 1302 // Try to enable hardware acceleration if requested 1303 final boolean hardwareAccelerated = 1304 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; 1305 1306 if (hardwareAccelerated) { 1307 if (!ThreadedRenderer.isAvailable()) { 1308 return; 1309 } 1310 1311 // Persistent processes (including the system) should not do 1312 // accelerated rendering on low-end devices. In that case, 1313 // sRendererDisabled will be set. In addition, the system process 1314 // itself should never do accelerated rendering. In that case, both 1315 // sRendererDisabled and sSystemRendererDisabled are set. When 1316 // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED 1317 // can be used by code on the system process to escape that and enable 1318 // HW accelerated drawing. (This is basically for the lock screen.) 1319 1320 final boolean fakeHwAccelerated = (attrs.privateFlags & 1321 WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0; 1322 final boolean forceHwAccelerated = (attrs.privateFlags & 1323 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0; 1324 1325 if (fakeHwAccelerated) { 1326 // This is exclusively for the preview windows the window manager 1327 // shows for launching applications, so they will look more like 1328 // the app being launched. 1329 mAttachInfo.mHardwareAccelerationRequested = true; 1330 } else if (!ThreadedRenderer.sRendererDisabled 1331 || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) { 1332 if (mAttachInfo.mThreadedRenderer != null) { 1333 mAttachInfo.mThreadedRenderer.destroy(); 1334 } 1335 1336 final Rect insets = attrs.surfaceInsets; 1337 final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0 1338 || insets.top != 0 || insets.bottom != 0; 1339 final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets; 1340 final boolean wideGamut = 1341 mContext.getResources().getConfiguration().isScreenWideColorGamut() 1342 && attrs.getColorMode() == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT; 1343 1344 mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent, 1345 attrs.getTitle().toString()); 1346 mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut); 1347 updateForceDarkMode(); 1348 if (mAttachInfo.mThreadedRenderer != null) { 1349 mAttachInfo.mHardwareAccelerated = 1350 mAttachInfo.mHardwareAccelerationRequested = true; 1351 } 1352 } 1353 } 1354 } 1355 getNightMode()1356 private int getNightMode() { 1357 return mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; 1358 } 1359 updateForceDarkMode()1360 private void updateForceDarkMode() { 1361 if (mAttachInfo.mThreadedRenderer == null) return; 1362 1363 boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES; 1364 1365 if (useAutoDark) { 1366 boolean forceDarkAllowedDefault = 1367 SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false); 1368 TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme); 1369 useAutoDark = a.getBoolean(R.styleable.Theme_isLightTheme, true) 1370 && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault); 1371 a.recycle(); 1372 } 1373 1374 if (mAttachInfo.mThreadedRenderer.setForceDark(useAutoDark)) { 1375 // TODO: Don't require regenerating all display lists to apply this setting 1376 invalidateWorld(mView); 1377 } 1378 } 1379 1380 @UnsupportedAppUsage getView()1381 public View getView() { 1382 return mView; 1383 } 1384 getLocation()1385 final WindowLeaked getLocation() { 1386 return mLocation; 1387 } 1388 setLayoutParams(WindowManager.LayoutParams attrs, boolean newView)1389 void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { 1390 synchronized (this) { 1391 final int oldInsetLeft = mWindowAttributes.surfaceInsets.left; 1392 final int oldInsetTop = mWindowAttributes.surfaceInsets.top; 1393 final int oldInsetRight = mWindowAttributes.surfaceInsets.right; 1394 final int oldInsetBottom = mWindowAttributes.surfaceInsets.bottom; 1395 final int oldSoftInputMode = mWindowAttributes.softInputMode; 1396 final boolean oldHasManualSurfaceInsets = mWindowAttributes.hasManualSurfaceInsets; 1397 1398 if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags 1399 & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0 1400 && (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) { 1401 Slog.d(mTag, "setLayoutParams: FLAG_KEEP_SCREEN_ON from true to false!"); 1402 } 1403 1404 // Keep track of the actual window flags supplied by the client. 1405 mClientWindowLayoutFlags = attrs.flags; 1406 1407 // Preserve compatible window flag if exists. 1408 final int compatibleWindowFlag = mWindowAttributes.privateFlags 1409 & WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; 1410 1411 // Transfer over system UI visibility values as they carry current state. 1412 attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility; 1413 attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility; 1414 1415 final int changes = mWindowAttributes.copyFrom(attrs); 1416 if ((changes & WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) { 1417 // Recompute system ui visibility. 1418 mAttachInfo.mRecomputeGlobalAttributes = true; 1419 } 1420 if ((changes & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) { 1421 // Request to update light center. 1422 mAttachInfo.mNeedsUpdateLightCenter = true; 1423 } 1424 if (mWindowAttributes.packageName == null) { 1425 mWindowAttributes.packageName = mBasePackageName; 1426 } 1427 mWindowAttributes.privateFlags |= compatibleWindowFlag; 1428 1429 mWindowAttributes.privateFlags |= 1430 WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; 1431 1432 if (mWindowAttributes.preservePreviousSurfaceInsets) { 1433 // Restore old surface insets. 1434 mWindowAttributes.surfaceInsets.set( 1435 oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom); 1436 mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets; 1437 } else if (mWindowAttributes.surfaceInsets.left != oldInsetLeft 1438 || mWindowAttributes.surfaceInsets.top != oldInsetTop 1439 || mWindowAttributes.surfaceInsets.right != oldInsetRight 1440 || mWindowAttributes.surfaceInsets.bottom != oldInsetBottom) { 1441 mNeedsRendererSetup = true; 1442 } 1443 1444 applyKeepScreenOnFlag(mWindowAttributes); 1445 1446 if (newView) { 1447 mSoftInputMode = attrs.softInputMode; 1448 requestLayout(); 1449 } 1450 1451 // Don't lose the mode we last auto-computed. 1452 if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) 1453 == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { 1454 mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode 1455 & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) 1456 | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); 1457 } 1458 1459 if ((changes & LayoutParams.SOFT_INPUT_MODE_CHANGED) != 0) { 1460 requestFitSystemWindows(); 1461 } 1462 1463 mWindowAttributesChanged = true; 1464 scheduleTraversals(); 1465 } 1466 } 1467 handleAppVisibility(boolean visible)1468 void handleAppVisibility(boolean visible) { 1469 if (mAppVisible != visible) { 1470 mAppVisible = visible; 1471 mAppVisibilityChanged = true; 1472 scheduleTraversals(); 1473 if (!mAppVisible) { 1474 WindowManagerGlobal.trimForeground(); 1475 } 1476 } 1477 } 1478 handleGetNewSurface()1479 void handleGetNewSurface() { 1480 mNewSurfaceNeeded = true; 1481 mFullRedrawNeeded = true; 1482 scheduleTraversals(); 1483 } 1484 1485 private final DisplayListener mDisplayListener = new DisplayListener() { 1486 @Override 1487 public void onDisplayChanged(int displayId) { 1488 if (mView != null && mDisplay.getDisplayId() == displayId) { 1489 final int oldDisplayState = mAttachInfo.mDisplayState; 1490 final int newDisplayState = mDisplay.getState(); 1491 if (oldDisplayState != newDisplayState) { 1492 mAttachInfo.mDisplayState = newDisplayState; 1493 pokeDrawLockIfNeeded(); 1494 if (oldDisplayState != Display.STATE_UNKNOWN) { 1495 final int oldScreenState = toViewScreenState(oldDisplayState); 1496 final int newScreenState = toViewScreenState(newDisplayState); 1497 if (oldScreenState != newScreenState) { 1498 mView.dispatchScreenStateChanged(newScreenState); 1499 } 1500 if (oldDisplayState == Display.STATE_OFF) { 1501 // Draw was suppressed so we need to for it to happen here. 1502 mFullRedrawNeeded = true; 1503 scheduleTraversals(); 1504 } 1505 } 1506 } 1507 } 1508 } 1509 1510 @Override 1511 public void onDisplayRemoved(int displayId) { 1512 } 1513 1514 @Override 1515 public void onDisplayAdded(int displayId) { 1516 } 1517 1518 private int toViewScreenState(int displayState) { 1519 return displayState == Display.STATE_OFF ? 1520 View.SCREEN_STATE_OFF : View.SCREEN_STATE_ON; 1521 } 1522 }; 1523 1524 /** 1525 * Notify about move to a different display. 1526 * @param displayId The id of the display where this view root is moved to. 1527 * @param config Configuration of the resources on new display after move. 1528 * 1529 * @hide 1530 */ onMovedToDisplay(int displayId, Configuration config)1531 public void onMovedToDisplay(int displayId, Configuration config) { 1532 if (mDisplay.getDisplayId() == displayId) { 1533 return; 1534 } 1535 1536 // Get new instance of display based on current display adjustments. It may be updated later 1537 // if moving between the displays also involved a configuration change. 1538 updateInternalDisplay(displayId, mView.getResources()); 1539 mImeFocusController.onMovedToDisplay(); 1540 mAttachInfo.mDisplayState = mDisplay.getState(); 1541 // Internal state updated, now notify the view hierarchy. 1542 mView.dispatchMovedToDisplay(mDisplay, config); 1543 } 1544 1545 /** 1546 * Updates {@link #mDisplay} to the display object corresponding to {@param displayId}. 1547 * Uses DEFAULT_DISPLAY if there isn't a display object in the system corresponding 1548 * to {@param displayId}. 1549 */ updateInternalDisplay(int displayId, Resources resources)1550 private void updateInternalDisplay(int displayId, Resources resources) { 1551 final Display preferredDisplay = 1552 ResourcesManager.getInstance().getAdjustedDisplay(displayId, resources); 1553 if (preferredDisplay == null) { 1554 // Fallback to use default display. 1555 Slog.w(TAG, "Cannot get desired display with Id: " + displayId); 1556 mDisplay = ResourcesManager.getInstance() 1557 .getAdjustedDisplay(DEFAULT_DISPLAY, resources); 1558 } else { 1559 mDisplay = preferredDisplay; 1560 } 1561 mContext.updateDisplay(mDisplay.getDisplayId()); 1562 } 1563 pokeDrawLockIfNeeded()1564 void pokeDrawLockIfNeeded() { 1565 final int displayState = mAttachInfo.mDisplayState; 1566 if (mView != null && mAdded && mTraversalScheduled 1567 && (displayState == Display.STATE_DOZE 1568 || displayState == Display.STATE_DOZE_SUSPEND)) { 1569 try { 1570 mWindowSession.pokeDrawLock(mWindow); 1571 } catch (RemoteException ex) { 1572 // System server died, oh well. 1573 } 1574 } 1575 } 1576 1577 @Override requestFitSystemWindows()1578 public void requestFitSystemWindows() { 1579 checkThread(); 1580 mApplyInsetsRequested = true; 1581 scheduleTraversals(); 1582 } 1583 notifyInsetsChanged()1584 void notifyInsetsChanged() { 1585 if (sNewInsetsMode == NEW_INSETS_MODE_NONE) { 1586 return; 1587 } 1588 mApplyInsetsRequested = true; 1589 requestLayout(); 1590 1591 // See comment for View.sForceLayoutWhenInsetsChanged 1592 if (View.sForceLayoutWhenInsetsChanged && mView != null) { 1593 forceLayout(mView); 1594 } 1595 1596 // If this changes during traversal, no need to schedule another one as it will dispatch it 1597 // during the current traversal. 1598 if (!mIsInTraversal) { 1599 scheduleTraversals(); 1600 } 1601 } 1602 1603 @Override requestLayout()1604 public void requestLayout() { 1605 if (!mHandlingLayoutInLayoutRequest) { 1606 checkThread(); 1607 mLayoutRequested = true; 1608 scheduleTraversals(); 1609 } 1610 } 1611 1612 @Override isLayoutRequested()1613 public boolean isLayoutRequested() { 1614 return mLayoutRequested; 1615 } 1616 1617 @Override onDescendantInvalidated(@onNull View child, @NonNull View descendant)1618 public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) { 1619 // TODO: Re-enable after camera is fixed or consider targetSdk checking this 1620 // checkThread(); 1621 if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) { 1622 mIsAnimating = true; 1623 } 1624 invalidate(); 1625 } 1626 1627 @UnsupportedAppUsage invalidate()1628 void invalidate() { 1629 mDirty.set(0, 0, mWidth, mHeight); 1630 if (!mWillDrawSoon) { 1631 scheduleTraversals(); 1632 } 1633 } 1634 invalidateWorld(View view)1635 void invalidateWorld(View view) { 1636 view.invalidate(); 1637 if (view instanceof ViewGroup) { 1638 ViewGroup parent = (ViewGroup) view; 1639 for (int i = 0; i < parent.getChildCount(); i++) { 1640 invalidateWorld(parent.getChildAt(i)); 1641 } 1642 } 1643 } 1644 1645 @Override invalidateChild(View child, Rect dirty)1646 public void invalidateChild(View child, Rect dirty) { 1647 invalidateChildInParent(null, dirty); 1648 } 1649 1650 @Override invalidateChildInParent(int[] location, Rect dirty)1651 public ViewParent invalidateChildInParent(int[] location, Rect dirty) { 1652 checkThread(); 1653 if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty); 1654 1655 if (dirty == null) { 1656 invalidate(); 1657 return null; 1658 } else if (dirty.isEmpty() && !mIsAnimating) { 1659 return null; 1660 } 1661 1662 if (mCurScrollY != 0 || mTranslator != null) { 1663 mTempRect.set(dirty); 1664 dirty = mTempRect; 1665 if (mCurScrollY != 0) { 1666 dirty.offset(0, -mCurScrollY); 1667 } 1668 if (mTranslator != null) { 1669 mTranslator.translateRectInAppWindowToScreen(dirty); 1670 } 1671 if (mAttachInfo.mScalingRequired) { 1672 dirty.inset(-1, -1); 1673 } 1674 } 1675 1676 invalidateRectOnScreen(dirty); 1677 1678 return null; 1679 } 1680 invalidateRectOnScreen(Rect dirty)1681 private void invalidateRectOnScreen(Rect dirty) { 1682 final Rect localDirty = mDirty; 1683 1684 // Add the new dirty rect to the current one 1685 localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); 1686 // Intersect with the bounds of the window to skip 1687 // updates that lie outside of the visible region 1688 final float appScale = mAttachInfo.mApplicationScale; 1689 final boolean intersected = localDirty.intersect(0, 0, 1690 (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); 1691 if (!intersected) { 1692 localDirty.setEmpty(); 1693 } 1694 if (!mWillDrawSoon && (intersected || mIsAnimating)) { 1695 scheduleTraversals(); 1696 } 1697 } 1698 setIsAmbientMode(boolean ambient)1699 public void setIsAmbientMode(boolean ambient) { 1700 mIsAmbientMode = ambient; 1701 } 1702 setWindowStopped(boolean stopped)1703 void setWindowStopped(boolean stopped) { 1704 checkThread(); 1705 if (mStopped != stopped) { 1706 mStopped = stopped; 1707 final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer; 1708 if (renderer != null) { 1709 if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped); 1710 renderer.setStopped(mStopped); 1711 } 1712 if (!mStopped) { 1713 mNewSurfaceNeeded = true; 1714 scheduleTraversals(); 1715 } else { 1716 if (renderer != null) { 1717 renderer.destroyHardwareResources(mView); 1718 } 1719 1720 if (mSurface.isValid()) { 1721 if (mSurfaceHolder != null) { 1722 notifyHolderSurfaceDestroyed(); 1723 } 1724 notifySurfaceDestroyed(); 1725 } 1726 destroySurface(); 1727 } 1728 } 1729 scheduleConsumeBatchedInputImmediately(); 1730 } 1731 1732 1733 /** Register callbacks to be notified when the ViewRootImpl surface changes. */ 1734 interface SurfaceChangedCallback { surfaceCreated(Transaction t)1735 void surfaceCreated(Transaction t); surfaceReplaced(Transaction t)1736 void surfaceReplaced(Transaction t); surfaceDestroyed()1737 void surfaceDestroyed(); 1738 } 1739 1740 private final ArrayList<SurfaceChangedCallback> mSurfaceChangedCallbacks = new ArrayList<>(); addSurfaceChangedCallback(SurfaceChangedCallback c)1741 void addSurfaceChangedCallback(SurfaceChangedCallback c) { 1742 mSurfaceChangedCallbacks.add(c); 1743 } 1744 removeSurfaceChangedCallback(SurfaceChangedCallback c)1745 void removeSurfaceChangedCallback(SurfaceChangedCallback c) { 1746 mSurfaceChangedCallbacks.remove(c); 1747 } 1748 notifySurfaceCreated()1749 private void notifySurfaceCreated() { 1750 for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) { 1751 mSurfaceChangedCallbacks.get(i).surfaceCreated(mSurfaceChangedTransaction); 1752 } 1753 } 1754 1755 /** 1756 * Notify listeners when the ViewRootImpl surface has been replaced. This callback will not be 1757 * called if a new surface is created, only if the valid surface has been replaced with another 1758 * valid surface. 1759 */ notifySurfaceReplaced()1760 private void notifySurfaceReplaced() { 1761 for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) { 1762 mSurfaceChangedCallbacks.get(i).surfaceReplaced(mSurfaceChangedTransaction); 1763 } 1764 } 1765 notifySurfaceDestroyed()1766 private void notifySurfaceDestroyed() { 1767 for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) { 1768 mSurfaceChangedCallbacks.get(i).surfaceDestroyed(); 1769 } 1770 } 1771 1772 /** 1773 * @return child layer with the same bounds as its parent {@code mSurface} and cropped to the 1774 * surface insets. If the layer does not exist, it is created. 1775 * 1776 * <p>Parenting to this layer will ensure that its children are cropped by the view's surface 1777 * insets. 1778 */ getBoundsLayer()1779 public SurfaceControl getBoundsLayer() { 1780 if (mBoundsLayer == null) { 1781 mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession) 1782 .setContainerLayer() 1783 .setName("Bounds for - " + getTitle().toString()) 1784 .setParent(getRenderSurfaceControl()) 1785 .setCallsite("ViewRootImpl.getBoundsLayer") 1786 .build(); 1787 setBoundsLayerCrop(); 1788 mTransaction.show(mBoundsLayer).apply(); 1789 } 1790 return mBoundsLayer; 1791 } 1792 getOrCreateBLASTSurface(int width, int height)1793 Surface getOrCreateBLASTSurface(int width, int height) { 1794 if (mSurfaceControl == null 1795 || !mSurfaceControl.isValid() 1796 || mBlastSurfaceControl == null 1797 || !mBlastSurfaceControl.isValid()) { 1798 return null; 1799 } 1800 1801 Surface ret = null; 1802 if (mBlastBufferQueue == null) { 1803 mBlastBufferQueue = new BLASTBufferQueue( 1804 mBlastSurfaceControl, width, height, mEnableTripleBuffering); 1805 // We only return the Surface the first time, as otherwise 1806 // it hasn't changed and there is no need to update. 1807 ret = mBlastBufferQueue.getSurface(); 1808 } else { 1809 mBlastBufferQueue.update(mBlastSurfaceControl, width, height); 1810 } 1811 1812 return ret; 1813 } 1814 setBoundsLayerCrop()1815 private void setBoundsLayerCrop() { 1816 // mWinFrame is already adjusted for surface insets. So offset it and use it as 1817 // the cropping bounds. 1818 mTempBoundsRect.set(mWinFrame); 1819 mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left, 1820 mWindowAttributes.surfaceInsets.top); 1821 mTransaction.setWindowCrop(mBoundsLayer, mTempBoundsRect); 1822 } 1823 1824 /** 1825 * Called after window layout to update the bounds surface. If the surface insets have changed 1826 * or the surface has resized, update the bounds surface. 1827 * 1828 * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl. 1829 */ updateBoundsLayer(boolean shouldReparent)1830 private void updateBoundsLayer(boolean shouldReparent) { 1831 if (mBoundsLayer != null) { 1832 setBoundsLayerCrop(); 1833 mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(), 1834 mSurface.getNextFrameNumber()); 1835 1836 if (shouldReparent) { 1837 mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl()); 1838 } 1839 mTransaction.apply(); 1840 } 1841 } 1842 destroySurface()1843 private void destroySurface() { 1844 if (mBoundsLayer != null) { 1845 mBoundsLayer.release(); 1846 mBoundsLayer = null; 1847 } 1848 mSurface.release(); 1849 mSurfaceControl.release(); 1850 1851 mBlastSurfaceControl.release(); 1852 // We should probably add an explicit dispose. 1853 mBlastBufferQueue = null; 1854 } 1855 1856 /** 1857 * Block the input events during an Activity Transition. The KEYCODE_BACK event is allowed 1858 * through to allow quick reversal of the Activity Transition. 1859 * 1860 * @param paused true to pause, false to resume. 1861 */ setPausedForTransition(boolean paused)1862 public void setPausedForTransition(boolean paused) { 1863 mPausedForTransition = paused; 1864 } 1865 1866 @Override getParent()1867 public ViewParent getParent() { 1868 return null; 1869 } 1870 1871 @Override getChildVisibleRect(View child, Rect r, android.graphics.Point offset)1872 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { 1873 if (child != mView) { 1874 throw new RuntimeException("child is not mine, honest!"); 1875 } 1876 // Note: don't apply scroll offset, because we want to know its 1877 // visibility in the virtual canvas being given to the view hierarchy. 1878 return r.intersect(0, 0, mWidth, mHeight); 1879 } 1880 1881 @Override bringChildToFront(View child)1882 public void bringChildToFront(View child) { 1883 } 1884 getHostVisibility()1885 int getHostVisibility() { 1886 return (mAppVisible || mForceDecorViewVisibility) ? mView.getVisibility() : View.GONE; 1887 } 1888 1889 /** 1890 * Add LayoutTransition to the list of transitions to be started in the next traversal. 1891 * This list will be cleared after the transitions on the list are start()'ed. These 1892 * transitionsa re added by LayoutTransition itself when it sets up animations. The setup 1893 * happens during the layout phase of traversal, which we want to complete before any of the 1894 * animations are started (because those animations may side-effect properties that layout 1895 * depends upon, like the bounding rectangles of the affected views). So we add the transition 1896 * to the list and it is started just prior to starting the drawing phase of traversal. 1897 * 1898 * @param transition The LayoutTransition to be started on the next traversal. 1899 * 1900 * @hide 1901 */ requestTransitionStart(LayoutTransition transition)1902 public void requestTransitionStart(LayoutTransition transition) { 1903 if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) { 1904 if (mPendingTransitions == null) { 1905 mPendingTransitions = new ArrayList<LayoutTransition>(); 1906 } 1907 mPendingTransitions.add(transition); 1908 } 1909 } 1910 1911 /** 1912 * Notifies the HardwareRenderer that a new frame will be coming soon. 1913 * Currently only {@link ThreadedRenderer} cares about this, and uses 1914 * this knowledge to adjust the scheduling of off-thread animations 1915 */ notifyRendererOfFramePending()1916 void notifyRendererOfFramePending() { 1917 if (mAttachInfo.mThreadedRenderer != null) { 1918 mAttachInfo.mThreadedRenderer.notifyFramePending(); 1919 } 1920 } 1921 1922 @UnsupportedAppUsage scheduleTraversals()1923 void scheduleTraversals() { 1924 if (!mTraversalScheduled) { 1925 mTraversalScheduled = true; 1926 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); 1927 mChoreographer.postCallback( 1928 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 1929 notifyRendererOfFramePending(); 1930 pokeDrawLockIfNeeded(); 1931 } 1932 } 1933 unscheduleTraversals()1934 void unscheduleTraversals() { 1935 if (mTraversalScheduled) { 1936 mTraversalScheduled = false; 1937 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); 1938 mChoreographer.removeCallbacks( 1939 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 1940 } 1941 } 1942 doTraversal()1943 void doTraversal() { 1944 if (mTraversalScheduled) { 1945 mTraversalScheduled = false; 1946 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); 1947 1948 if (mProfile) { 1949 Debug.startMethodTracing("ViewAncestor"); 1950 } 1951 1952 performTraversals(); 1953 1954 if (mProfile) { 1955 Debug.stopMethodTracing(); 1956 mProfile = false; 1957 } 1958 } 1959 } 1960 applyKeepScreenOnFlag(WindowManager.LayoutParams params)1961 private void applyKeepScreenOnFlag(WindowManager.LayoutParams params) { 1962 // Update window's global keep screen on flag: if a view has requested 1963 // that the screen be kept on, then it is always set; otherwise, it is 1964 // set to whatever the client last requested for the global state. 1965 if (mAttachInfo.mKeepScreenOn) { 1966 params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 1967 } else { 1968 params.flags = (params.flags&~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) 1969 | (mClientWindowLayoutFlags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1970 } 1971 } 1972 collectViewAttributes()1973 private boolean collectViewAttributes() { 1974 if (mAttachInfo.mRecomputeGlobalAttributes) { 1975 //Log.i(mTag, "Computing view hierarchy attributes!"); 1976 mAttachInfo.mRecomputeGlobalAttributes = false; 1977 boolean oldScreenOn = mAttachInfo.mKeepScreenOn; 1978 mAttachInfo.mKeepScreenOn = false; 1979 mAttachInfo.mSystemUiVisibility = 0; 1980 mAttachInfo.mHasSystemUiListeners = false; 1981 mView.dispatchCollectViewAttributes(mAttachInfo, 0); 1982 mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility; 1983 WindowManager.LayoutParams params = mWindowAttributes; 1984 mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params); 1985 mCompatibleVisibilityInfo.globalVisibility = 1986 (mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE) 1987 | (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); 1988 if (mDispatchedSystemUiVisibility != mCompatibleVisibilityInfo.globalVisibility) { 1989 mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY); 1990 mHandler.sendMessage(mHandler.obtainMessage( 1991 MSG_DISPATCH_SYSTEM_UI_VISIBILITY, mCompatibleVisibilityInfo)); 1992 } 1993 if (mAttachInfo.mKeepScreenOn != oldScreenOn 1994 || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility 1995 || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) { 1996 applyKeepScreenOnFlag(params); 1997 params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility; 1998 params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners; 1999 mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility); 2000 return true; 2001 } 2002 } 2003 return false; 2004 } 2005 getImpliedSystemUiVisibility(WindowManager.LayoutParams params)2006 private int getImpliedSystemUiVisibility(WindowManager.LayoutParams params) { 2007 int vis = 0; 2008 // Translucent decor window flags imply stable system ui visibility. 2009 if ((params.flags & FLAG_TRANSLUCENT_STATUS) != 0) { 2010 vis |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 2011 } 2012 if ((params.flags & FLAG_TRANSLUCENT_NAVIGATION) != 0) { 2013 vis |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; 2014 } 2015 return vis; 2016 } 2017 2018 /** 2019 * Update the compatible system UI visibility for dispatching it to the legacy app. 2020 * 2021 * @param type Indicates which type of the insets source we are handling. 2022 * @param visible True if the insets source is visible. 2023 * @param hasControl True if we can control the insets source. 2024 */ updateCompatSysUiVisibility(@nternalInsetsType int type, boolean visible, boolean hasControl)2025 void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible, 2026 boolean hasControl) { 2027 if ((type != ITYPE_STATUS_BAR && type != ITYPE_NAVIGATION_BAR) 2028 || ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) { 2029 return; 2030 } 2031 final SystemUiVisibilityInfo info = mCompatibleVisibilityInfo; 2032 final int systemUiFlag = type == ITYPE_STATUS_BAR 2033 ? View.SYSTEM_UI_FLAG_FULLSCREEN 2034 : View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 2035 final boolean wasVisible = (info.globalVisibility & systemUiFlag) == 0; 2036 if (visible) { 2037 info.globalVisibility &= ~systemUiFlag; 2038 if (!wasVisible && hasControl) { 2039 // The local system UI visibility can only be cleared while we have the control. 2040 info.localChanges |= systemUiFlag; 2041 } 2042 } else { 2043 info.globalVisibility |= systemUiFlag; 2044 info.localChanges &= ~systemUiFlag; 2045 } 2046 if (mDispatchedSystemUiVisibility != info.globalVisibility) { 2047 mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY); 2048 mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, info)); 2049 } 2050 } 2051 handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args)2052 private void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) { 2053 if (mSeq != args.seq && sNewInsetsMode != NEW_INSETS_MODE_FULL) { 2054 // The sequence has changed, so we need to update our value and make 2055 // sure to do a traversal afterward so the window manager is given our 2056 // most recent data. 2057 mSeq = args.seq; 2058 mAttachInfo.mForceReportNewAttributes = true; 2059 scheduleTraversals(); 2060 } 2061 if (mView == null) return; 2062 if (args.localChanges != 0) { 2063 mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges); 2064 args.localChanges = 0; 2065 } 2066 2067 final int visibility = args.globalVisibility & View.SYSTEM_UI_CLEARABLE_FLAGS; 2068 if (mDispatchedSystemUiVisibility != visibility) { 2069 mDispatchedSystemUiVisibility = visibility; 2070 mView.dispatchSystemUiVisibilityChanged(visibility); 2071 } 2072 } 2073 2074 @VisibleForTesting adjustLayoutParamsForCompatibility(WindowManager.LayoutParams inOutParams)2075 public static void adjustLayoutParamsForCompatibility(WindowManager.LayoutParams inOutParams) { 2076 if (sNewInsetsMode != NEW_INSETS_MODE_FULL) { 2077 return; 2078 } 2079 final int sysUiVis = inOutParams.systemUiVisibility | inOutParams.subtreeSystemUiVisibility; 2080 final int flags = inOutParams.flags; 2081 final int type = inOutParams.type; 2082 2083 if ((inOutParams.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) { 2084 inOutParams.insetsFlags.appearance = 0; 2085 if ((sysUiVis & SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { 2086 inOutParams.insetsFlags.appearance |= APPEARANCE_LOW_PROFILE_BARS; 2087 } 2088 if ((sysUiVis & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) { 2089 inOutParams.insetsFlags.appearance |= APPEARANCE_LIGHT_STATUS_BARS; 2090 } 2091 if ((sysUiVis & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0) { 2092 inOutParams.insetsFlags.appearance |= APPEARANCE_LIGHT_NAVIGATION_BARS; 2093 } 2094 } 2095 2096 if ((inOutParams.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) == 0) { 2097 if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0 2098 || (flags & FLAG_FULLSCREEN) != 0) { 2099 inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 2100 } else if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE) != 0) { 2101 inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE; 2102 } else { 2103 inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH; 2104 } 2105 } 2106 2107 if ((inOutParams.privateFlags & PRIVATE_FLAG_FIT_INSETS_CONTROLLED) != 0) { 2108 return; 2109 } 2110 2111 int types = inOutParams.getFitInsetsTypes(); 2112 int sides = inOutParams.getFitInsetsSides(); 2113 boolean ignoreVis = inOutParams.isFitInsetsIgnoringVisibility(); 2114 2115 if (((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0 2116 || (flags & FLAG_LAYOUT_IN_SCREEN) != 0) 2117 || (flags & FLAG_TRANSLUCENT_STATUS) != 0) { 2118 types &= ~Type.statusBars(); 2119 } 2120 if ((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 2121 || (flags & FLAG_TRANSLUCENT_NAVIGATION) != 0) { 2122 types &= ~Type.systemBars(); 2123 } 2124 if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { 2125 ignoreVis = true; 2126 } else if ((types & Type.systemBars()) == Type.systemBars()) { 2127 types |= Type.ime(); 2128 } 2129 inOutParams.setFitInsetsTypes(types); 2130 inOutParams.setFitInsetsSides(sides); 2131 inOutParams.setFitInsetsIgnoringVisibility(ignoreVis); 2132 2133 // The fitting of insets are not really controlled by the clients, so we remove the flag. 2134 inOutParams.privateFlags &= ~PRIVATE_FLAG_FIT_INSETS_CONTROLLED; 2135 } 2136 controlInsetsForCompatibility(WindowManager.LayoutParams params)2137 private void controlInsetsForCompatibility(WindowManager.LayoutParams params) { 2138 if (sNewInsetsMode != NEW_INSETS_MODE_FULL) { 2139 return; 2140 } 2141 final int sysUiVis = params.systemUiVisibility | params.subtreeSystemUiVisibility; 2142 final int flags = params.flags; 2143 final boolean matchParent = params.width == MATCH_PARENT && params.height == MATCH_PARENT; 2144 final boolean nonAttachedAppWindow = params.type >= FIRST_APPLICATION_WINDOW 2145 && params.type <= LAST_APPLICATION_WINDOW; 2146 final boolean statusWasHiddenByFlags = (mTypesHiddenByFlags & Type.statusBars()) != 0; 2147 final boolean statusIsHiddenByFlags = (sysUiVis & SYSTEM_UI_FLAG_FULLSCREEN) != 0 2148 || ((flags & FLAG_FULLSCREEN) != 0 && matchParent && nonAttachedAppWindow); 2149 final boolean navWasHiddenByFlags = (mTypesHiddenByFlags & Type.navigationBars()) != 0; 2150 final boolean navIsHiddenByFlags = (sysUiVis & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; 2151 2152 @InsetsType int typesToHide = 0; 2153 @InsetsType int typesToShow = 0; 2154 if (statusIsHiddenByFlags && !statusWasHiddenByFlags) { 2155 typesToHide |= Type.statusBars(); 2156 } else if (!statusIsHiddenByFlags && statusWasHiddenByFlags) { 2157 typesToShow |= Type.statusBars(); 2158 } 2159 if (navIsHiddenByFlags && !navWasHiddenByFlags) { 2160 typesToHide |= Type.navigationBars(); 2161 } else if (!navIsHiddenByFlags && navWasHiddenByFlags) { 2162 typesToShow |= Type.navigationBars(); 2163 } 2164 if (typesToHide != 0) { 2165 getInsetsController().hide(typesToHide); 2166 } 2167 if (typesToShow != 0) { 2168 getInsetsController().show(typesToShow); 2169 } 2170 mTypesHiddenByFlags |= typesToHide; 2171 mTypesHiddenByFlags &= ~typesToShow; 2172 } 2173 measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWindowWidth, final int desiredWindowHeight)2174 private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, 2175 final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) { 2176 int childWidthMeasureSpec; 2177 int childHeightMeasureSpec; 2178 boolean windowSizeMayChange = false; 2179 2180 if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag, 2181 "Measuring " + host + " in display " + desiredWindowWidth 2182 + "x" + desiredWindowHeight + "..."); 2183 2184 boolean goodMeasure = false; 2185 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { 2186 // On large screens, we don't want to allow dialogs to just 2187 // stretch to fill the entire width of the screen to display 2188 // one line of text. First try doing the layout at a smaller 2189 // size to see if it will fit. 2190 final DisplayMetrics packageMetrics = res.getDisplayMetrics(); 2191 res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); 2192 int baseSize = 0; 2193 if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { 2194 baseSize = (int)mTmpValue.getDimension(packageMetrics); 2195 } 2196 if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize 2197 + ", desiredWindowWidth=" + desiredWindowWidth); 2198 if (baseSize != 0 && desiredWindowWidth > baseSize) { 2199 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); 2200 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); 2201 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 2202 if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured (" 2203 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() 2204 + ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec) 2205 + " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec)); 2206 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { 2207 goodMeasure = true; 2208 } else { 2209 // Didn't fit in that size... try expanding a bit. 2210 baseSize = (baseSize+desiredWindowWidth)/2; 2211 if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize=" 2212 + baseSize); 2213 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); 2214 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 2215 if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured (" 2216 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); 2217 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { 2218 if (DEBUG_DIALOG) Log.v(mTag, "Good!"); 2219 goodMeasure = true; 2220 } 2221 } 2222 } 2223 } 2224 2225 if (!goodMeasure) { 2226 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); 2227 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); 2228 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 2229 if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { 2230 windowSizeMayChange = true; 2231 } 2232 } 2233 2234 if (DBG) { 2235 System.out.println("======================================"); 2236 System.out.println("performTraversals -- after measure"); 2237 host.debug(); 2238 } 2239 2240 return windowSizeMayChange; 2241 } 2242 2243 /** 2244 * Modifies the input matrix such that it maps view-local coordinates to 2245 * on-screen coordinates. 2246 * 2247 * @param m input matrix to modify 2248 */ transformMatrixToGlobal(Matrix m)2249 void transformMatrixToGlobal(Matrix m) { 2250 m.preTranslate(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop); 2251 } 2252 2253 /** 2254 * Modifies the input matrix such that it maps on-screen coordinates to 2255 * view-local coordinates. 2256 * 2257 * @param m input matrix to modify 2258 */ transformMatrixToLocal(Matrix m)2259 void transformMatrixToLocal(Matrix m) { 2260 m.postTranslate(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop); 2261 } 2262 getWindowInsets(boolean forceConstruct)2263 /* package */ WindowInsets getWindowInsets(boolean forceConstruct) { 2264 if (mLastWindowInsets == null || forceConstruct) { 2265 mLastWindowInsets = mInsetsController.calculateInsets( 2266 mContext.getResources().getConfiguration().isScreenRound(), 2267 mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(), 2268 mWindowAttributes.softInputMode, mWindowAttributes.flags, 2269 (mWindowAttributes.systemUiVisibility 2270 | mWindowAttributes.subtreeSystemUiVisibility)); 2271 2272 Rect visibleInsets = mInsetsController.calculateVisibleInsets( 2273 mWindowAttributes.softInputMode); 2274 2275 mAttachInfo.mVisibleInsets.set(visibleInsets); 2276 mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect()); 2277 mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect()); 2278 } 2279 return mLastWindowInsets; 2280 } 2281 dispatchApplyInsets(View host)2282 public void dispatchApplyInsets(View host) { 2283 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchApplyInsets"); 2284 mApplyInsetsRequested = false; 2285 WindowInsets insets = getWindowInsets(true /* forceConstruct */); 2286 if (!shouldDispatchCutout()) { 2287 // Window is either not laid out in cutout or the status bar inset takes care of 2288 // clearing the cutout, so we don't need to dispatch the cutout to the hierarchy. 2289 insets = insets.consumeDisplayCutout(); 2290 } 2291 host.dispatchApplyWindowInsets(insets); 2292 mAttachInfo.delayNotifyContentCaptureInsetsEvent(insets.getInsets(Type.all())); 2293 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 2294 } 2295 updateCaptionInsets()2296 private boolean updateCaptionInsets() { 2297 if (!(mView instanceof DecorView)) return false; 2298 final int captionInsetsHeight = ((DecorView) mView).getCaptionInsetsHeight(); 2299 final Rect captionFrame = new Rect(); 2300 if (captionInsetsHeight != 0) { 2301 captionFrame.set(mWinFrame.left, mWinFrame.top, mWinFrame.right, 2302 mWinFrame.top + captionInsetsHeight); 2303 } 2304 if (mAttachInfo.mCaptionInsets.equals(captionFrame)) return false; 2305 mAttachInfo.mCaptionInsets.set(captionFrame); 2306 return true; 2307 } 2308 shouldDispatchCutout()2309 private boolean shouldDispatchCutout() { 2310 return mWindowAttributes.layoutInDisplayCutoutMode 2311 == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS 2312 || mWindowAttributes.layoutInDisplayCutoutMode 2313 == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 2314 } 2315 2316 @VisibleForTesting getInsetsController()2317 public InsetsController getInsetsController() { 2318 return mInsetsController; 2319 } 2320 shouldUseDisplaySize(final WindowManager.LayoutParams lp)2321 private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) { 2322 return lp.type == TYPE_STATUS_BAR_ADDITIONAL 2323 || lp.type == TYPE_INPUT_METHOD 2324 || lp.type == TYPE_VOLUME_OVERLAY; 2325 } 2326 dipToPx(int dip)2327 int dipToPx(int dip) { 2328 final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); 2329 return (int) (displayMetrics.density * dip + 0.5f); 2330 } 2331 performTraversals()2332 private void performTraversals() { 2333 // cache mView since it is used so much below... 2334 final View host = mView; 2335 2336 if (DBG) { 2337 System.out.println("======================================"); 2338 System.out.println("performTraversals"); 2339 host.debug(); 2340 } 2341 2342 if (host == null || !mAdded) 2343 return; 2344 2345 mIsInTraversal = true; 2346 mWillDrawSoon = true; 2347 boolean windowSizeMayChange = false; 2348 WindowManager.LayoutParams lp = mWindowAttributes; 2349 2350 int desiredWindowWidth; 2351 int desiredWindowHeight; 2352 2353 final int viewVisibility = getHostVisibility(); 2354 final boolean viewVisibilityChanged = !mFirst 2355 && (mViewVisibility != viewVisibility || mNewSurfaceNeeded 2356 // Also check for possible double visibility update, which will make current 2357 // viewVisibility value equal to mViewVisibility and we may miss it. 2358 || mAppVisibilityChanged); 2359 mAppVisibilityChanged = false; 2360 final boolean viewUserVisibilityChanged = !mFirst && 2361 ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE)); 2362 2363 WindowManager.LayoutParams params = null; 2364 CompatibilityInfo compatibilityInfo = 2365 mDisplay.getDisplayAdjustments().getCompatibilityInfo(); 2366 if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { 2367 params = lp; 2368 mFullRedrawNeeded = true; 2369 mLayoutRequested = true; 2370 if (mLastInCompatMode) { 2371 params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; 2372 mLastInCompatMode = false; 2373 } else { 2374 params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; 2375 mLastInCompatMode = true; 2376 } 2377 } 2378 2379 Rect frame = mWinFrame; 2380 if (mFirst) { 2381 mFullRedrawNeeded = true; 2382 mLayoutRequested = true; 2383 2384 final Configuration config = mContext.getResources().getConfiguration(); 2385 if (shouldUseDisplaySize(lp)) { 2386 // NOTE -- system code, won't try to do compat mode. 2387 Point size = new Point(); 2388 mDisplay.getRealSize(size); 2389 desiredWindowWidth = size.x; 2390 desiredWindowHeight = size.y; 2391 } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT 2392 || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { 2393 // For wrap content, we have to remeasure later on anyways. Use size consistent with 2394 // below so we get best use of the measure cache. 2395 desiredWindowWidth = dipToPx(config.screenWidthDp); 2396 desiredWindowHeight = dipToPx(config.screenHeightDp); 2397 } else { 2398 // After addToDisplay, the frame contains the frameHint from window manager, which 2399 // for most windows is going to be the same size as the result of relayoutWindow. 2400 // Using this here allows us to avoid remeasuring after relayoutWindow 2401 desiredWindowWidth = frame.width(); 2402 desiredWindowHeight = frame.height(); 2403 } 2404 2405 // We used to use the following condition to choose 32 bits drawing caches: 2406 // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888 2407 // However, windows are now always 32 bits by default, so choose 32 bits 2408 mAttachInfo.mUse32BitDrawingCache = true; 2409 mAttachInfo.mWindowVisibility = viewVisibility; 2410 mAttachInfo.mRecomputeGlobalAttributes = false; 2411 mLastConfigurationFromResources.setTo(config); 2412 mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; 2413 // Set the layout direction if it has not been set before (inherit is the default) 2414 if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { 2415 host.setLayoutDirection(config.getLayoutDirection()); 2416 } 2417 host.dispatchAttachedToWindow(mAttachInfo, 0); 2418 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true); 2419 dispatchApplyInsets(host); 2420 } else { 2421 desiredWindowWidth = frame.width(); 2422 desiredWindowHeight = frame.height(); 2423 if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { 2424 if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame); 2425 mFullRedrawNeeded = true; 2426 mLayoutRequested = true; 2427 windowSizeMayChange = true; 2428 } 2429 } 2430 2431 if (viewVisibilityChanged) { 2432 mAttachInfo.mWindowVisibility = viewVisibility; 2433 host.dispatchWindowVisibilityChanged(viewVisibility); 2434 if (viewUserVisibilityChanged) { 2435 host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); 2436 } 2437 if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { 2438 endDragResizing(); 2439 destroyHardwareResources(); 2440 } 2441 } 2442 2443 // Non-visible windows can't hold accessibility focus. 2444 if (mAttachInfo.mWindowVisibility != View.VISIBLE) { 2445 host.clearAccessibilityFocus(); 2446 } 2447 2448 // Execute enqueued actions on every traversal in case a detached view enqueued an action 2449 getRunQueue().executeActions(mAttachInfo.mHandler); 2450 2451 boolean cutoutChanged = false; 2452 2453 boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); 2454 if (layoutRequested) { 2455 2456 final Resources res = mView.getContext().getResources(); 2457 2458 if (mFirst) { 2459 // make sure touch mode code executes by setting cached value 2460 // to opposite of the added touch mode. 2461 mAttachInfo.mInTouchMode = !mAddedTouchMode; 2462 ensureTouchModeLocally(mAddedTouchMode); 2463 } else { 2464 if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) { 2465 cutoutChanged = true; 2466 } 2467 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT 2468 || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { 2469 windowSizeMayChange = true; 2470 2471 if (shouldUseDisplaySize(lp)) { 2472 // NOTE -- system code, won't try to do compat mode. 2473 Point size = new Point(); 2474 mDisplay.getRealSize(size); 2475 desiredWindowWidth = size.x; 2476 desiredWindowHeight = size.y; 2477 } else { 2478 Configuration config = res.getConfiguration(); 2479 desiredWindowWidth = dipToPx(config.screenWidthDp); 2480 desiredWindowHeight = dipToPx(config.screenHeightDp); 2481 } 2482 } 2483 } 2484 2485 // Ask host how big it wants to be 2486 windowSizeMayChange |= measureHierarchy(host, lp, res, 2487 desiredWindowWidth, desiredWindowHeight); 2488 } 2489 2490 if (collectViewAttributes()) { 2491 params = lp; 2492 } 2493 if (mAttachInfo.mForceReportNewAttributes) { 2494 mAttachInfo.mForceReportNewAttributes = false; 2495 params = lp; 2496 } 2497 2498 if (mFirst || mAttachInfo.mViewVisibilityChanged) { 2499 mAttachInfo.mViewVisibilityChanged = false; 2500 int resizeMode = mSoftInputMode & 2501 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; 2502 // If we are in auto resize mode, then we need to determine 2503 // what mode to use now. 2504 if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { 2505 final int N = mAttachInfo.mScrollContainers.size(); 2506 for (int i=0; i<N; i++) { 2507 if (mAttachInfo.mScrollContainers.get(i).isShown()) { 2508 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 2509 } 2510 } 2511 if (resizeMode == 0) { 2512 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; 2513 } 2514 if ((lp.softInputMode & 2515 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) { 2516 lp.softInputMode = (lp.softInputMode & 2517 ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) | 2518 resizeMode; 2519 params = lp; 2520 } 2521 } 2522 } 2523 2524 if (mApplyInsetsRequested) { 2525 dispatchApplyInsets(host); 2526 if (mLayoutRequested) { 2527 // Short-circuit catching a new layout request here, so 2528 // we don't need to go through two layout passes when things 2529 // change due to fitting system windows, which can happen a lot. 2530 windowSizeMayChange |= measureHierarchy(host, lp, 2531 mView.getContext().getResources(), 2532 desiredWindowWidth, desiredWindowHeight); 2533 } 2534 } 2535 2536 if (layoutRequested) { 2537 // Clear this now, so that if anything requests a layout in the 2538 // rest of this function we will catch it and re-run a full 2539 // layout pass. 2540 mLayoutRequested = false; 2541 } 2542 2543 boolean windowShouldResize = layoutRequested && windowSizeMayChange 2544 && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) 2545 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && 2546 frame.width() < desiredWindowWidth && frame.width() != mWidth) 2547 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT && 2548 frame.height() < desiredWindowHeight && frame.height() != mHeight)); 2549 windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM; 2550 2551 // If the activity was just relaunched, it might have unfrozen the task bounds (while 2552 // relaunching), so we need to force a call into window manager to pick up the latest 2553 // bounds. 2554 windowShouldResize |= mActivityRelaunched; 2555 2556 // Determine whether to compute insets. 2557 // If there are no inset listeners remaining then we may still need to compute 2558 // insets in case the old insets were non-empty and must be reset. 2559 final boolean computesInternalInsets = 2560 mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners() 2561 || mAttachInfo.mHasNonEmptyGivenInternalInsets; 2562 2563 boolean insetsPending = false; 2564 int relayoutResult = 0; 2565 boolean updatedConfiguration = false; 2566 2567 final int surfaceGenerationId = mSurface.getGenerationId(); 2568 2569 final boolean isViewVisible = viewVisibility == View.VISIBLE; 2570 final boolean windowRelayoutWasForced = mForceNextWindowRelayout; 2571 boolean surfaceSizeChanged = false; 2572 boolean surfaceCreated = false; 2573 boolean surfaceDestroyed = false; 2574 /* True if surface generation id changes. */ 2575 boolean surfaceReplaced = false; 2576 2577 final boolean windowAttributesChanged = mWindowAttributesChanged; 2578 if (windowAttributesChanged) { 2579 mWindowAttributesChanged = false; 2580 params = lp; 2581 } 2582 2583 if (params != null) { 2584 if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0 2585 && !PixelFormat.formatHasAlpha(params.format)) { 2586 params.format = PixelFormat.TRANSLUCENT; 2587 } 2588 adjustLayoutParamsForCompatibility(params); 2589 controlInsetsForCompatibility(params); 2590 } 2591 2592 if (mFirst || windowShouldResize || viewVisibilityChanged || cutoutChanged || params != null 2593 || mForceNextWindowRelayout) { 2594 mForceNextWindowRelayout = false; 2595 2596 if (isViewVisible) { 2597 // If this window is giving internal insets to the window 2598 // manager, and it is being added or changing its visibility, 2599 // then we want to first give the window manager "fake" 2600 // insets to cause it to effectively ignore the content of 2601 // the window during layout. This avoids it briefly causing 2602 // other windows to resize/move based on the raw frame of the 2603 // window, waiting until we can finish laying out this window 2604 // and get back to the window manager with the ultimately 2605 // computed insets. 2606 insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged); 2607 } 2608 2609 if (mSurfaceHolder != null) { 2610 mSurfaceHolder.mSurfaceLock.lock(); 2611 mDrawingAllowed = true; 2612 } 2613 2614 boolean hwInitialized = false; 2615 boolean dispatchApplyInsets = false; 2616 boolean hadSurface = mSurface.isValid(); 2617 2618 try { 2619 if (DEBUG_LAYOUT) { 2620 Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" + 2621 host.getMeasuredHeight() + ", params=" + params); 2622 } 2623 2624 if (mAttachInfo.mThreadedRenderer != null) { 2625 // relayoutWindow may decide to destroy mSurface. As that decision 2626 // happens in WindowManager service, we need to be defensive here 2627 // and stop using the surface in case it gets destroyed. 2628 if (mAttachInfo.mThreadedRenderer.pause()) { 2629 // Animations were running so we need to push a frame 2630 // to resume them 2631 mDirty.set(0, 0, mWidth, mHeight); 2632 } 2633 mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED); 2634 } 2635 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); 2636 2637 if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString() 2638 + " cutout=" + mPendingDisplayCutout.get().toString() 2639 + " surface=" + mSurface); 2640 2641 // If the pending {@link MergedConfiguration} handed back from 2642 // {@link #relayoutWindow} does not match the one last reported, 2643 // WindowManagerService has reported back a frame from a configuration not yet 2644 // handled by the client. In this case, we need to accept the configuration so we 2645 // do not lay out and draw with the wrong configuration. 2646 if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) { 2647 if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: " 2648 + mPendingMergedConfiguration.getMergedConfiguration()); 2649 performConfigurationChange(mPendingMergedConfiguration, !mFirst, 2650 INVALID_DISPLAY /* same display */); 2651 updatedConfiguration = true; 2652 } 2653 2654 cutoutChanged = !mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout); 2655 surfaceSizeChanged = (relayoutResult 2656 & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0; 2657 final boolean alwaysConsumeSystemBarsChanged = 2658 mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars; 2659 final boolean colorModeChanged = hasColorModeChanged(lp.getColorMode()); 2660 surfaceCreated = !hadSurface && mSurface.isValid(); 2661 surfaceDestroyed = hadSurface && !mSurface.isValid(); 2662 surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()) 2663 && mSurface.isValid(); 2664 2665 if (cutoutChanged) { 2666 mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout); 2667 if (DEBUG_LAYOUT) { 2668 Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout); 2669 } 2670 // Need to relayout with content insets. 2671 dispatchApplyInsets = true; 2672 } 2673 if (alwaysConsumeSystemBarsChanged) { 2674 mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars; 2675 dispatchApplyInsets = true; 2676 } 2677 if (updateCaptionInsets()) { 2678 dispatchApplyInsets = true; 2679 } 2680 if (dispatchApplyInsets || mLastSystemUiVisibility != 2681 mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) { 2682 mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; 2683 dispatchApplyInsets(host); 2684 // We applied insets so force contentInsetsChanged to ensure the 2685 // hierarchy is measured below. 2686 dispatchApplyInsets = true; 2687 } 2688 if (colorModeChanged && mAttachInfo.mThreadedRenderer != null) { 2689 mAttachInfo.mThreadedRenderer.setWideGamut( 2690 lp.getColorMode() == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT); 2691 } 2692 2693 if (surfaceCreated) { 2694 // If we are creating a new surface, then we need to 2695 // completely redraw it. 2696 mFullRedrawNeeded = true; 2697 mPreviousTransparentRegion.setEmpty(); 2698 2699 // Only initialize up-front if transparent regions are not 2700 // requested, otherwise defer to see if the entire window 2701 // will be transparent 2702 if (mAttachInfo.mThreadedRenderer != null) { 2703 try { 2704 hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface); 2705 if (hwInitialized && (host.mPrivateFlags 2706 & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) { 2707 // Don't pre-allocate if transparent regions 2708 // are requested as they may not be needed 2709 mAttachInfo.mThreadedRenderer.allocateBuffers(); 2710 } 2711 } catch (OutOfResourcesException e) { 2712 handleOutOfResourcesException(e); 2713 return; 2714 } 2715 } 2716 notifySurfaceCreated(); 2717 } else if (surfaceDestroyed) { 2718 // If the surface has been removed, then reset the scroll 2719 // positions. 2720 if (mLastScrolledFocus != null) { 2721 mLastScrolledFocus.clear(); 2722 } 2723 mScrollY = mCurScrollY = 0; 2724 if (mView instanceof RootViewSurfaceTaker) { 2725 ((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY); 2726 } 2727 if (mScroller != null) { 2728 mScroller.abortAnimation(); 2729 } 2730 // Our surface is gone 2731 if (mAttachInfo.mThreadedRenderer != null && 2732 mAttachInfo.mThreadedRenderer.isEnabled()) { 2733 mAttachInfo.mThreadedRenderer.destroy(); 2734 } 2735 } else if ((surfaceReplaced 2736 || surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged) 2737 && mSurfaceHolder == null 2738 && mAttachInfo.mThreadedRenderer != null 2739 && mSurface.isValid()) { 2740 mFullRedrawNeeded = true; 2741 try { 2742 // Need to do updateSurface (which leads to CanvasContext::setSurface and 2743 // re-create the EGLSurface) if either the Surface changed (as indicated by 2744 // generation id), or WindowManager changed the surface size. The latter is 2745 // because on some chips, changing the consumer side's BufferQueue size may 2746 // not take effect immediately unless we create a new EGLSurface. 2747 // Note that frame size change doesn't always imply surface size change (eg. 2748 // drag resizing uses fullscreen surface), need to check surfaceSizeChanged 2749 // flag from WindowManager. 2750 mAttachInfo.mThreadedRenderer.updateSurface(mSurface); 2751 } catch (OutOfResourcesException e) { 2752 handleOutOfResourcesException(e); 2753 return; 2754 } 2755 } 2756 2757 if (!surfaceCreated && surfaceReplaced) { 2758 notifySurfaceReplaced(); 2759 } 2760 2761 final boolean freeformResizing = (relayoutResult 2762 & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0; 2763 final boolean dockedResizing = (relayoutResult 2764 & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0; 2765 final boolean dragResizing = freeformResizing || dockedResizing; 2766 if (mDragResizing != dragResizing) { 2767 if (dragResizing) { 2768 mResizeMode = freeformResizing 2769 ? RESIZE_MODE_FREEFORM 2770 : RESIZE_MODE_DOCKED_DIVIDER; 2771 final boolean backdropSizeMatchesFrame = 2772 mWinFrame.width() == mPendingBackDropFrame.width() 2773 && mWinFrame.height() == mPendingBackDropFrame.height(); 2774 // TODO: Need cutout? 2775 startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame, 2776 mLastWindowInsets.getSystemWindowInsets().toRect(), 2777 mLastWindowInsets.getStableInsets().toRect(), mResizeMode); 2778 } else { 2779 // We shouldn't come here, but if we come we should end the resize. 2780 endDragResizing(); 2781 } 2782 } 2783 if (!mUseMTRenderer) { 2784 if (dragResizing) { 2785 mCanvasOffsetX = mWinFrame.left; 2786 mCanvasOffsetY = mWinFrame.top; 2787 } else { 2788 mCanvasOffsetX = mCanvasOffsetY = 0; 2789 } 2790 } 2791 } catch (RemoteException e) { 2792 } 2793 2794 if (DEBUG_ORIENTATION) Log.v( 2795 TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface); 2796 2797 mAttachInfo.mWindowLeft = frame.left; 2798 mAttachInfo.mWindowTop = frame.top; 2799 2800 // !!FIXME!! This next section handles the case where we did not get the 2801 // window size we asked for. We should avoid this by getting a maximum size from 2802 // the window session beforehand. 2803 if (mWidth != frame.width() || mHeight != frame.height()) { 2804 mWidth = frame.width(); 2805 mHeight = frame.height(); 2806 } 2807 2808 if (mSurfaceHolder != null) { 2809 // The app owns the surface; tell it about what is going on. 2810 if (mSurface.isValid()) { 2811 // XXX .copyFrom() doesn't work! 2812 //mSurfaceHolder.mSurface.copyFrom(mSurface); 2813 mSurfaceHolder.mSurface = mSurface; 2814 } 2815 mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight); 2816 mSurfaceHolder.mSurfaceLock.unlock(); 2817 if (surfaceCreated) { 2818 mSurfaceHolder.ungetCallbacks(); 2819 2820 mIsCreating = true; 2821 SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); 2822 if (callbacks != null) { 2823 for (SurfaceHolder.Callback c : callbacks) { 2824 c.surfaceCreated(mSurfaceHolder); 2825 } 2826 } 2827 } 2828 2829 if ((surfaceCreated || surfaceReplaced || surfaceSizeChanged 2830 || windowAttributesChanged) && mSurface.isValid()) { 2831 SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); 2832 if (callbacks != null) { 2833 for (SurfaceHolder.Callback c : callbacks) { 2834 c.surfaceChanged(mSurfaceHolder, lp.format, 2835 mWidth, mHeight); 2836 } 2837 } 2838 mIsCreating = false; 2839 } 2840 2841 if (surfaceDestroyed) { 2842 notifyHolderSurfaceDestroyed(); 2843 mSurfaceHolder.mSurfaceLock.lock(); 2844 try { 2845 mSurfaceHolder.mSurface = new Surface(); 2846 } finally { 2847 mSurfaceHolder.mSurfaceLock.unlock(); 2848 } 2849 } 2850 } 2851 2852 final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer; 2853 if (threadedRenderer != null && threadedRenderer.isEnabled()) { 2854 if (hwInitialized 2855 || mWidth != threadedRenderer.getWidth() 2856 || mHeight != threadedRenderer.getHeight() 2857 || mNeedsRendererSetup) { 2858 threadedRenderer.setup(mWidth, mHeight, mAttachInfo, 2859 mWindowAttributes.surfaceInsets); 2860 mNeedsRendererSetup = false; 2861 } 2862 } 2863 2864 if (!mStopped || mReportNextDraw) { 2865 boolean focusChangedDueToTouchMode = ensureTouchModeLocally( 2866 (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); 2867 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() 2868 || mHeight != host.getMeasuredHeight() || dispatchApplyInsets || 2869 updatedConfiguration) { 2870 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); 2871 int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); 2872 2873 if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth=" 2874 + mWidth + " measuredWidth=" + host.getMeasuredWidth() 2875 + " mHeight=" + mHeight 2876 + " measuredHeight=" + host.getMeasuredHeight() 2877 + " dispatchApplyInsets=" + dispatchApplyInsets); 2878 2879 // Ask host how big it wants to be 2880 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 2881 2882 // Implementation of weights from WindowManager.LayoutParams 2883 // We just grow the dimensions as needed and re-measure if 2884 // needs be 2885 int width = host.getMeasuredWidth(); 2886 int height = host.getMeasuredHeight(); 2887 boolean measureAgain = false; 2888 2889 if (lp.horizontalWeight > 0.0f) { 2890 width += (int) ((mWidth - width) * lp.horizontalWeight); 2891 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, 2892 MeasureSpec.EXACTLY); 2893 measureAgain = true; 2894 } 2895 if (lp.verticalWeight > 0.0f) { 2896 height += (int) ((mHeight - height) * lp.verticalWeight); 2897 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 2898 MeasureSpec.EXACTLY); 2899 measureAgain = true; 2900 } 2901 2902 if (measureAgain) { 2903 if (DEBUG_LAYOUT) Log.v(mTag, 2904 "And hey let's measure once more: width=" + width 2905 + " height=" + height); 2906 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 2907 } 2908 2909 layoutRequested = true; 2910 } 2911 } 2912 } else { 2913 // Not the first pass and no window/insets/visibility change but the window 2914 // may have moved and we need check that and if so to update the left and right 2915 // in the attach info. We translate only the window frame since on window move 2916 // the window manager tells us only for the new frame but the insets are the 2917 // same and we do not want to translate them more than once. 2918 maybeHandleWindowMove(frame); 2919 } 2920 2921 if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) { 2922 // If the surface has been replaced, there's a chance the bounds layer is not parented 2923 // to the new layer. When updating bounds layer, also reparent to the main VRI 2924 // SurfaceControl to ensure it's correctly placed in the hierarchy. 2925 // 2926 // This needs to be done on the client side since WMS won't reparent the children to the 2927 // new surface if it thinks the app is closing. WMS gets the signal that the app is 2928 // stopping, but on the client side it doesn't get stopped since it's restarted quick 2929 // enough. WMS doesn't want to keep around old children since they will leak when the 2930 // client creates new children. 2931 updateBoundsLayer(surfaceReplaced); 2932 } 2933 2934 final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); 2935 boolean triggerGlobalLayoutListener = didLayout 2936 || mAttachInfo.mRecomputeGlobalAttributes; 2937 if (didLayout) { 2938 performLayout(lp, mWidth, mHeight); 2939 2940 // By this point all views have been sized and positioned 2941 // We can compute the transparent area 2942 2943 if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) { 2944 // start out transparent 2945 // TODO: AVOID THAT CALL BY CACHING THE RESULT? 2946 host.getLocationInWindow(mTmpLocation); 2947 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1], 2948 mTmpLocation[0] + host.mRight - host.mLeft, 2949 mTmpLocation[1] + host.mBottom - host.mTop); 2950 2951 host.gatherTransparentRegion(mTransparentRegion); 2952 if (mTranslator != null) { 2953 mTranslator.translateRegionInWindowToScreen(mTransparentRegion); 2954 } 2955 2956 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { 2957 mPreviousTransparentRegion.set(mTransparentRegion); 2958 mFullRedrawNeeded = true; 2959 // reconfigure window manager 2960 try { 2961 mWindowSession.setTransparentRegion(mWindow, mTransparentRegion); 2962 } catch (RemoteException e) { 2963 } 2964 } 2965 } 2966 2967 if (DBG) { 2968 System.out.println("======================================"); 2969 System.out.println("performTraversals -- after setFrame"); 2970 host.debug(); 2971 } 2972 } 2973 2974 if (surfaceDestroyed) { 2975 notifySurfaceDestroyed(); 2976 } 2977 2978 if (triggerGlobalLayoutListener) { 2979 mAttachInfo.mRecomputeGlobalAttributes = false; 2980 mAttachInfo.mTreeObserver.dispatchOnGlobalLayout(); 2981 } 2982 2983 if (computesInternalInsets) { 2984 // Clear the original insets. 2985 final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets; 2986 insets.reset(); 2987 2988 // Compute new insets in place. 2989 mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); 2990 mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty(); 2991 2992 // Tell the window manager. 2993 if (insetsPending || !mLastGivenInsets.equals(insets)) { 2994 mLastGivenInsets.set(insets); 2995 2996 // Translate insets to screen coordinates if needed. 2997 final Rect contentInsets; 2998 final Rect visibleInsets; 2999 final Region touchableRegion; 3000 if (mTranslator != null) { 3001 contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets); 3002 visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets); 3003 touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion); 3004 } else { 3005 contentInsets = insets.contentInsets; 3006 visibleInsets = insets.visibleInsets; 3007 touchableRegion = insets.touchableRegion; 3008 } 3009 3010 try { 3011 mWindowSession.setInsets(mWindow, insets.mTouchableInsets, 3012 contentInsets, visibleInsets, touchableRegion); 3013 } catch (RemoteException e) { 3014 } 3015 } 3016 } 3017 3018 if (mFirst) { 3019 if (sAlwaysAssignFocus || !isInTouchMode()) { 3020 // handle first focus request 3021 if (DEBUG_INPUT_RESIZE) { 3022 Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus()); 3023 } 3024 if (mView != null) { 3025 if (!mView.hasFocus()) { 3026 mView.restoreDefaultFocus(); 3027 if (DEBUG_INPUT_RESIZE) { 3028 Log.v(mTag, "First: requested focused view=" + mView.findFocus()); 3029 } 3030 } else { 3031 if (DEBUG_INPUT_RESIZE) { 3032 Log.v(mTag, "First: existing focused view=" + mView.findFocus()); 3033 } 3034 } 3035 } 3036 } else { 3037 // Some views (like ScrollView) won't hand focus to descendants that aren't within 3038 // their viewport. Before layout, there's a good change these views are size 0 3039 // which means no children can get focus. After layout, this view now has size, but 3040 // is not guaranteed to hand-off focus to a focusable child (specifically, the edge- 3041 // case where the child has a size prior to layout and thus won't trigger 3042 // focusableViewAvailable). 3043 View focused = mView.findFocus(); 3044 if (focused instanceof ViewGroup 3045 && ((ViewGroup) focused).getDescendantFocusability() 3046 == ViewGroup.FOCUS_AFTER_DESCENDANTS) { 3047 focused.restoreDefaultFocus(); 3048 } 3049 } 3050 } 3051 3052 final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible; 3053 final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible; 3054 final boolean regainedFocus = hasWindowFocus && mLostWindowFocus; 3055 if (regainedFocus) { 3056 mLostWindowFocus = false; 3057 } else if (!hasWindowFocus && mHadWindowFocus) { 3058 mLostWindowFocus = true; 3059 } 3060 3061 if (changedVisibility || regainedFocus) { 3062 // Toasts are presented as notifications - don't present them as windows as well 3063 boolean isToast = (mWindowAttributes == null) ? false 3064 : (mWindowAttributes.type == TYPE_TOAST); 3065 if (!isToast) { 3066 host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 3067 } 3068 } 3069 3070 mFirst = false; 3071 mWillDrawSoon = false; 3072 mNewSurfaceNeeded = false; 3073 mActivityRelaunched = false; 3074 mViewVisibility = viewVisibility; 3075 mHadWindowFocus = hasWindowFocus; 3076 3077 mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); 3078 3079 // Remember if we must report the next draw. 3080 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 3081 reportNextDraw(); 3082 } 3083 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC) != 0) { 3084 reportNextDraw(); 3085 setUseBLASTSyncTransaction(); 3086 mSendNextFrameToWm = true; 3087 } 3088 3089 boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; 3090 3091 if (!cancelDraw) { 3092 if (mPendingTransitions != null && mPendingTransitions.size() > 0) { 3093 for (int i = 0; i < mPendingTransitions.size(); ++i) { 3094 mPendingTransitions.get(i).startChangingAnimations(); 3095 } 3096 mPendingTransitions.clear(); 3097 } 3098 3099 performDraw(); 3100 } else { 3101 if (isViewVisible) { 3102 // Try again 3103 scheduleTraversals(); 3104 } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) { 3105 for (int i = 0; i < mPendingTransitions.size(); ++i) { 3106 mPendingTransitions.get(i).endChangingAnimations(); 3107 } 3108 mPendingTransitions.clear(); 3109 } 3110 } 3111 3112 if (mAttachInfo.mContentCaptureEvents != null) { 3113 notifyContentCatpureEvents(); 3114 } 3115 3116 mIsInTraversal = false; 3117 } 3118 notifyContentCatpureEvents()3119 private void notifyContentCatpureEvents() { 3120 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); 3121 try { 3122 MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager 3123 .getMainContentCaptureSession(); 3124 for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) { 3125 int sessionId = mAttachInfo.mContentCaptureEvents.keyAt(i); 3126 mainSession.notifyViewTreeEvent(sessionId, /* started= */ true); 3127 ArrayList<Object> events = mAttachInfo.mContentCaptureEvents 3128 .valueAt(i); 3129 for_each_event: for (int j = 0; j < events.size(); j++) { 3130 Object event = events.get(j); 3131 if (event instanceof AutofillId) { 3132 mainSession.notifyViewDisappeared(sessionId, (AutofillId) event); 3133 } else if (event instanceof View) { 3134 View view = (View) event; 3135 ContentCaptureSession session = view.getContentCaptureSession(); 3136 if (session == null) { 3137 Log.w(mTag, "no content capture session on view: " + view); 3138 continue for_each_event; 3139 } 3140 int actualId = session.getId(); 3141 if (actualId != sessionId) { 3142 Log.w(mTag, "content capture session mismatch for view (" + view 3143 + "): was " + sessionId + " before, it's " + actualId + " now"); 3144 continue for_each_event; 3145 } 3146 ViewStructure structure = session.newViewStructure(view); 3147 view.onProvideContentCaptureStructure(structure, /* flags= */ 0); 3148 session.notifyViewAppeared(structure); 3149 } else if (event instanceof Insets) { 3150 mainSession.notifyViewInsetsChanged(sessionId, (Insets) event); 3151 } else { 3152 Log.w(mTag, "invalid content capture event: " + event); 3153 } 3154 } 3155 mainSession.notifyViewTreeEvent(sessionId, /* started= */ false); 3156 } 3157 mAttachInfo.mContentCaptureEvents = null; 3158 } finally { 3159 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 3160 } 3161 } 3162 notifyHolderSurfaceDestroyed()3163 private void notifyHolderSurfaceDestroyed() { 3164 mSurfaceHolder.ungetCallbacks(); 3165 SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); 3166 if (callbacks != null) { 3167 for (SurfaceHolder.Callback c : callbacks) { 3168 c.surfaceDestroyed(mSurfaceHolder); 3169 } 3170 } 3171 } 3172 maybeHandleWindowMove(Rect frame)3173 private void maybeHandleWindowMove(Rect frame) { 3174 // TODO: Well, we are checking whether the frame has changed similarly 3175 // to how this is done for the insets. This is however incorrect since 3176 // the insets and the frame are translated. For example, the old frame 3177 // was (1, 1 - 1, 1) and was translated to say (2, 2 - 2, 2), now the new 3178 // reported frame is (2, 2 - 2, 2) which implies no change but this is not 3179 // true since we are comparing a not translated value to a translated one. 3180 // This scenario is rare but we may want to fix that. 3181 3182 final boolean windowMoved = mAttachInfo.mWindowLeft != frame.left 3183 || mAttachInfo.mWindowTop != frame.top; 3184 if (windowMoved) { 3185 if (mTranslator != null) { 3186 mTranslator.translateRectInScreenToAppWinFrame(frame); 3187 } 3188 mAttachInfo.mWindowLeft = frame.left; 3189 mAttachInfo.mWindowTop = frame.top; 3190 } 3191 if (windowMoved || mAttachInfo.mNeedsUpdateLightCenter) { 3192 // Update the light position for the new offsets. 3193 if (mAttachInfo.mThreadedRenderer != null) { 3194 mAttachInfo.mThreadedRenderer.setLightCenter(mAttachInfo); 3195 } 3196 mAttachInfo.mNeedsUpdateLightCenter = false; 3197 } 3198 } 3199 handleWindowFocusChanged()3200 private void handleWindowFocusChanged() { 3201 final boolean hasWindowFocus; 3202 final boolean inTouchMode; 3203 synchronized (this) { 3204 if (!mWindowFocusChanged) { 3205 return; 3206 } 3207 mWindowFocusChanged = false; 3208 hasWindowFocus = mUpcomingWindowFocus; 3209 inTouchMode = mUpcomingInTouchMode; 3210 } 3211 if (sNewInsetsMode != NEW_INSETS_MODE_NONE) { 3212 // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback 3213 // config changes. 3214 if (hasWindowFocus) { 3215 mInsetsController.onWindowFocusGained(); 3216 } else { 3217 mInsetsController.onWindowFocusLost(); 3218 } 3219 } 3220 3221 if (mAdded) { 3222 profileRendering(hasWindowFocus); 3223 3224 if (hasWindowFocus) { 3225 ensureTouchModeLocally(inTouchMode); 3226 if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) { 3227 mFullRedrawNeeded = true; 3228 try { 3229 final WindowManager.LayoutParams lp = mWindowAttributes; 3230 final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null; 3231 mAttachInfo.mThreadedRenderer.initializeIfNeeded( 3232 mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); 3233 } catch (OutOfResourcesException e) { 3234 Log.e(mTag, "OutOfResourcesException locking surface", e); 3235 try { 3236 if (!mWindowSession.outOfMemory(mWindow)) { 3237 Slog.w(mTag, "No processes killed for memory;" 3238 + " killing self"); 3239 Process.killProcess(Process.myPid()); 3240 } 3241 } catch (RemoteException ex) { 3242 } 3243 // Retry in a bit. 3244 mHandler.sendMessageDelayed(mHandler.obtainMessage( 3245 MSG_WINDOW_FOCUS_CHANGED), 500); 3246 return; 3247 } 3248 } 3249 } 3250 3251 mAttachInfo.mHasWindowFocus = hasWindowFocus; 3252 mImeFocusController.updateImeFocusable(mWindowAttributes, true /* force */); 3253 mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes); 3254 3255 if (mView != null) { 3256 mAttachInfo.mKeyDispatchState.reset(); 3257 mView.dispatchWindowFocusChanged(hasWindowFocus); 3258 mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); 3259 if (mAttachInfo.mTooltipHost != null) { 3260 mAttachInfo.mTooltipHost.hideTooltip(); 3261 } 3262 } 3263 3264 // Note: must be done after the focus change callbacks, 3265 // so all of the view state is set up correctly. 3266 mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus, 3267 mWindowAttributes); 3268 3269 if (hasWindowFocus) { 3270 // Clear the forward bit. We can just do this directly, since 3271 // the window manager doesn't care about it. 3272 mWindowAttributes.softInputMode &= 3273 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 3274 ((WindowManager.LayoutParams) mView.getLayoutParams()) 3275 .softInputMode &= 3276 ~WindowManager.LayoutParams 3277 .SOFT_INPUT_IS_FORWARD_NAVIGATION; 3278 3279 // Refocusing a window that has a focused view should fire a 3280 // focus event for the view since the global focused view changed. 3281 fireAccessibilityFocusEventIfHasFocusedNode(); 3282 } else { 3283 if (mPointerCapture) { 3284 handlePointerCaptureChanged(false); 3285 } 3286 } 3287 } 3288 mFirstInputStage.onWindowFocusChanged(hasWindowFocus); 3289 3290 // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus 3291 // is lost, so we don't need to to force a flush - there might be other events such as 3292 // text changes, but these should be flushed independently. 3293 if (hasWindowFocus) { 3294 handleContentCaptureFlush(); 3295 } 3296 } 3297 fireAccessibilityFocusEventIfHasFocusedNode()3298 private void fireAccessibilityFocusEventIfHasFocusedNode() { 3299 if (!AccessibilityManager.getInstance(mContext).isEnabled()) { 3300 return; 3301 } 3302 final View focusedView = mView.findFocus(); 3303 if (focusedView == null) { 3304 return; 3305 } 3306 final AccessibilityNodeProvider provider = focusedView.getAccessibilityNodeProvider(); 3307 if (provider == null) { 3308 focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 3309 } else { 3310 final AccessibilityNodeInfo focusedNode = findFocusedVirtualNode(provider); 3311 if (focusedNode != null) { 3312 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId( 3313 focusedNode.getSourceNodeId()); 3314 // This is a best effort since clearing and setting the focus via the 3315 // provider APIs could have side effects. We don't have a provider API 3316 // similar to that on View to ask a given event to be fired. 3317 final AccessibilityEvent event = AccessibilityEvent.obtain( 3318 AccessibilityEvent.TYPE_VIEW_FOCUSED); 3319 event.setSource(focusedView, virtualId); 3320 event.setPackageName(focusedNode.getPackageName()); 3321 event.setChecked(focusedNode.isChecked()); 3322 event.setContentDescription(focusedNode.getContentDescription()); 3323 event.setPassword(focusedNode.isPassword()); 3324 event.getText().add(focusedNode.getText()); 3325 event.setEnabled(focusedNode.isEnabled()); 3326 focusedView.getParent().requestSendAccessibilityEvent(focusedView, event); 3327 focusedNode.recycle(); 3328 } 3329 } 3330 } 3331 findFocusedVirtualNode(AccessibilityNodeProvider provider)3332 private AccessibilityNodeInfo findFocusedVirtualNode(AccessibilityNodeProvider provider) { 3333 AccessibilityNodeInfo focusedNode = provider.findFocus( 3334 AccessibilityNodeInfo.FOCUS_INPUT); 3335 if (focusedNode != null) { 3336 return focusedNode; 3337 } 3338 3339 if (!mContext.isAutofillCompatibilityEnabled()) { 3340 return null; 3341 } 3342 3343 // Unfortunately some provider implementations don't properly 3344 // implement AccessibilityNodeProvider#findFocus 3345 AccessibilityNodeInfo current = provider.createAccessibilityNodeInfo( 3346 AccessibilityNodeProvider.HOST_VIEW_ID); 3347 if (current.isFocused()) { 3348 return current; 3349 } 3350 3351 final Queue<AccessibilityNodeInfo> fringe = new LinkedList<>(); 3352 fringe.offer(current); 3353 3354 while (!fringe.isEmpty()) { 3355 current = fringe.poll(); 3356 final LongArray childNodeIds = current.getChildNodeIds(); 3357 if (childNodeIds== null || childNodeIds.size() <= 0) { 3358 continue; 3359 } 3360 final int childCount = childNodeIds.size(); 3361 for (int i = 0; i < childCount; i++) { 3362 final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId( 3363 childNodeIds.get(i)); 3364 final AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(virtualId); 3365 if (child != null) { 3366 if (child.isFocused()) { 3367 return child; 3368 } 3369 fringe.offer(child); 3370 } 3371 } 3372 current.recycle(); 3373 } 3374 3375 return null; 3376 } 3377 handleOutOfResourcesException(Surface.OutOfResourcesException e)3378 private void handleOutOfResourcesException(Surface.OutOfResourcesException e) { 3379 Log.e(mTag, "OutOfResourcesException initializing HW surface", e); 3380 try { 3381 if (!mWindowSession.outOfMemory(mWindow) && 3382 Process.myUid() != Process.SYSTEM_UID) { 3383 Slog.w(mTag, "No processes killed for memory; killing self"); 3384 Process.killProcess(Process.myPid()); 3385 } 3386 } catch (RemoteException ex) { 3387 } 3388 mLayoutRequested = true; // ask wm for a new surface next time. 3389 } 3390 performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)3391 private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { 3392 if (mView == null) { 3393 return; 3394 } 3395 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); 3396 try { 3397 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 3398 } finally { 3399 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 3400 } 3401 } 3402 3403 /** 3404 * Called by {@link android.view.View#isInLayout()} to determine whether the view hierarchy 3405 * is currently undergoing a layout pass. 3406 * 3407 * @return whether the view hierarchy is currently undergoing a layout pass 3408 */ isInLayout()3409 boolean isInLayout() { 3410 return mInLayout; 3411 } 3412 3413 /** 3414 * Called by {@link android.view.View#requestLayout()} if the view hierarchy is currently 3415 * undergoing a layout pass. requestLayout() should not generally be called during layout, 3416 * unless the container hierarchy knows what it is doing (i.e., it is fine as long as 3417 * all children in that container hierarchy are measured and laid out at the end of the layout 3418 * pass for that container). If requestLayout() is called anyway, we handle it correctly 3419 * by registering all requesters during a frame as it proceeds. At the end of the frame, 3420 * we check all of those views to see if any still have pending layout requests, which 3421 * indicates that they were not correctly handled by their container hierarchy. If that is 3422 * the case, we clear all such flags in the tree, to remove the buggy flag state that leads 3423 * to blank containers, and force a second request/measure/layout pass in this frame. If 3424 * more requestLayout() calls are received during that second layout pass, we post those 3425 * requests to the next frame to avoid possible infinite loops. 3426 * 3427 * <p>The return value from this method indicates whether the request should proceed 3428 * (if it is a request during the first layout pass) or should be skipped and posted to the 3429 * next frame (if it is a request during the second layout pass).</p> 3430 * 3431 * @param view the view that requested the layout. 3432 * 3433 * @return true if request should proceed, false otherwise. 3434 */ requestLayoutDuringLayout(final View view)3435 boolean requestLayoutDuringLayout(final View view) { 3436 if (view.mParent == null || view.mAttachInfo == null) { 3437 // Would not normally trigger another layout, so just let it pass through as usual 3438 return true; 3439 } 3440 if (!mLayoutRequesters.contains(view)) { 3441 mLayoutRequesters.add(view); 3442 } 3443 if (!mHandlingLayoutInLayoutRequest) { 3444 // Let the request proceed normally; it will be processed in a second layout pass 3445 // if necessary 3446 return true; 3447 } else { 3448 // Don't let the request proceed during the second layout pass. 3449 // It will post to the next frame instead. 3450 return false; 3451 } 3452 } 3453 performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)3454 private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, 3455 int desiredWindowHeight) { 3456 mScrollMayChange = true; 3457 mInLayout = true; 3458 3459 final View host = mView; 3460 if (host == null) { 3461 return; 3462 } 3463 if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { 3464 Log.v(mTag, "Laying out " + host + " to (" + 3465 host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")"); 3466 } 3467 3468 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); 3469 try { 3470 host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); 3471 3472 mInLayout = false; 3473 int numViewsRequestingLayout = mLayoutRequesters.size(); 3474 if (numViewsRequestingLayout > 0) { 3475 // requestLayout() was called during layout. 3476 // If no layout-request flags are set on the requesting views, there is no problem. 3477 // If some requests are still pending, then we need to clear those flags and do 3478 // a full request/measure/layout pass to handle this situation. 3479 ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, 3480 false); 3481 if (validLayoutRequesters != null) { 3482 // Set this flag to indicate that any further requests are happening during 3483 // the second pass, which may result in posting those requests to the next 3484 // frame instead 3485 mHandlingLayoutInLayoutRequest = true; 3486 3487 // Process fresh layout requests, then measure and layout 3488 int numValidRequests = validLayoutRequesters.size(); 3489 for (int i = 0; i < numValidRequests; ++i) { 3490 final View view = validLayoutRequesters.get(i); 3491 Log.w("View", "requestLayout() improperly called by " + view + 3492 " during layout: running second layout pass"); 3493 view.requestLayout(); 3494 } 3495 measureHierarchy(host, lp, mView.getContext().getResources(), 3496 desiredWindowWidth, desiredWindowHeight); 3497 mInLayout = true; 3498 host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); 3499 3500 mHandlingLayoutInLayoutRequest = false; 3501 3502 // Check the valid requests again, this time without checking/clearing the 3503 // layout flags, since requests happening during the second pass get noop'd 3504 validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true); 3505 if (validLayoutRequesters != null) { 3506 final ArrayList<View> finalRequesters = validLayoutRequesters; 3507 // Post second-pass requests to the next frame 3508 getRunQueue().post(new Runnable() { 3509 @Override 3510 public void run() { 3511 int numValidRequests = finalRequesters.size(); 3512 for (int i = 0; i < numValidRequests; ++i) { 3513 final View view = finalRequesters.get(i); 3514 Log.w("View", "requestLayout() improperly called by " + view + 3515 " during second layout pass: posting in next frame"); 3516 view.requestLayout(); 3517 } 3518 } 3519 }); 3520 } 3521 } 3522 3523 } 3524 } finally { 3525 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 3526 } 3527 mInLayout = false; 3528 } 3529 3530 /** 3531 * This method is called during layout when there have been calls to requestLayout() during 3532 * layout. It walks through the list of views that requested layout to determine which ones 3533 * still need it, based on visibility in the hierarchy and whether they have already been 3534 * handled (as is usually the case with ListView children). 3535 * 3536 * @param layoutRequesters The list of views that requested layout during layout 3537 * @param secondLayoutRequests Whether the requests were issued during the second layout pass. 3538 * If so, the FORCE_LAYOUT flag was not set on requesters. 3539 * @return A list of the actual views that still need to be laid out. 3540 */ getValidLayoutRequesters(ArrayList<View> layoutRequesters, boolean secondLayoutRequests)3541 private ArrayList<View> getValidLayoutRequesters(ArrayList<View> layoutRequesters, 3542 boolean secondLayoutRequests) { 3543 3544 int numViewsRequestingLayout = layoutRequesters.size(); 3545 ArrayList<View> validLayoutRequesters = null; 3546 for (int i = 0; i < numViewsRequestingLayout; ++i) { 3547 View view = layoutRequesters.get(i); 3548 if (view != null && view.mAttachInfo != null && view.mParent != null && 3549 (secondLayoutRequests || (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) == 3550 View.PFLAG_FORCE_LAYOUT)) { 3551 boolean gone = false; 3552 View parent = view; 3553 // Only trigger new requests for views in a non-GONE hierarchy 3554 while (parent != null) { 3555 if ((parent.mViewFlags & View.VISIBILITY_MASK) == View.GONE) { 3556 gone = true; 3557 break; 3558 } 3559 if (parent.mParent instanceof View) { 3560 parent = (View) parent.mParent; 3561 } else { 3562 parent = null; 3563 } 3564 } 3565 if (!gone) { 3566 if (validLayoutRequesters == null) { 3567 validLayoutRequesters = new ArrayList<View>(); 3568 } 3569 validLayoutRequesters.add(view); 3570 } 3571 } 3572 } 3573 if (!secondLayoutRequests) { 3574 // If we're checking the layout flags, then we need to clean them up also 3575 for (int i = 0; i < numViewsRequestingLayout; ++i) { 3576 View view = layoutRequesters.get(i); 3577 while (view != null && 3578 (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) { 3579 view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT; 3580 if (view.mParent instanceof View) { 3581 view = (View) view.mParent; 3582 } else { 3583 view = null; 3584 } 3585 } 3586 } 3587 } 3588 layoutRequesters.clear(); 3589 return validLayoutRequesters; 3590 } 3591 3592 @Override requestTransparentRegion(View child)3593 public void requestTransparentRegion(View child) { 3594 // the test below should not fail unless someone is messing with us 3595 checkThread(); 3596 if (mView != child) { 3597 return; 3598 } 3599 3600 if ((mView.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) { 3601 mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS; 3602 // Need to make sure we re-evaluate the window attributes next 3603 // time around, to ensure the window has the correct format. 3604 mWindowAttributesChanged = true; 3605 } 3606 3607 // Always request layout to apply the latest transparent region. 3608 requestLayout(); 3609 } 3610 3611 /** 3612 * Figures out the measure spec for the root view in a window based on it's 3613 * layout params. 3614 * 3615 * @param windowSize 3616 * The available width or height of the window 3617 * 3618 * @param rootDimension 3619 * The layout params for one dimension (width or height) of the 3620 * window. 3621 * 3622 * @return The measure spec to use to measure the root view. 3623 */ getRootMeasureSpec(int windowSize, int rootDimension)3624 private static int getRootMeasureSpec(int windowSize, int rootDimension) { 3625 int measureSpec; 3626 switch (rootDimension) { 3627 3628 case ViewGroup.LayoutParams.MATCH_PARENT: 3629 // Window can't resize. Force root view to be windowSize. 3630 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); 3631 break; 3632 case ViewGroup.LayoutParams.WRAP_CONTENT: 3633 // Window can resize. Set max size for root view. 3634 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); 3635 break; 3636 default: 3637 // Window wants to be an exact size. Force root view to be that size. 3638 measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); 3639 break; 3640 } 3641 return measureSpec; 3642 } 3643 3644 int mHardwareXOffset; 3645 int mHardwareYOffset; 3646 3647 @Override onPreDraw(RecordingCanvas canvas)3648 public void onPreDraw(RecordingCanvas canvas) { 3649 // If mCurScrollY is not 0 then this influences the hardwareYOffset. The end result is we 3650 // can apply offsets that are not handled by anything else, resulting in underdraw as 3651 // the View is shifted (thus shifting the window background) exposing unpainted 3652 // content. To handle this with minimal glitches we just clear to BLACK if the window 3653 // is opaque. If it's not opaque then HWUI already internally does a glClear to 3654 // transparent, so there's no risk of underdraw on non-opaque surfaces. 3655 if (mCurScrollY != 0 && mHardwareYOffset != 0 && mAttachInfo.mThreadedRenderer.isOpaque()) { 3656 canvas.drawColor(Color.BLACK); 3657 } 3658 canvas.translate(-mHardwareXOffset, -mHardwareYOffset); 3659 } 3660 3661 @Override onPostDraw(RecordingCanvas canvas)3662 public void onPostDraw(RecordingCanvas canvas) { 3663 drawAccessibilityFocusedDrawableIfNeeded(canvas); 3664 if (mUseMTRenderer) { 3665 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 3666 mWindowCallbacks.get(i).onPostDraw(canvas); 3667 } 3668 } 3669 } 3670 3671 /** 3672 * @hide 3673 */ outputDisplayList(View view)3674 void outputDisplayList(View view) { 3675 view.mRenderNode.output(); 3676 } 3677 3678 /** 3679 * @see #PROPERTY_PROFILE_RENDERING 3680 */ profileRendering(boolean enabled)3681 private void profileRendering(boolean enabled) { 3682 if (mProfileRendering) { 3683 mRenderProfilingEnabled = enabled; 3684 3685 if (mRenderProfiler != null) { 3686 mChoreographer.removeFrameCallback(mRenderProfiler); 3687 } 3688 if (mRenderProfilingEnabled) { 3689 if (mRenderProfiler == null) { 3690 mRenderProfiler = new Choreographer.FrameCallback() { 3691 @Override 3692 public void doFrame(long frameTimeNanos) { 3693 mDirty.set(0, 0, mWidth, mHeight); 3694 scheduleTraversals(); 3695 if (mRenderProfilingEnabled) { 3696 mChoreographer.postFrameCallback(mRenderProfiler); 3697 } 3698 } 3699 }; 3700 } 3701 mChoreographer.postFrameCallback(mRenderProfiler); 3702 } else { 3703 mRenderProfiler = null; 3704 } 3705 } 3706 } 3707 3708 /** 3709 * Called from draw() when DEBUG_FPS is enabled 3710 */ trackFPS()3711 private void trackFPS() { 3712 // Tracks frames per second drawn. First value in a series of draws may be bogus 3713 // because it down not account for the intervening idle time 3714 long nowTime = System.currentTimeMillis(); 3715 if (mFpsStartTime < 0) { 3716 mFpsStartTime = mFpsPrevTime = nowTime; 3717 mFpsNumFrames = 0; 3718 } else { 3719 ++mFpsNumFrames; 3720 String thisHash = Integer.toHexString(System.identityHashCode(this)); 3721 long frameTime = nowTime - mFpsPrevTime; 3722 long totalTime = nowTime - mFpsStartTime; 3723 Log.v(mTag, "0x" + thisHash + "\tFrame time:\t" + frameTime); 3724 mFpsPrevTime = nowTime; 3725 if (totalTime > 1000) { 3726 float fps = (float) mFpsNumFrames * 1000 / totalTime; 3727 Log.v(mTag, "0x" + thisHash + "\tFPS:\t" + fps); 3728 mFpsStartTime = nowTime; 3729 mFpsNumFrames = 0; 3730 } 3731 } 3732 } 3733 3734 /** 3735 * A count of the number of calls to pendingDrawFinished we 3736 * require to notify the WM drawing is complete. 3737 */ 3738 int mDrawsNeededToReport = 0; 3739 3740 /** 3741 * Delay notifying WM of draw finished until 3742 * a balanced call to pendingDrawFinished. 3743 */ drawPending()3744 void drawPending() { 3745 mDrawsNeededToReport++; 3746 } 3747 pendingDrawFinished()3748 void pendingDrawFinished() { 3749 if (mDrawsNeededToReport == 0) { 3750 throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls"); 3751 } 3752 mDrawsNeededToReport--; 3753 if (mDrawsNeededToReport == 0) { 3754 reportDrawFinished(); 3755 } 3756 } 3757 postDrawFinished()3758 private void postDrawFinished() { 3759 mHandler.sendEmptyMessage(MSG_DRAW_FINISHED); 3760 } 3761 reportDrawFinished()3762 private void reportDrawFinished() { 3763 try { 3764 mDrawsNeededToReport = 0; 3765 mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction); 3766 } catch (RemoteException e) { 3767 // Have fun! 3768 } 3769 } 3770 performDraw()3771 private void performDraw() { 3772 if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { 3773 return; 3774 } else if (mView == null) { 3775 return; 3776 } 3777 3778 final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw; 3779 mFullRedrawNeeded = false; 3780 3781 mIsDrawing = true; 3782 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); 3783 3784 boolean usingAsyncReport = false; 3785 boolean reportNextDraw = mReportNextDraw; // Capture the original value 3786 if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { 3787 ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver 3788 .captureFrameCommitCallbacks(); 3789 final boolean needFrameCompleteCallback = mNextDrawUseBLASTSyncTransaction || 3790 (commitCallbacks != null && commitCallbacks.size() > 0) || 3791 mReportNextDraw; 3792 usingAsyncReport = mReportNextDraw; 3793 if (needFrameCompleteCallback) { 3794 final Handler handler = mAttachInfo.mHandler; 3795 mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { 3796 finishBLASTSync(!mSendNextFrameToWm); 3797 handler.postAtFrontOfQueue(() -> { 3798 if (reportNextDraw) { 3799 // TODO: Use the frame number 3800 pendingDrawFinished(); 3801 } 3802 if (commitCallbacks != null) { 3803 for (int i = 0; i < commitCallbacks.size(); i++) { 3804 commitCallbacks.get(i).run(); 3805 } 3806 } 3807 });}); 3808 } 3809 } 3810 3811 try { 3812 if (mNextDrawUseBLASTSyncTransaction) { 3813 // TODO(b/149747443) 3814 // We aren't prepared to handle overlapping use of mRtBLASTSyncTransaction 3815 // so if we are BLAST syncing we make sure the previous draw has 3816 // totally finished. 3817 if (mAttachInfo.mThreadedRenderer != null) { 3818 mAttachInfo.mThreadedRenderer.pause(); 3819 } 3820 3821 mNextReportConsumeBLAST = true; 3822 mNextDrawUseBLASTSyncTransaction = false; 3823 3824 if (mBlastBufferQueue != null) { 3825 mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction); 3826 } 3827 } 3828 boolean canUseAsync = draw(fullRedrawNeeded); 3829 if (usingAsyncReport && !canUseAsync) { 3830 mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null); 3831 usingAsyncReport = false; 3832 finishBLASTSync(true /* apply */); 3833 } 3834 } finally { 3835 mIsDrawing = false; 3836 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 3837 } 3838 3839 // For whatever reason we didn't create a HardwareRenderer, end any 3840 // hardware animations that are now dangling 3841 if (mAttachInfo.mPendingAnimatingRenderNodes != null) { 3842 final int count = mAttachInfo.mPendingAnimatingRenderNodes.size(); 3843 for (int i = 0; i < count; i++) { 3844 mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators(); 3845 } 3846 mAttachInfo.mPendingAnimatingRenderNodes.clear(); 3847 } 3848 3849 if (mReportNextDraw) { 3850 mReportNextDraw = false; 3851 3852 // if we're using multi-thread renderer, wait for the window frame draws 3853 if (mWindowDrawCountDown != null) { 3854 try { 3855 mWindowDrawCountDown.await(); 3856 } catch (InterruptedException e) { 3857 Log.e(mTag, "Window redraw count down interrupted!"); 3858 } 3859 mWindowDrawCountDown = null; 3860 } 3861 3862 if (mAttachInfo.mThreadedRenderer != null) { 3863 mAttachInfo.mThreadedRenderer.setStopped(mStopped); 3864 } 3865 3866 if (LOCAL_LOGV) { 3867 Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle()); 3868 } 3869 3870 if (mSurfaceHolder != null && mSurface.isValid()) { 3871 SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished); 3872 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 3873 3874 sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); 3875 } else if (!usingAsyncReport) { 3876 if (mAttachInfo.mThreadedRenderer != null) { 3877 mAttachInfo.mThreadedRenderer.fence(); 3878 } 3879 pendingDrawFinished(); 3880 } 3881 } 3882 if (mPerformContentCapture) { 3883 performContentCaptureInitialReport(); 3884 } 3885 } 3886 3887 /** 3888 * Checks (and caches) if content capture is enabled for this context. 3889 */ isContentCaptureEnabled()3890 private boolean isContentCaptureEnabled() { 3891 switch (mContentCaptureEnabled) { 3892 case CONTENT_CAPTURE_ENABLED_TRUE: 3893 return true; 3894 case CONTENT_CAPTURE_ENABLED_FALSE: 3895 return false; 3896 case CONTENT_CAPTURE_ENABLED_NOT_CHECKED: 3897 final boolean reallyEnabled = isContentCaptureReallyEnabled(); 3898 mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE 3899 : CONTENT_CAPTURE_ENABLED_FALSE; 3900 return reallyEnabled; 3901 default: 3902 Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled); 3903 return false; 3904 } 3905 3906 } 3907 3908 /** 3909 * Checks (without caching) if content capture is enabled for this context. 3910 */ isContentCaptureReallyEnabled()3911 private boolean isContentCaptureReallyEnabled() { 3912 // First check if context supports it, so it saves a service lookup when it doesn't 3913 if (mContext.getContentCaptureOptions() == null) return false; 3914 3915 final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext); 3916 // Then check if it's enabled in the contex itself. 3917 if (ccm == null || !ccm.isContentCaptureEnabled()) return false; 3918 3919 return true; 3920 } 3921 performContentCaptureInitialReport()3922 private void performContentCaptureInitialReport() { 3923 mPerformContentCapture = false; // One-time offer! 3924 final View rootView = mView; 3925 if (DEBUG_CONTENT_CAPTURE) { 3926 Log.v(mTag, "performContentCaptureInitialReport() on " + rootView); 3927 } 3928 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 3929 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " 3930 + getClass().getSimpleName()); 3931 } 3932 try { 3933 if (!isContentCaptureEnabled()) return; 3934 3935 // Content capture is a go! 3936 rootView.dispatchInitialProvideContentCaptureStructure(); 3937 } finally { 3938 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 3939 } 3940 } 3941 handleContentCaptureFlush()3942 private void handleContentCaptureFlush() { 3943 if (DEBUG_CONTENT_CAPTURE) { 3944 Log.v(mTag, "handleContentCaptureFlush()"); 3945 } 3946 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 3947 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for " 3948 + getClass().getSimpleName()); 3949 } 3950 try { 3951 if (!isContentCaptureEnabled()) return; 3952 3953 final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager; 3954 if (ccm == null) { 3955 Log.w(TAG, "No ContentCapture on AttachInfo"); 3956 return; 3957 } 3958 ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED); 3959 } finally { 3960 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 3961 } 3962 } 3963 draw(boolean fullRedrawNeeded)3964 private boolean draw(boolean fullRedrawNeeded) { 3965 Surface surface = mSurface; 3966 if (!surface.isValid()) { 3967 return false; 3968 } 3969 3970 if (DEBUG_FPS) { 3971 trackFPS(); 3972 } 3973 3974 if (!sFirstDrawComplete) { 3975 synchronized (sFirstDrawHandlers) { 3976 sFirstDrawComplete = true; 3977 final int count = sFirstDrawHandlers.size(); 3978 for (int i = 0; i< count; i++) { 3979 mHandler.post(sFirstDrawHandlers.get(i)); 3980 } 3981 } 3982 } 3983 3984 scrollToRectOrFocus(null, false); 3985 3986 if (mAttachInfo.mViewScrollChanged) { 3987 mAttachInfo.mViewScrollChanged = false; 3988 mAttachInfo.mTreeObserver.dispatchOnScrollChanged(); 3989 } 3990 3991 boolean animating = mScroller != null && mScroller.computeScrollOffset(); 3992 final int curScrollY; 3993 if (animating) { 3994 curScrollY = mScroller.getCurrY(); 3995 } else { 3996 curScrollY = mScrollY; 3997 } 3998 if (mCurScrollY != curScrollY) { 3999 mCurScrollY = curScrollY; 4000 fullRedrawNeeded = true; 4001 if (mView instanceof RootViewSurfaceTaker) { 4002 ((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY); 4003 } 4004 } 4005 4006 final float appScale = mAttachInfo.mApplicationScale; 4007 final boolean scalingRequired = mAttachInfo.mScalingRequired; 4008 4009 final Rect dirty = mDirty; 4010 if (mSurfaceHolder != null) { 4011 // The app owns the surface, we won't draw. 4012 dirty.setEmpty(); 4013 if (animating && mScroller != null) { 4014 mScroller.abortAnimation(); 4015 } 4016 return false; 4017 } 4018 4019 if (fullRedrawNeeded) { 4020 dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); 4021 } 4022 4023 if (DEBUG_ORIENTATION || DEBUG_DRAW) { 4024 Log.v(mTag, "Draw " + mView + "/" 4025 + mWindowAttributes.getTitle() 4026 + ": dirty={" + dirty.left + "," + dirty.top 4027 + "," + dirty.right + "," + dirty.bottom + "} surface=" 4028 + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" + 4029 appScale + ", width=" + mWidth + ", height=" + mHeight); 4030 } 4031 4032 mAttachInfo.mTreeObserver.dispatchOnDraw(); 4033 4034 int xOffset = -mCanvasOffsetX; 4035 int yOffset = -mCanvasOffsetY + curScrollY; 4036 final WindowManager.LayoutParams params = mWindowAttributes; 4037 final Rect surfaceInsets = params != null ? params.surfaceInsets : null; 4038 if (surfaceInsets != null) { 4039 xOffset -= surfaceInsets.left; 4040 yOffset -= surfaceInsets.top; 4041 4042 // Offset dirty rect for surface insets. 4043 dirty.offset(surfaceInsets.left, surfaceInsets.right); 4044 } 4045 4046 boolean accessibilityFocusDirty = false; 4047 final Drawable drawable = mAttachInfo.mAccessibilityFocusDrawable; 4048 if (drawable != null) { 4049 final Rect bounds = mAttachInfo.mTmpInvalRect; 4050 final boolean hasFocus = getAccessibilityFocusedRect(bounds); 4051 if (!hasFocus) { 4052 bounds.setEmpty(); 4053 } 4054 if (!bounds.equals(drawable.getBounds())) { 4055 accessibilityFocusDirty = true; 4056 } 4057 } 4058 4059 mAttachInfo.mDrawingTime = 4060 mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS; 4061 4062 boolean useAsyncReport = false; 4063 if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { 4064 if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { 4065 // If accessibility focus moved, always invalidate the root. 4066 boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested; 4067 mInvalidateRootRequested = false; 4068 4069 // Draw with hardware renderer. 4070 mIsAnimating = false; 4071 4072 if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) { 4073 mHardwareYOffset = yOffset; 4074 mHardwareXOffset = xOffset; 4075 invalidateRoot = true; 4076 } 4077 4078 if (invalidateRoot) { 4079 mAttachInfo.mThreadedRenderer.invalidateRoot(); 4080 } 4081 4082 dirty.setEmpty(); 4083 4084 // Stage the content drawn size now. It will be transferred to the renderer 4085 // shortly before the draw commands get send to the renderer. 4086 final boolean updated = updateContentDrawBounds(); 4087 4088 if (mReportNextDraw) { 4089 // report next draw overrides setStopped() 4090 // This value is re-sync'd to the value of mStopped 4091 // in the handling of mReportNextDraw post-draw. 4092 mAttachInfo.mThreadedRenderer.setStopped(false); 4093 } 4094 4095 if (updated) { 4096 requestDrawWindow(); 4097 } 4098 4099 useAsyncReport = true; 4100 4101 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); 4102 } else { 4103 // If we get here with a disabled & requested hardware renderer, something went 4104 // wrong (an invalidate posted right before we destroyed the hardware surface 4105 // for instance) so we should just bail out. Locking the surface with software 4106 // rendering at this point would lock it forever and prevent hardware renderer 4107 // from doing its job when it comes back. 4108 // Before we request a new frame we must however attempt to reinitiliaze the 4109 // hardware renderer if it's in requested state. This would happen after an 4110 // eglTerminate() for instance. 4111 if (mAttachInfo.mThreadedRenderer != null && 4112 !mAttachInfo.mThreadedRenderer.isEnabled() && 4113 mAttachInfo.mThreadedRenderer.isRequested() && 4114 mSurface.isValid()) { 4115 4116 try { 4117 mAttachInfo.mThreadedRenderer.initializeIfNeeded( 4118 mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); 4119 } catch (OutOfResourcesException e) { 4120 handleOutOfResourcesException(e); 4121 return false; 4122 } 4123 4124 mFullRedrawNeeded = true; 4125 scheduleTraversals(); 4126 return false; 4127 } 4128 4129 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, 4130 scalingRequired, dirty, surfaceInsets)) { 4131 return false; 4132 } 4133 } 4134 } 4135 4136 if (animating) { 4137 mFullRedrawNeeded = true; 4138 scheduleTraversals(); 4139 } 4140 return useAsyncReport; 4141 } 4142 4143 /** 4144 * @return true if drawing was successful, false if an error occurred 4145 */ drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets)4146 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, 4147 boolean scalingRequired, Rect dirty, Rect surfaceInsets) { 4148 4149 // Draw with software renderer. 4150 final Canvas canvas; 4151 4152 // We already have the offset of surfaceInsets in xoff, yoff and dirty region, 4153 // therefore we need to add it back when moving the dirty region. 4154 int dirtyXOffset = xoff; 4155 int dirtyYOffset = yoff; 4156 if (surfaceInsets != null) { 4157 dirtyXOffset += surfaceInsets.left; 4158 dirtyYOffset += surfaceInsets.top; 4159 } 4160 4161 try { 4162 dirty.offset(-dirtyXOffset, -dirtyYOffset); 4163 final int left = dirty.left; 4164 final int top = dirty.top; 4165 final int right = dirty.right; 4166 final int bottom = dirty.bottom; 4167 4168 canvas = mSurface.lockCanvas(dirty); 4169 4170 // TODO: Do this in native 4171 canvas.setDensity(mDensity); 4172 } catch (Surface.OutOfResourcesException e) { 4173 handleOutOfResourcesException(e); 4174 return false; 4175 } catch (IllegalArgumentException e) { 4176 Log.e(mTag, "Could not lock surface", e); 4177 // Don't assume this is due to out of memory, it could be 4178 // something else, and if it is something else then we could 4179 // kill stuff (or ourself) for no reason. 4180 mLayoutRequested = true; // ask wm for a new surface next time. 4181 return false; 4182 } finally { 4183 dirty.offset(dirtyXOffset, dirtyYOffset); // Reset to the original value. 4184 } 4185 4186 try { 4187 if (DEBUG_ORIENTATION || DEBUG_DRAW) { 4188 Log.v(mTag, "Surface " + surface + " drawing to bitmap w=" 4189 + canvas.getWidth() + ", h=" + canvas.getHeight()); 4190 //canvas.drawARGB(255, 255, 0, 0); 4191 } 4192 4193 // If this bitmap's format includes an alpha channel, we 4194 // need to clear it before drawing so that the child will 4195 // properly re-composite its drawing on a transparent 4196 // background. This automatically respects the clip/dirty region 4197 // or 4198 // If we are applying an offset, we need to clear the area 4199 // where the offset doesn't appear to avoid having garbage 4200 // left in the blank areas. 4201 if (!canvas.isOpaque() || yoff != 0 || xoff != 0) { 4202 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 4203 } 4204 4205 dirty.setEmpty(); 4206 mIsAnimating = false; 4207 mView.mPrivateFlags |= View.PFLAG_DRAWN; 4208 4209 if (DEBUG_DRAW) { 4210 Context cxt = mView.getContext(); 4211 Log.i(mTag, "Drawing: package:" + cxt.getPackageName() + 4212 ", metrics=" + cxt.getResources().getDisplayMetrics() + 4213 ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo()); 4214 } 4215 canvas.translate(-xoff, -yoff); 4216 if (mTranslator != null) { 4217 mTranslator.translateCanvas(canvas); 4218 } 4219 canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0); 4220 4221 mView.draw(canvas); 4222 4223 drawAccessibilityFocusedDrawableIfNeeded(canvas); 4224 } finally { 4225 try { 4226 surface.unlockCanvasAndPost(canvas); 4227 } catch (IllegalArgumentException e) { 4228 Log.e(mTag, "Could not unlock surface", e); 4229 mLayoutRequested = true; // ask wm for a new surface next time. 4230 //noinspection ReturnInsideFinallyBlock 4231 return false; 4232 } 4233 4234 if (LOCAL_LOGV) { 4235 Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost"); 4236 } 4237 } 4238 return true; 4239 } 4240 4241 /** 4242 * We want to draw a highlight around the current accessibility focused. 4243 * Since adding a style for all possible view is not a viable option we 4244 * have this specialized drawing method. 4245 * 4246 * Note: We are doing this here to be able to draw the highlight for 4247 * virtual views in addition to real ones. 4248 * 4249 * @param canvas The canvas on which to draw. 4250 */ drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas)4251 private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) { 4252 final Rect bounds = mAttachInfo.mTmpInvalRect; 4253 if (getAccessibilityFocusedRect(bounds)) { 4254 final Drawable drawable = getAccessibilityFocusedDrawable(); 4255 if (drawable != null) { 4256 drawable.setBounds(bounds); 4257 drawable.draw(canvas); 4258 } 4259 } else if (mAttachInfo.mAccessibilityFocusDrawable != null) { 4260 mAttachInfo.mAccessibilityFocusDrawable.setBounds(0, 0, 0, 0); 4261 } 4262 } 4263 getAccessibilityFocusedRect(Rect bounds)4264 private boolean getAccessibilityFocusedRect(Rect bounds) { 4265 final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext); 4266 if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) { 4267 return false; 4268 } 4269 4270 final View host = mAccessibilityFocusedHost; 4271 if (host == null || host.mAttachInfo == null) { 4272 return false; 4273 } 4274 4275 final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); 4276 if (provider == null) { 4277 host.getBoundsOnScreen(bounds, true); 4278 } else if (mAccessibilityFocusedVirtualView != null) { 4279 mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds); 4280 } else { 4281 return false; 4282 } 4283 4284 // Transform the rect into window-relative coordinates. 4285 final AttachInfo attachInfo = mAttachInfo; 4286 bounds.offset(0, attachInfo.mViewRootImpl.mScrollY); 4287 bounds.offset(-attachInfo.mWindowLeft, -attachInfo.mWindowTop); 4288 if (!bounds.intersect(0, 0, attachInfo.mViewRootImpl.mWidth, 4289 attachInfo.mViewRootImpl.mHeight)) { 4290 // If no intersection, set bounds to empty. 4291 bounds.setEmpty(); 4292 } 4293 return !bounds.isEmpty(); 4294 } 4295 getAccessibilityFocusedDrawable()4296 private Drawable getAccessibilityFocusedDrawable() { 4297 // Lazily load the accessibility focus drawable. 4298 if (mAttachInfo.mAccessibilityFocusDrawable == null) { 4299 final TypedValue value = new TypedValue(); 4300 final boolean resolved = mView.mContext.getTheme().resolveAttribute( 4301 R.attr.accessibilityFocusedDrawable, value, true); 4302 if (resolved) { 4303 mAttachInfo.mAccessibilityFocusDrawable = 4304 mView.mContext.getDrawable(value.resourceId); 4305 } 4306 } 4307 return mAttachInfo.mAccessibilityFocusDrawable; 4308 } 4309 updateSystemGestureExclusionRectsForView(View view)4310 void updateSystemGestureExclusionRectsForView(View view) { 4311 mGestureExclusionTracker.updateRectsForView(view); 4312 mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); 4313 } 4314 systemGestureExclusionChanged()4315 void systemGestureExclusionChanged() { 4316 final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects(); 4317 if (rectsForWindowManager != null && mView != null) { 4318 try { 4319 mWindowSession.reportSystemGestureExclusionChanged(mWindow, rectsForWindowManager); 4320 } catch (RemoteException e) { 4321 throw e.rethrowFromSystemServer(); 4322 } 4323 mAttachInfo.mTreeObserver 4324 .dispatchOnSystemGestureExclusionRectsChanged(rectsForWindowManager); 4325 } 4326 } 4327 updateLocationInParentDisplay(int x, int y)4328 void updateLocationInParentDisplay(int x, int y) { 4329 if (mAttachInfo != null 4330 && !mAttachInfo.mLocationInParentDisplay.equals(x, y)) { 4331 mAttachInfo.mLocationInParentDisplay.set(x, y); 4332 } 4333 } 4334 4335 /** 4336 * Set the root-level system gesture exclusion rects. These are added to those provided by 4337 * the root's view hierarchy. 4338 */ setRootSystemGestureExclusionRects(@onNull List<Rect> rects)4339 public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { 4340 mGestureExclusionTracker.setRootSystemGestureExclusionRects(rects); 4341 mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); 4342 } 4343 4344 /** 4345 * Returns the root-level system gesture exclusion rects. These do not include those provided by 4346 * the root's view hierarchy. 4347 */ 4348 @NonNull getRootSystemGestureExclusionRects()4349 public List<Rect> getRootSystemGestureExclusionRects() { 4350 return mGestureExclusionTracker.getRootSystemGestureExclusionRects(); 4351 } 4352 4353 /** 4354 * Requests that the root render node is invalidated next time we perform a draw, such that 4355 * {@link WindowCallbacks#onPostDraw} gets called. 4356 */ requestInvalidateRootRenderNode()4357 public void requestInvalidateRootRenderNode() { 4358 mInvalidateRootRequested = true; 4359 } 4360 scrollToRectOrFocus(Rect rectangle, boolean immediate)4361 boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) { 4362 final Rect ci = getWindowInsets(false).getSystemWindowInsetsAsRect(); 4363 final Rect vi = mAttachInfo.mVisibleInsets; 4364 int scrollY = 0; 4365 boolean handled = false; 4366 4367 if (vi.left > ci.left || vi.top > ci.top 4368 || vi.right > ci.right || vi.bottom > ci.bottom) { 4369 // We'll assume that we aren't going to change the scroll 4370 // offset, since we want to avoid that unless it is actually 4371 // going to make the focus visible... otherwise we scroll 4372 // all over the place. 4373 scrollY = mScrollY; 4374 // We can be called for two different situations: during a draw, 4375 // to update the scroll position if the focus has changed (in which 4376 // case 'rectangle' is null), or in response to a 4377 // requestChildRectangleOnScreen() call (in which case 'rectangle' 4378 // is non-null and we just want to scroll to whatever that 4379 // rectangle is). 4380 final View focus = mView.findFocus(); 4381 if (focus == null) { 4382 return false; 4383 } 4384 View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null; 4385 if (focus != lastScrolledFocus) { 4386 // If the focus has changed, then ignore any requests to scroll 4387 // to a rectangle; first we want to make sure the entire focus 4388 // view is visible. 4389 rectangle = null; 4390 } 4391 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Eval scroll: focus=" + focus 4392 + " rectangle=" + rectangle + " ci=" + ci 4393 + " vi=" + vi); 4394 if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) { 4395 // Optimization: if the focus hasn't changed since last 4396 // time, and no layout has happened, then just leave things 4397 // as they are. 4398 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Keeping scroll y=" 4399 + mScrollY + " vi=" + vi.toShortString()); 4400 } else { 4401 // We need to determine if the currently focused view is 4402 // within the visible part of the window and, if not, apply 4403 // a pan so it can be seen. 4404 mLastScrolledFocus = new WeakReference<View>(focus); 4405 mScrollMayChange = false; 4406 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Need to scroll?"); 4407 // Try to find the rectangle from the focus view. 4408 if (focus.getGlobalVisibleRect(mVisRect, null)) { 4409 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Root w=" 4410 + mView.getWidth() + " h=" + mView.getHeight() 4411 + " ci=" + ci.toShortString() 4412 + " vi=" + vi.toShortString()); 4413 if (rectangle == null) { 4414 focus.getFocusedRect(mTempRect); 4415 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Focus " + focus 4416 + ": focusRect=" + mTempRect.toShortString()); 4417 if (mView instanceof ViewGroup) { 4418 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 4419 focus, mTempRect); 4420 } 4421 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 4422 "Focus in window: focusRect=" 4423 + mTempRect.toShortString() 4424 + " visRect=" + mVisRect.toShortString()); 4425 } else { 4426 mTempRect.set(rectangle); 4427 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 4428 "Request scroll to rect: " 4429 + mTempRect.toShortString() 4430 + " visRect=" + mVisRect.toShortString()); 4431 } 4432 if (mTempRect.intersect(mVisRect)) { 4433 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 4434 "Focus window visible rect: " 4435 + mTempRect.toShortString()); 4436 if (mTempRect.height() > 4437 (mView.getHeight()-vi.top-vi.bottom)) { 4438 // If the focus simply is not going to fit, then 4439 // best is probably just to leave things as-is. 4440 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 4441 "Too tall; leaving scrollY=" + scrollY); 4442 } 4443 // Next, check whether top or bottom is covered based on the non-scrolled 4444 // position, and calculate new scrollY (or set it to 0). 4445 // We can't keep using mScrollY here. For example mScrollY is non-zero 4446 // due to IME, then IME goes away. The current value of mScrollY leaves top 4447 // and bottom both visible, but we still need to scroll it back to 0. 4448 else if (mTempRect.top < vi.top) { 4449 scrollY = mTempRect.top - vi.top; 4450 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 4451 "Top covered; scrollY=" + scrollY); 4452 } else if (mTempRect.bottom > (mView.getHeight()-vi.bottom)) { 4453 scrollY = mTempRect.bottom - (mView.getHeight()-vi.bottom); 4454 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 4455 "Bottom covered; scrollY=" + scrollY); 4456 } else { 4457 scrollY = 0; 4458 } 4459 handled = true; 4460 } 4461 } 4462 } 4463 } 4464 4465 if (scrollY != mScrollY) { 4466 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Pan scroll changed: old=" 4467 + mScrollY + " , new=" + scrollY); 4468 if (!immediate) { 4469 if (mScroller == null) { 4470 mScroller = new Scroller(mView.getContext()); 4471 } 4472 mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY); 4473 } else if (mScroller != null) { 4474 mScroller.abortAnimation(); 4475 } 4476 mScrollY = scrollY; 4477 } 4478 4479 return handled; 4480 } 4481 4482 /** 4483 * @hide 4484 */ 4485 @UnsupportedAppUsage getAccessibilityFocusedHost()4486 public View getAccessibilityFocusedHost() { 4487 return mAccessibilityFocusedHost; 4488 } 4489 4490 /** 4491 * @hide 4492 */ 4493 @UnsupportedAppUsage getAccessibilityFocusedVirtualView()4494 public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() { 4495 return mAccessibilityFocusedVirtualView; 4496 } 4497 setAccessibilityFocus(View view, AccessibilityNodeInfo node)4498 void setAccessibilityFocus(View view, AccessibilityNodeInfo node) { 4499 // If we have a virtual view with accessibility focus we need 4500 // to clear the focus and invalidate the virtual view bounds. 4501 if (mAccessibilityFocusedVirtualView != null) { 4502 4503 AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView; 4504 View focusHost = mAccessibilityFocusedHost; 4505 4506 // Wipe the state of the current accessibility focus since 4507 // the call into the provider to clear accessibility focus 4508 // will fire an accessibility event which will end up calling 4509 // this method and we want to have clean state when this 4510 // invocation happens. 4511 mAccessibilityFocusedHost = null; 4512 mAccessibilityFocusedVirtualView = null; 4513 4514 // Clear accessibility focus on the host after clearing state since 4515 // this method may be reentrant. 4516 focusHost.clearAccessibilityFocusNoCallbacks( 4517 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 4518 4519 AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider(); 4520 if (provider != null) { 4521 // Invalidate the area of the cleared accessibility focus. 4522 focusNode.getBoundsInParent(mTempRect); 4523 focusHost.invalidate(mTempRect); 4524 // Clear accessibility focus in the virtual node. 4525 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( 4526 focusNode.getSourceNodeId()); 4527 provider.performAction(virtualNodeId, 4528 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); 4529 } 4530 focusNode.recycle(); 4531 } 4532 if ((mAccessibilityFocusedHost != null) && (mAccessibilityFocusedHost != view)) { 4533 // Clear accessibility focus in the view. 4534 mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks( 4535 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 4536 } 4537 4538 // Set the new focus host and node. 4539 mAccessibilityFocusedHost = view; 4540 mAccessibilityFocusedVirtualView = node; 4541 4542 if (mAttachInfo.mThreadedRenderer != null) { 4543 mAttachInfo.mThreadedRenderer.invalidateRoot(); 4544 } 4545 } 4546 hasPointerCapture()4547 boolean hasPointerCapture() { 4548 return mPointerCapture; 4549 } 4550 requestPointerCapture(boolean enabled)4551 void requestPointerCapture(boolean enabled) { 4552 if (mPointerCapture == enabled) { 4553 return; 4554 } 4555 InputManager.getInstance().requestPointerCapture(mAttachInfo.mWindowToken, enabled); 4556 } 4557 handlePointerCaptureChanged(boolean hasCapture)4558 private void handlePointerCaptureChanged(boolean hasCapture) { 4559 if (mPointerCapture == hasCapture) { 4560 return; 4561 } 4562 mPointerCapture = hasCapture; 4563 if (mView != null) { 4564 mView.dispatchPointerCaptureChanged(hasCapture); 4565 } 4566 } 4567 hasColorModeChanged(int colorMode)4568 private boolean hasColorModeChanged(int colorMode) { 4569 if (mAttachInfo.mThreadedRenderer == null) { 4570 return false; 4571 } 4572 final boolean isWideGamut = colorMode == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT; 4573 if (mAttachInfo.mThreadedRenderer.isWideGamut() == isWideGamut) { 4574 return false; 4575 } 4576 if (isWideGamut && !mContext.getResources().getConfiguration().isScreenWideColorGamut()) { 4577 return false; 4578 } 4579 return true; 4580 } 4581 4582 @Override requestChildFocus(View child, View focused)4583 public void requestChildFocus(View child, View focused) { 4584 if (DEBUG_INPUT_RESIZE) { 4585 Log.v(mTag, "Request child focus: focus now " + focused); 4586 } 4587 checkThread(); 4588 scheduleTraversals(); 4589 } 4590 4591 @Override clearChildFocus(View child)4592 public void clearChildFocus(View child) { 4593 if (DEBUG_INPUT_RESIZE) { 4594 Log.v(mTag, "Clearing child focus"); 4595 } 4596 checkThread(); 4597 scheduleTraversals(); 4598 } 4599 4600 @Override getParentForAccessibility()4601 public ViewParent getParentForAccessibility() { 4602 return null; 4603 } 4604 4605 @Override focusableViewAvailable(View v)4606 public void focusableViewAvailable(View v) { 4607 checkThread(); 4608 if (mView != null) { 4609 if (!mView.hasFocus()) { 4610 if (sAlwaysAssignFocus || !mAttachInfo.mInTouchMode) { 4611 v.requestFocus(); 4612 } 4613 } else { 4614 // the one case where will transfer focus away from the current one 4615 // is if the current view is a view group that prefers to give focus 4616 // to its children first AND the view is a descendant of it. 4617 View focused = mView.findFocus(); 4618 if (focused instanceof ViewGroup) { 4619 ViewGroup group = (ViewGroup) focused; 4620 if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS 4621 && isViewDescendantOf(v, focused)) { 4622 v.requestFocus(); 4623 } 4624 } 4625 } 4626 } 4627 } 4628 4629 @Override recomputeViewAttributes(View child)4630 public void recomputeViewAttributes(View child) { 4631 checkThread(); 4632 if (mView == child) { 4633 mAttachInfo.mRecomputeGlobalAttributes = true; 4634 if (!mWillDrawSoon) { 4635 scheduleTraversals(); 4636 } 4637 } 4638 } 4639 dispatchDetachedFromWindow()4640 void dispatchDetachedFromWindow() { 4641 // Make sure we free-up insets resources if view never received onWindowFocusLost() 4642 // because of a die-signal 4643 mInsetsController.onWindowFocusLost(); 4644 mFirstInputStage.onDetachedFromWindow(); 4645 if (mView != null && mView.mAttachInfo != null) { 4646 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); 4647 mView.dispatchDetachedFromWindow(); 4648 } 4649 4650 mAccessibilityInteractionConnectionManager.ensureNoConnection(); 4651 mAccessibilityManager.removeAccessibilityStateChangeListener( 4652 mAccessibilityInteractionConnectionManager); 4653 mAccessibilityManager.removeHighTextContrastStateChangeListener( 4654 mHighContrastTextManager); 4655 removeSendWindowContentChangedCallback(); 4656 4657 destroyHardwareRenderer(); 4658 4659 setAccessibilityFocus(null, null); 4660 4661 mInsetsController.cancelExistingAnimations(); 4662 4663 mView.assignParent(null); 4664 mView = null; 4665 mAttachInfo.mRootView = null; 4666 4667 destroySurface(); 4668 4669 if (mInputQueueCallback != null && mInputQueue != null) { 4670 mInputQueueCallback.onInputQueueDestroyed(mInputQueue); 4671 mInputQueue.dispose(); 4672 mInputQueueCallback = null; 4673 mInputQueue = null; 4674 } 4675 try { 4676 mWindowSession.remove(mWindow); 4677 } catch (RemoteException e) { 4678 } 4679 // Dispose receiver would dispose client InputChannel, too. That could send out a socket 4680 // broken event, so we need to unregister the server InputChannel when removing window to 4681 // prevent server side receive the event and prompt error. 4682 if (mInputEventReceiver != null) { 4683 mInputEventReceiver.dispose(); 4684 mInputEventReceiver = null; 4685 } 4686 4687 mDisplayManager.unregisterDisplayListener(mDisplayListener); 4688 4689 unscheduleTraversals(); 4690 } 4691 4692 /** 4693 * Notifies all callbacks that configuration and/or display has changed and updates internal 4694 * state. 4695 * @param mergedConfiguration New global and override config in {@link MergedConfiguration} 4696 * container. 4697 * @param force Flag indicating if we should force apply the config. 4698 * @param newDisplayId Id of new display if moved, {@link Display#INVALID_DISPLAY} if not 4699 * changed. 4700 */ performConfigurationChange(MergedConfiguration mergedConfiguration, boolean force, int newDisplayId)4701 private void performConfigurationChange(MergedConfiguration mergedConfiguration, boolean force, 4702 int newDisplayId) { 4703 if (mergedConfiguration == null) { 4704 throw new IllegalArgumentException("No merged config provided."); 4705 } 4706 4707 Configuration globalConfig = mergedConfiguration.getGlobalConfiguration(); 4708 final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration(); 4709 if (DEBUG_CONFIGURATION) Log.v(mTag, 4710 "Applying new config to window " + mWindowAttributes.getTitle() 4711 + ", globalConfig: " + globalConfig 4712 + ", overrideConfig: " + overrideConfig); 4713 4714 final CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo(); 4715 if (!ci.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) { 4716 globalConfig = new Configuration(globalConfig); 4717 ci.applyToConfiguration(mNoncompatDensity, globalConfig); 4718 } 4719 4720 synchronized (sConfigCallbacks) { 4721 for (int i=sConfigCallbacks.size()-1; i>=0; i--) { 4722 sConfigCallbacks.get(i).onConfigurationChanged(globalConfig); 4723 } 4724 } 4725 4726 mLastReportedMergedConfiguration.setConfiguration(globalConfig, overrideConfig); 4727 4728 mForceNextConfigUpdate = force; 4729 if (mActivityConfigCallback != null) { 4730 // An activity callback is set - notify it about override configuration update. 4731 // This basically initiates a round trip to ActivityThread and back, which will ensure 4732 // that corresponding activity and resources are updated before updating inner state of 4733 // ViewRootImpl. Eventually it will call #updateConfiguration(). 4734 mActivityConfigCallback.onConfigurationChanged(overrideConfig, newDisplayId); 4735 } else { 4736 // There is no activity callback - update the configuration right away. 4737 updateConfiguration(newDisplayId); 4738 } 4739 mForceNextConfigUpdate = false; 4740 } 4741 4742 /** 4743 * Update display and views if last applied merged configuration changed. 4744 * @param newDisplayId Id of new display if moved, {@link Display#INVALID_DISPLAY} otherwise. 4745 */ updateConfiguration(int newDisplayId)4746 public void updateConfiguration(int newDisplayId) { 4747 if (mView == null) { 4748 return; 4749 } 4750 4751 // At this point the resources have been updated to 4752 // have the most recent config, whatever that is. Use 4753 // the one in them which may be newer. 4754 final Resources localResources = mView.getResources(); 4755 final Configuration config = localResources.getConfiguration(); 4756 4757 // Handle move to display. 4758 if (newDisplayId != INVALID_DISPLAY) { 4759 onMovedToDisplay(newDisplayId, config); 4760 } 4761 4762 // Handle configuration change. 4763 if (mForceNextConfigUpdate || mLastConfigurationFromResources.diff(config) != 0) { 4764 // Update the display with new DisplayAdjustments. 4765 updateInternalDisplay(mDisplay.getDisplayId(), localResources); 4766 4767 final int lastLayoutDirection = mLastConfigurationFromResources.getLayoutDirection(); 4768 final int currentLayoutDirection = config.getLayoutDirection(); 4769 mLastConfigurationFromResources.setTo(config); 4770 if (lastLayoutDirection != currentLayoutDirection 4771 && mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { 4772 mView.setLayoutDirection(currentLayoutDirection); 4773 } 4774 mView.dispatchConfigurationChanged(config); 4775 4776 // We could have gotten this {@link Configuration} update after we called 4777 // {@link #performTraversals} with an older {@link Configuration}. As a result, our 4778 // window frame may be stale. We must ensure the next pass of {@link #performTraversals} 4779 // catches this. 4780 mForceNextWindowRelayout = true; 4781 requestLayout(); 4782 } 4783 4784 updateForceDarkMode(); 4785 } 4786 4787 /** 4788 * Return true if child is an ancestor of parent, (or equal to the parent). 4789 */ isViewDescendantOf(View child, View parent)4790 public static boolean isViewDescendantOf(View child, View parent) { 4791 if (child == parent) { 4792 return true; 4793 } 4794 4795 final ViewParent theParent = child.getParent(); 4796 return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); 4797 } 4798 forceLayout(View view)4799 private static void forceLayout(View view) { 4800 view.forceLayout(); 4801 if (view instanceof ViewGroup) { 4802 ViewGroup group = (ViewGroup) view; 4803 final int count = group.getChildCount(); 4804 for (int i = 0; i < count; i++) { 4805 forceLayout(group.getChildAt(i)); 4806 } 4807 } 4808 } 4809 4810 private static final int MSG_INVALIDATE = 1; 4811 private static final int MSG_INVALIDATE_RECT = 2; 4812 private static final int MSG_DIE = 3; 4813 private static final int MSG_RESIZED = 4; 4814 private static final int MSG_RESIZED_REPORT = 5; 4815 private static final int MSG_WINDOW_FOCUS_CHANGED = 6; 4816 private static final int MSG_DISPATCH_INPUT_EVENT = 7; 4817 private static final int MSG_DISPATCH_APP_VISIBILITY = 8; 4818 private static final int MSG_DISPATCH_GET_NEW_SURFACE = 9; 4819 private static final int MSG_DISPATCH_KEY_FROM_IME = 11; 4820 private static final int MSG_DISPATCH_KEY_FROM_AUTOFILL = 12; 4821 private static final int MSG_CHECK_FOCUS = 13; 4822 private static final int MSG_CLOSE_SYSTEM_DIALOGS = 14; 4823 private static final int MSG_DISPATCH_DRAG_EVENT = 15; 4824 private static final int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16; 4825 private static final int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17; 4826 private static final int MSG_UPDATE_CONFIGURATION = 18; 4827 private static final int MSG_PROCESS_INPUT_EVENTS = 19; 4828 private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; 4829 private static final int MSG_INVALIDATE_WORLD = 22; 4830 private static final int MSG_WINDOW_MOVED = 23; 4831 private static final int MSG_SYNTHESIZE_INPUT_EVENT = 24; 4832 private static final int MSG_DISPATCH_WINDOW_SHOWN = 25; 4833 private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; 4834 private static final int MSG_UPDATE_POINTER_ICON = 27; 4835 private static final int MSG_POINTER_CAPTURE_CHANGED = 28; 4836 private static final int MSG_DRAW_FINISHED = 29; 4837 private static final int MSG_INSETS_CHANGED = 30; 4838 private static final int MSG_INSETS_CONTROL_CHANGED = 31; 4839 private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32; 4840 private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33; 4841 private static final int MSG_SHOW_INSETS = 34; 4842 private static final int MSG_HIDE_INSETS = 35; 4843 private static final int MSG_REQUEST_SCROLL_CAPTURE = 36; 4844 4845 4846 final class ViewRootHandler extends Handler { 4847 @Override getMessageName(Message message)4848 public String getMessageName(Message message) { 4849 switch (message.what) { 4850 case MSG_INVALIDATE: 4851 return "MSG_INVALIDATE"; 4852 case MSG_INVALIDATE_RECT: 4853 return "MSG_INVALIDATE_RECT"; 4854 case MSG_DIE: 4855 return "MSG_DIE"; 4856 case MSG_RESIZED: 4857 return "MSG_RESIZED"; 4858 case MSG_RESIZED_REPORT: 4859 return "MSG_RESIZED_REPORT"; 4860 case MSG_WINDOW_FOCUS_CHANGED: 4861 return "MSG_WINDOW_FOCUS_CHANGED"; 4862 case MSG_DISPATCH_INPUT_EVENT: 4863 return "MSG_DISPATCH_INPUT_EVENT"; 4864 case MSG_DISPATCH_APP_VISIBILITY: 4865 return "MSG_DISPATCH_APP_VISIBILITY"; 4866 case MSG_DISPATCH_GET_NEW_SURFACE: 4867 return "MSG_DISPATCH_GET_NEW_SURFACE"; 4868 case MSG_DISPATCH_KEY_FROM_IME: 4869 return "MSG_DISPATCH_KEY_FROM_IME"; 4870 case MSG_DISPATCH_KEY_FROM_AUTOFILL: 4871 return "MSG_DISPATCH_KEY_FROM_AUTOFILL"; 4872 case MSG_CHECK_FOCUS: 4873 return "MSG_CHECK_FOCUS"; 4874 case MSG_CLOSE_SYSTEM_DIALOGS: 4875 return "MSG_CLOSE_SYSTEM_DIALOGS"; 4876 case MSG_DISPATCH_DRAG_EVENT: 4877 return "MSG_DISPATCH_DRAG_EVENT"; 4878 case MSG_DISPATCH_DRAG_LOCATION_EVENT: 4879 return "MSG_DISPATCH_DRAG_LOCATION_EVENT"; 4880 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: 4881 return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY"; 4882 case MSG_UPDATE_CONFIGURATION: 4883 return "MSG_UPDATE_CONFIGURATION"; 4884 case MSG_PROCESS_INPUT_EVENTS: 4885 return "MSG_PROCESS_INPUT_EVENTS"; 4886 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: 4887 return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; 4888 case MSG_WINDOW_MOVED: 4889 return "MSG_WINDOW_MOVED"; 4890 case MSG_SYNTHESIZE_INPUT_EVENT: 4891 return "MSG_SYNTHESIZE_INPUT_EVENT"; 4892 case MSG_DISPATCH_WINDOW_SHOWN: 4893 return "MSG_DISPATCH_WINDOW_SHOWN"; 4894 case MSG_UPDATE_POINTER_ICON: 4895 return "MSG_UPDATE_POINTER_ICON"; 4896 case MSG_POINTER_CAPTURE_CHANGED: 4897 return "MSG_POINTER_CAPTURE_CHANGED"; 4898 case MSG_DRAW_FINISHED: 4899 return "MSG_DRAW_FINISHED"; 4900 case MSG_INSETS_CHANGED: 4901 return "MSG_INSETS_CHANGED"; 4902 case MSG_INSETS_CONTROL_CHANGED: 4903 return "MSG_INSETS_CONTROL_CHANGED"; 4904 case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: 4905 return "MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED"; 4906 case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED: 4907 return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED"; 4908 case MSG_SHOW_INSETS: 4909 return "MSG_SHOW_INSETS"; 4910 case MSG_HIDE_INSETS: 4911 return "MSG_HIDE_INSETS"; 4912 } 4913 return super.getMessageName(message); 4914 } 4915 4916 @Override sendMessageAtTime(Message msg, long uptimeMillis)4917 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 4918 if (msg.what == MSG_REQUEST_KEYBOARD_SHORTCUTS && msg.obj == null) { 4919 // Debugging for b/27963013 4920 throw new NullPointerException( 4921 "Attempted to call MSG_REQUEST_KEYBOARD_SHORTCUTS with null receiver:"); 4922 } 4923 return super.sendMessageAtTime(msg, uptimeMillis); 4924 } 4925 4926 @Override handleMessage(Message msg)4927 public void handleMessage(Message msg) { 4928 switch (msg.what) { 4929 case MSG_INVALIDATE: 4930 ((View) msg.obj).invalidate(); 4931 break; 4932 case MSG_INVALIDATE_RECT: 4933 final View.AttachInfo.InvalidateInfo info = 4934 (View.AttachInfo.InvalidateInfo) msg.obj; 4935 info.target.invalidate(info.left, info.top, info.right, info.bottom); 4936 info.recycle(); 4937 break; 4938 case MSG_PROCESS_INPUT_EVENTS: 4939 mProcessInputEventsScheduled = false; 4940 doProcessInputEvents(); 4941 break; 4942 case MSG_DISPATCH_APP_VISIBILITY: 4943 handleAppVisibility(msg.arg1 != 0); 4944 break; 4945 case MSG_DISPATCH_GET_NEW_SURFACE: 4946 handleGetNewSurface(); 4947 break; 4948 case MSG_RESIZED: { 4949 // Recycled in the fall through... 4950 SomeArgs args = (SomeArgs) msg.obj; 4951 if (mWinFrame.equals(args.arg1) 4952 && mPendingDisplayCutout.get().equals(args.arg9) 4953 && mPendingBackDropFrame.equals(args.arg8) 4954 && mLastReportedMergedConfiguration.equals(args.arg4) 4955 && args.argi1 == 0 4956 && mDisplay.getDisplayId() == args.argi3) { 4957 break; 4958 } 4959 } // fall through... 4960 case MSG_RESIZED_REPORT: 4961 if (mAdded) { 4962 SomeArgs args = (SomeArgs) msg.obj; 4963 4964 final int displayId = args.argi3; 4965 MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4; 4966 final boolean displayChanged = mDisplay.getDisplayId() != displayId; 4967 boolean configChanged = false; 4968 4969 if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) { 4970 // If configuration changed - notify about that and, maybe, 4971 // about move to display. 4972 performConfigurationChange(mergedConfiguration, false /* force */, 4973 displayChanged 4974 ? displayId : INVALID_DISPLAY /* same display */); 4975 configChanged = true; 4976 } else if (displayChanged) { 4977 // Moved to display without config change - report last applied one. 4978 onMovedToDisplay(displayId, mLastConfigurationFromResources); 4979 } 4980 4981 final boolean framesChanged = !mWinFrame.equals(args.arg1) 4982 || !mPendingDisplayCutout.get().equals(args.arg9); 4983 4984 setFrame((Rect) args.arg1); 4985 mPendingDisplayCutout.set((DisplayCutout) args.arg9); 4986 mPendingBackDropFrame.set((Rect) args.arg8); 4987 mForceNextWindowRelayout = args.argi1 != 0; 4988 mPendingAlwaysConsumeSystemBars = args.argi2 != 0; 4989 4990 args.recycle(); 4991 4992 if (msg.what == MSG_RESIZED_REPORT) { 4993 reportNextDraw(); 4994 } 4995 4996 if (mView != null && (framesChanged || configChanged)) { 4997 forceLayout(mView); 4998 } 4999 requestLayout(); 5000 } 5001 break; 5002 case MSG_INSETS_CHANGED: 5003 mInsetsController.onStateChanged((InsetsState) msg.obj); 5004 break; 5005 case MSG_INSETS_CONTROL_CHANGED: { 5006 SomeArgs args = (SomeArgs) msg.obj; 5007 5008 // Deliver state change before control change, such that: 5009 // a) When gaining control, controller can compare with server state to evaluate 5010 // whether it needs to run animation. 5011 // b) When loosing control, controller can restore server state by taking last 5012 // dispatched state as truth. 5013 mInsetsController.onStateChanged((InsetsState) args.arg1); 5014 mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2); 5015 break; 5016 } 5017 case MSG_SHOW_INSETS: { 5018 if (mView == null) { 5019 Log.e(TAG, 5020 String.format("Calling showInsets(%d,%b) on window that no longer" 5021 + " has views.", msg.arg1, msg.arg2 == 1)); 5022 } 5023 mInsetsController.show(msg.arg1, msg.arg2 == 1); 5024 break; 5025 } 5026 case MSG_HIDE_INSETS: { 5027 mInsetsController.hide(msg.arg1, msg.arg2 == 1); 5028 break; 5029 } 5030 case MSG_WINDOW_MOVED: 5031 if (mAdded) { 5032 final int w = mWinFrame.width(); 5033 final int h = mWinFrame.height(); 5034 final int l = msg.arg1; 5035 final int t = msg.arg2; 5036 mTmpFrame.left = l; 5037 mTmpFrame.right = l + w; 5038 mTmpFrame.top = t; 5039 mTmpFrame.bottom = t + h; 5040 setFrame(mTmpFrame); 5041 5042 mPendingBackDropFrame.set(mWinFrame); 5043 maybeHandleWindowMove(mWinFrame); 5044 } 5045 break; 5046 case MSG_WINDOW_FOCUS_CHANGED: { 5047 handleWindowFocusChanged(); 5048 } break; 5049 case MSG_DIE: 5050 doDie(); 5051 break; 5052 case MSG_DISPATCH_INPUT_EVENT: { 5053 SomeArgs args = (SomeArgs) msg.obj; 5054 InputEvent event = (InputEvent) args.arg1; 5055 InputEventReceiver receiver = (InputEventReceiver) args.arg2; 5056 enqueueInputEvent(event, receiver, 0, true); 5057 args.recycle(); 5058 } break; 5059 case MSG_SYNTHESIZE_INPUT_EVENT: { 5060 InputEvent event = (InputEvent) msg.obj; 5061 enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true); 5062 } break; 5063 case MSG_DISPATCH_KEY_FROM_IME: { 5064 if (LOCAL_LOGV) { 5065 Log.v(TAG, "Dispatching key " + msg.obj + " from IME to " + mView); 5066 } 5067 KeyEvent event = (KeyEvent) msg.obj; 5068 if ((event.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) != 0) { 5069 // The IME is trying to say this event is from the 5070 // system! Bad bad bad! 5071 //noinspection UnusedAssignment 5072 event = KeyEvent.changeFlags(event, 5073 event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); 5074 } 5075 enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true); 5076 } break; 5077 case MSG_DISPATCH_KEY_FROM_AUTOFILL: { 5078 if (LOCAL_LOGV) { 5079 Log.v(TAG, "Dispatching key " + msg.obj + " from Autofill to " + mView); 5080 } 5081 KeyEvent event = (KeyEvent) msg.obj; 5082 enqueueInputEvent(event, null, 0, true); 5083 } break; 5084 case MSG_CHECK_FOCUS: { 5085 getImeFocusController().checkFocus(false, true); 5086 } break; 5087 case MSG_CLOSE_SYSTEM_DIALOGS: { 5088 if (mView != null) { 5089 mView.onCloseSystemDialogs((String) msg.obj); 5090 } 5091 } break; 5092 case MSG_DISPATCH_DRAG_EVENT: { 5093 } // fall through 5094 case MSG_DISPATCH_DRAG_LOCATION_EVENT: { 5095 DragEvent event = (DragEvent) msg.obj; 5096 // only present when this app called startDrag() 5097 event.mLocalState = mLocalDragState; 5098 handleDragEvent(event); 5099 } break; 5100 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: { 5101 handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj); 5102 } break; 5103 case MSG_UPDATE_CONFIGURATION: { 5104 Configuration config = (Configuration) msg.obj; 5105 if (config.isOtherSeqNewer( 5106 mLastReportedMergedConfiguration.getMergedConfiguration())) { 5107 // If we already have a newer merged config applied - use its global part. 5108 config = mLastReportedMergedConfiguration.getGlobalConfiguration(); 5109 } 5110 5111 // Use the newer global config and last reported override config. 5112 mPendingMergedConfiguration.setConfiguration(config, 5113 mLastReportedMergedConfiguration.getOverrideConfiguration()); 5114 5115 performConfigurationChange(mPendingMergedConfiguration, false /* force */, 5116 INVALID_DISPLAY /* same display */); 5117 } break; 5118 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { 5119 setAccessibilityFocus(null, null); 5120 } break; 5121 case MSG_INVALIDATE_WORLD: { 5122 if (mView != null) { 5123 invalidateWorld(mView); 5124 } 5125 } break; 5126 case MSG_DISPATCH_WINDOW_SHOWN: { 5127 handleDispatchWindowShown(); 5128 } break; 5129 case MSG_REQUEST_KEYBOARD_SHORTCUTS: { 5130 final IResultReceiver receiver = (IResultReceiver) msg.obj; 5131 final int deviceId = msg.arg1; 5132 handleRequestKeyboardShortcuts(receiver, deviceId); 5133 } break; 5134 case MSG_UPDATE_POINTER_ICON: { 5135 MotionEvent event = (MotionEvent) msg.obj; 5136 resetPointerIcon(event); 5137 } break; 5138 case MSG_POINTER_CAPTURE_CHANGED: { 5139 final boolean hasCapture = msg.arg1 != 0; 5140 handlePointerCaptureChanged(hasCapture); 5141 } break; 5142 case MSG_DRAW_FINISHED: { 5143 pendingDrawFinished(); 5144 } break; 5145 case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: { 5146 systemGestureExclusionChanged(); 5147 } break; 5148 case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED: { 5149 updateLocationInParentDisplay(msg.arg1, msg.arg2); 5150 } break; 5151 case MSG_REQUEST_SCROLL_CAPTURE: 5152 handleScrollCaptureRequest((IScrollCaptureController) msg.obj); 5153 break; 5154 } 5155 } 5156 } 5157 5158 final ViewRootHandler mHandler = new ViewRootHandler(); 5159 5160 /** 5161 * Something in the current window tells us we need to change the touch mode. For 5162 * example, we are not in touch mode, and the user touches the screen. 5163 * 5164 * If the touch mode has changed, tell the window manager, and handle it locally. 5165 * 5166 * @param inTouchMode Whether we want to be in touch mode. 5167 * @return True if the touch mode changed and focus changed was changed as a result 5168 */ 5169 @UnsupportedAppUsage ensureTouchMode(boolean inTouchMode)5170 boolean ensureTouchMode(boolean inTouchMode) { 5171 if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current " 5172 + "touch mode is " + mAttachInfo.mInTouchMode); 5173 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 5174 5175 // tell the window manager 5176 try { 5177 mWindowSession.setInTouchMode(inTouchMode); 5178 } catch (RemoteException e) { 5179 throw new RuntimeException(e); 5180 } 5181 5182 // handle the change 5183 return ensureTouchModeLocally(inTouchMode); 5184 } 5185 5186 /** 5187 * Ensure that the touch mode for this window is set, and if it is changing, 5188 * take the appropriate action. 5189 * @param inTouchMode Whether we want to be in touch mode. 5190 * @return True if the touch mode changed and focus changed was changed as a result 5191 */ ensureTouchModeLocally(boolean inTouchMode)5192 private boolean ensureTouchModeLocally(boolean inTouchMode) { 5193 if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current " 5194 + "touch mode is " + mAttachInfo.mInTouchMode); 5195 5196 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 5197 5198 mAttachInfo.mInTouchMode = inTouchMode; 5199 mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode); 5200 5201 return (inTouchMode) ? enterTouchMode() : leaveTouchMode(); 5202 } 5203 enterTouchMode()5204 private boolean enterTouchMode() { 5205 if (mView != null && mView.hasFocus()) { 5206 // note: not relying on mFocusedView here because this could 5207 // be when the window is first being added, and mFocused isn't 5208 // set yet. 5209 final View focused = mView.findFocus(); 5210 if (focused != null && !focused.isFocusableInTouchMode()) { 5211 final ViewGroup ancestorToTakeFocus = findAncestorToTakeFocusInTouchMode(focused); 5212 if (ancestorToTakeFocus != null) { 5213 // there is an ancestor that wants focus after its 5214 // descendants that is focusable in touch mode.. give it 5215 // focus 5216 return ancestorToTakeFocus.requestFocus(); 5217 } else { 5218 // There's nothing to focus. Clear and propagate through the 5219 // hierarchy, but don't attempt to place new focus. 5220 focused.clearFocusInternal(null, true, false); 5221 return true; 5222 } 5223 } 5224 } 5225 return false; 5226 } 5227 5228 /** 5229 * Find an ancestor of focused that wants focus after its descendants and is 5230 * focusable in touch mode. 5231 * @param focused The currently focused view. 5232 * @return An appropriate view, or null if no such view exists. 5233 */ findAncestorToTakeFocusInTouchMode(View focused)5234 private static ViewGroup findAncestorToTakeFocusInTouchMode(View focused) { 5235 ViewParent parent = focused.getParent(); 5236 while (parent instanceof ViewGroup) { 5237 final ViewGroup vgParent = (ViewGroup) parent; 5238 if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS 5239 && vgParent.isFocusableInTouchMode()) { 5240 return vgParent; 5241 } 5242 if (vgParent.isRootNamespace()) { 5243 return null; 5244 } else { 5245 parent = vgParent.getParent(); 5246 } 5247 } 5248 return null; 5249 } 5250 leaveTouchMode()5251 private boolean leaveTouchMode() { 5252 if (mView != null) { 5253 if (mView.hasFocus()) { 5254 View focusedView = mView.findFocus(); 5255 if (!(focusedView instanceof ViewGroup)) { 5256 // some view has focus, let it keep it 5257 return false; 5258 } else if (((ViewGroup) focusedView).getDescendantFocusability() != 5259 ViewGroup.FOCUS_AFTER_DESCENDANTS) { 5260 // some view group has focus, and doesn't prefer its children 5261 // over itself for focus, so let them keep it. 5262 return false; 5263 } 5264 } 5265 5266 // find the best view to give focus to in this brave new non-touch-mode 5267 // world 5268 return mView.restoreDefaultFocus(); 5269 } 5270 return false; 5271 } 5272 5273 /** 5274 * Base class for implementing a stage in the chain of responsibility 5275 * for processing input events. 5276 * <p> 5277 * Events are delivered to the stage by the {@link #deliver} method. The stage 5278 * then has the choice of finishing the event or forwarding it to the next stage. 5279 * </p> 5280 */ 5281 abstract class InputStage { 5282 private final InputStage mNext; 5283 5284 protected static final int FORWARD = 0; 5285 protected static final int FINISH_HANDLED = 1; 5286 protected static final int FINISH_NOT_HANDLED = 2; 5287 5288 private String mTracePrefix; 5289 5290 /** 5291 * Creates an input stage. 5292 * @param next The next stage to which events should be forwarded. 5293 */ InputStage(InputStage next)5294 public InputStage(InputStage next) { 5295 mNext = next; 5296 } 5297 5298 /** 5299 * Delivers an event to be processed. 5300 */ deliver(QueuedInputEvent q)5301 public final void deliver(QueuedInputEvent q) { 5302 if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { 5303 forward(q); 5304 } else if (shouldDropInputEvent(q)) { 5305 finish(q, false); 5306 } else { 5307 traceEvent(q, Trace.TRACE_TAG_VIEW); 5308 final int result; 5309 try { 5310 result = onProcess(q); 5311 } finally { 5312 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 5313 } 5314 apply(q, result); 5315 } 5316 } 5317 5318 /** 5319 * Marks the the input event as finished then forwards it to the next stage. 5320 */ finish(QueuedInputEvent q, boolean handled)5321 protected void finish(QueuedInputEvent q, boolean handled) { 5322 q.mFlags |= QueuedInputEvent.FLAG_FINISHED; 5323 if (handled) { 5324 q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED; 5325 } 5326 forward(q); 5327 } 5328 5329 /** 5330 * Forwards the event to the next stage. 5331 */ forward(QueuedInputEvent q)5332 protected void forward(QueuedInputEvent q) { 5333 onDeliverToNext(q); 5334 } 5335 5336 /** 5337 * Applies a result code from {@link #onProcess} to the specified event. 5338 */ apply(QueuedInputEvent q, int result)5339 protected void apply(QueuedInputEvent q, int result) { 5340 if (result == FORWARD) { 5341 forward(q); 5342 } else if (result == FINISH_HANDLED) { 5343 finish(q, true); 5344 } else if (result == FINISH_NOT_HANDLED) { 5345 finish(q, false); 5346 } else { 5347 throw new IllegalArgumentException("Invalid result: " + result); 5348 } 5349 } 5350 5351 /** 5352 * Called when an event is ready to be processed. 5353 * @return A result code indicating how the event was handled. 5354 */ onProcess(QueuedInputEvent q)5355 protected int onProcess(QueuedInputEvent q) { 5356 return FORWARD; 5357 } 5358 5359 /** 5360 * Called when an event is being delivered to the next stage. 5361 */ onDeliverToNext(QueuedInputEvent q)5362 protected void onDeliverToNext(QueuedInputEvent q) { 5363 if (DEBUG_INPUT_STAGES) { 5364 Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q); 5365 } 5366 if (mNext != null) { 5367 mNext.deliver(q); 5368 } else { 5369 finishInputEvent(q); 5370 } 5371 } 5372 onWindowFocusChanged(boolean hasWindowFocus)5373 protected void onWindowFocusChanged(boolean hasWindowFocus) { 5374 if (mNext != null) { 5375 mNext.onWindowFocusChanged(hasWindowFocus); 5376 } 5377 } 5378 onDetachedFromWindow()5379 protected void onDetachedFromWindow() { 5380 if (mNext != null) { 5381 mNext.onDetachedFromWindow(); 5382 } 5383 } 5384 shouldDropInputEvent(QueuedInputEvent q)5385 protected boolean shouldDropInputEvent(QueuedInputEvent q) { 5386 if (mView == null || !mAdded) { 5387 Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent); 5388 return true; 5389 } else if ((!mAttachInfo.mHasWindowFocus 5390 && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) 5391 && !isAutofillUiShowing()) || mStopped 5392 || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) 5393 || (mPausedForTransition && !isBack(q.mEvent))) { 5394 // This is a focus event and the window doesn't currently have input focus or 5395 // has stopped. This could be an event that came back from the previous stage 5396 // but the window has lost focus or stopped in the meantime. 5397 if (isTerminalInputEvent(q.mEvent)) { 5398 // Don't drop terminal input events, however mark them as canceled. 5399 q.mEvent.cancel(); 5400 Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent); 5401 return false; 5402 } 5403 5404 // Drop non-terminal input events. 5405 Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent); 5406 return true; 5407 } 5408 return false; 5409 } 5410 dump(String prefix, PrintWriter writer)5411 void dump(String prefix, PrintWriter writer) { 5412 if (mNext != null) { 5413 mNext.dump(prefix, writer); 5414 } 5415 } 5416 isBack(InputEvent event)5417 private boolean isBack(InputEvent event) { 5418 if (event instanceof KeyEvent) { 5419 return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK; 5420 } else { 5421 return false; 5422 } 5423 } 5424 traceEvent(QueuedInputEvent q, long traceTag)5425 private void traceEvent(QueuedInputEvent q, long traceTag) { 5426 if (!Trace.isTagEnabled(traceTag)) { 5427 return; 5428 } 5429 5430 if (mTracePrefix == null) { 5431 mTracePrefix = getClass().getSimpleName(); 5432 } 5433 Trace.traceBegin(traceTag, mTracePrefix + " id=0x" 5434 + Integer.toHexString(q.mEvent.getId())); 5435 } 5436 } 5437 5438 /** 5439 * Base class for implementing an input pipeline stage that supports 5440 * asynchronous and out-of-order processing of input events. 5441 * <p> 5442 * In addition to what a normal input stage can do, an asynchronous 5443 * input stage may also defer an input event that has been delivered to it 5444 * and finish or forward it later. 5445 * </p> 5446 */ 5447 abstract class AsyncInputStage extends InputStage { 5448 private final String mTraceCounter; 5449 5450 private QueuedInputEvent mQueueHead; 5451 private QueuedInputEvent mQueueTail; 5452 private int mQueueLength; 5453 5454 protected static final int DEFER = 3; 5455 5456 /** 5457 * Creates an asynchronous input stage. 5458 * @param next The next stage to which events should be forwarded. 5459 * @param traceCounter The name of a counter to record the size of 5460 * the queue of pending events. 5461 */ AsyncInputStage(InputStage next, String traceCounter)5462 public AsyncInputStage(InputStage next, String traceCounter) { 5463 super(next); 5464 mTraceCounter = traceCounter; 5465 } 5466 5467 /** 5468 * Marks the event as deferred, which is to say that it will be handled 5469 * asynchronously. The caller is responsible for calling {@link #forward} 5470 * or {@link #finish} later when it is done handling the event. 5471 */ defer(QueuedInputEvent q)5472 protected void defer(QueuedInputEvent q) { 5473 q.mFlags |= QueuedInputEvent.FLAG_DEFERRED; 5474 enqueue(q); 5475 } 5476 5477 @Override forward(QueuedInputEvent q)5478 protected void forward(QueuedInputEvent q) { 5479 // Clear the deferred flag. 5480 q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED; 5481 5482 // Fast path if the queue is empty. 5483 QueuedInputEvent curr = mQueueHead; 5484 if (curr == null) { 5485 super.forward(q); 5486 return; 5487 } 5488 5489 // Determine whether the event must be serialized behind any others 5490 // before it can be delivered to the next stage. This is done because 5491 // deferred events might be handled out of order by the stage. 5492 final int deviceId = q.mEvent.getDeviceId(); 5493 QueuedInputEvent prev = null; 5494 boolean blocked = false; 5495 while (curr != null && curr != q) { 5496 if (!blocked && deviceId == curr.mEvent.getDeviceId()) { 5497 blocked = true; 5498 } 5499 prev = curr; 5500 curr = curr.mNext; 5501 } 5502 5503 // If the event is blocked, then leave it in the queue to be delivered later. 5504 // Note that the event might not yet be in the queue if it was not previously 5505 // deferred so we will enqueue it if needed. 5506 if (blocked) { 5507 if (curr == null) { 5508 enqueue(q); 5509 } 5510 return; 5511 } 5512 5513 // The event is not blocked. Deliver it immediately. 5514 if (curr != null) { 5515 curr = curr.mNext; 5516 dequeue(q, prev); 5517 } 5518 super.forward(q); 5519 5520 // Dequeuing this event may have unblocked successors. Deliver them. 5521 while (curr != null) { 5522 if (deviceId == curr.mEvent.getDeviceId()) { 5523 if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) { 5524 break; 5525 } 5526 QueuedInputEvent next = curr.mNext; 5527 dequeue(curr, prev); 5528 super.forward(curr); 5529 curr = next; 5530 } else { 5531 prev = curr; 5532 curr = curr.mNext; 5533 } 5534 } 5535 } 5536 5537 @Override apply(QueuedInputEvent q, int result)5538 protected void apply(QueuedInputEvent q, int result) { 5539 if (result == DEFER) { 5540 defer(q); 5541 } else { 5542 super.apply(q, result); 5543 } 5544 } 5545 enqueue(QueuedInputEvent q)5546 private void enqueue(QueuedInputEvent q) { 5547 if (mQueueTail == null) { 5548 mQueueHead = q; 5549 mQueueTail = q; 5550 } else { 5551 mQueueTail.mNext = q; 5552 mQueueTail = q; 5553 } 5554 5555 mQueueLength += 1; 5556 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength); 5557 } 5558 dequeue(QueuedInputEvent q, QueuedInputEvent prev)5559 private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) { 5560 if (prev == null) { 5561 mQueueHead = q.mNext; 5562 } else { 5563 prev.mNext = q.mNext; 5564 } 5565 if (mQueueTail == q) { 5566 mQueueTail = prev; 5567 } 5568 q.mNext = null; 5569 5570 mQueueLength -= 1; 5571 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength); 5572 } 5573 5574 @Override dump(String prefix, PrintWriter writer)5575 void dump(String prefix, PrintWriter writer) { 5576 writer.print(prefix); 5577 writer.print(getClass().getName()); 5578 writer.print(": mQueueLength="); 5579 writer.println(mQueueLength); 5580 5581 super.dump(prefix, writer); 5582 } 5583 } 5584 5585 /** 5586 * Delivers pre-ime input events to a native activity. 5587 * Does not support pointer events. 5588 */ 5589 final class NativePreImeInputStage extends AsyncInputStage 5590 implements InputQueue.FinishedInputEventCallback { NativePreImeInputStage(InputStage next, String traceCounter)5591 public NativePreImeInputStage(InputStage next, String traceCounter) { 5592 super(next, traceCounter); 5593 } 5594 5595 @Override onProcess(QueuedInputEvent q)5596 protected int onProcess(QueuedInputEvent q) { 5597 if (mInputQueue != null && q.mEvent instanceof KeyEvent) { 5598 mInputQueue.sendInputEvent(q.mEvent, q, true, this); 5599 return DEFER; 5600 } 5601 return FORWARD; 5602 } 5603 5604 @Override onFinishedInputEvent(Object token, boolean handled)5605 public void onFinishedInputEvent(Object token, boolean handled) { 5606 QueuedInputEvent q = (QueuedInputEvent)token; 5607 if (handled) { 5608 finish(q, true); 5609 return; 5610 } 5611 forward(q); 5612 } 5613 } 5614 5615 /** 5616 * Delivers pre-ime input events to the view hierarchy. 5617 * Does not support pointer events. 5618 */ 5619 final class ViewPreImeInputStage extends InputStage { ViewPreImeInputStage(InputStage next)5620 public ViewPreImeInputStage(InputStage next) { 5621 super(next); 5622 } 5623 5624 @Override onProcess(QueuedInputEvent q)5625 protected int onProcess(QueuedInputEvent q) { 5626 if (q.mEvent instanceof KeyEvent) { 5627 return processKeyEvent(q); 5628 } 5629 return FORWARD; 5630 } 5631 processKeyEvent(QueuedInputEvent q)5632 private int processKeyEvent(QueuedInputEvent q) { 5633 final KeyEvent event = (KeyEvent)q.mEvent; 5634 if (mView.dispatchKeyEventPreIme(event)) { 5635 return FINISH_HANDLED; 5636 } 5637 return FORWARD; 5638 } 5639 } 5640 5641 /** 5642 * Delivers input events to the ime. 5643 * Does not support pointer events. 5644 */ 5645 final class ImeInputStage extends AsyncInputStage 5646 implements InputMethodManager.FinishedInputEventCallback { ImeInputStage(InputStage next, String traceCounter)5647 public ImeInputStage(InputStage next, String traceCounter) { 5648 super(next, traceCounter); 5649 } 5650 5651 @Override onProcess(QueuedInputEvent q)5652 protected int onProcess(QueuedInputEvent q) { 5653 final int result = mImeFocusController.onProcessImeInputStage( 5654 q, q.mEvent, mWindowAttributes, this); 5655 switch (result) { 5656 case InputMethodManager.DISPATCH_IN_PROGRESS: 5657 // callback will be invoked later 5658 return DEFER; 5659 case InputMethodManager.DISPATCH_NOT_HANDLED: 5660 // The IME could not handle it, so skip along to the next InputStage 5661 return FORWARD; 5662 case InputMethodManager.DISPATCH_HANDLED: 5663 return FINISH_HANDLED; 5664 default: 5665 throw new IllegalStateException("Unexpected result=" + result); 5666 } 5667 } 5668 5669 @Override onFinishedInputEvent(Object token, boolean handled)5670 public void onFinishedInputEvent(Object token, boolean handled) { 5671 QueuedInputEvent q = (QueuedInputEvent)token; 5672 if (handled) { 5673 finish(q, true); 5674 return; 5675 } 5676 forward(q); 5677 } 5678 } 5679 5680 /** 5681 * Performs early processing of post-ime input events. 5682 */ 5683 final class EarlyPostImeInputStage extends InputStage { EarlyPostImeInputStage(InputStage next)5684 public EarlyPostImeInputStage(InputStage next) { 5685 super(next); 5686 } 5687 5688 @Override onProcess(QueuedInputEvent q)5689 protected int onProcess(QueuedInputEvent q) { 5690 if (q.mEvent instanceof KeyEvent) { 5691 return processKeyEvent(q); 5692 } else if (q.mEvent instanceof MotionEvent) { 5693 return processMotionEvent(q); 5694 } 5695 return FORWARD; 5696 } 5697 processKeyEvent(QueuedInputEvent q)5698 private int processKeyEvent(QueuedInputEvent q) { 5699 final KeyEvent event = (KeyEvent)q.mEvent; 5700 5701 if (mAttachInfo.mTooltipHost != null) { 5702 mAttachInfo.mTooltipHost.handleTooltipKey(event); 5703 } 5704 5705 // If the key's purpose is to exit touch mode then we consume it 5706 // and consider it handled. 5707 if (checkForLeavingTouchModeAndConsume(event)) { 5708 return FINISH_HANDLED; 5709 } 5710 5711 // Make sure the fallback event policy sees all keys that will be 5712 // delivered to the view hierarchy. 5713 mFallbackEventHandler.preDispatchKeyEvent(event); 5714 return FORWARD; 5715 } 5716 processMotionEvent(QueuedInputEvent q)5717 private int processMotionEvent(QueuedInputEvent q) { 5718 final MotionEvent event = (MotionEvent) q.mEvent; 5719 5720 if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { 5721 return processPointerEvent(q); 5722 } 5723 5724 // If the motion event is from an absolute position device, exit touch mode 5725 final int action = event.getActionMasked(); 5726 if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { 5727 if (event.isFromSource(InputDevice.SOURCE_CLASS_POSITION)) { 5728 ensureTouchMode(false); 5729 } 5730 } 5731 return FORWARD; 5732 } 5733 processPointerEvent(QueuedInputEvent q)5734 private int processPointerEvent(QueuedInputEvent q) { 5735 final MotionEvent event = (MotionEvent)q.mEvent; 5736 5737 // Translate the pointer event for compatibility, if needed. 5738 if (mTranslator != null) { 5739 mTranslator.translateEventInScreenToAppWindow(event); 5740 } 5741 5742 // Enter touch mode on down or scroll from any type of a device. 5743 final int action = event.getAction(); 5744 if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { 5745 ensureTouchMode(true); 5746 } 5747 5748 if (action == MotionEvent.ACTION_DOWN) { 5749 // Upon motion event within app window, close autofill ui. 5750 AutofillManager afm = getAutofillManager(); 5751 if (afm != null) { 5752 afm.requestHideFillUi(); 5753 } 5754 } 5755 5756 if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) { 5757 mAttachInfo.mTooltipHost.hideTooltip(); 5758 } 5759 5760 // Offset the scroll position. 5761 if (mCurScrollY != 0) { 5762 event.offsetLocation(0, mCurScrollY); 5763 } 5764 5765 // Remember the touch position for possible drag-initiation. 5766 if (event.isTouchEvent()) { 5767 mLastTouchPoint.x = event.getRawX(); 5768 mLastTouchPoint.y = event.getRawY(); 5769 mLastTouchSource = event.getSource(); 5770 } 5771 return FORWARD; 5772 } 5773 } 5774 5775 /** 5776 * Delivers post-ime input events to a native activity. 5777 */ 5778 final class NativePostImeInputStage extends AsyncInputStage 5779 implements InputQueue.FinishedInputEventCallback { NativePostImeInputStage(InputStage next, String traceCounter)5780 public NativePostImeInputStage(InputStage next, String traceCounter) { 5781 super(next, traceCounter); 5782 } 5783 5784 @Override onProcess(QueuedInputEvent q)5785 protected int onProcess(QueuedInputEvent q) { 5786 if (mInputQueue != null) { 5787 mInputQueue.sendInputEvent(q.mEvent, q, false, this); 5788 return DEFER; 5789 } 5790 return FORWARD; 5791 } 5792 5793 @Override onFinishedInputEvent(Object token, boolean handled)5794 public void onFinishedInputEvent(Object token, boolean handled) { 5795 QueuedInputEvent q = (QueuedInputEvent)token; 5796 if (handled) { 5797 finish(q, true); 5798 return; 5799 } 5800 forward(q); 5801 } 5802 } 5803 5804 /** 5805 * Delivers post-ime input events to the view hierarchy. 5806 */ 5807 final class ViewPostImeInputStage extends InputStage { ViewPostImeInputStage(InputStage next)5808 public ViewPostImeInputStage(InputStage next) { 5809 super(next); 5810 } 5811 5812 @Override onProcess(QueuedInputEvent q)5813 protected int onProcess(QueuedInputEvent q) { 5814 if (q.mEvent instanceof KeyEvent) { 5815 return processKeyEvent(q); 5816 } else { 5817 final int source = q.mEvent.getSource(); 5818 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { 5819 return processPointerEvent(q); 5820 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 5821 return processTrackballEvent(q); 5822 } else { 5823 return processGenericMotionEvent(q); 5824 } 5825 } 5826 } 5827 5828 @Override onDeliverToNext(QueuedInputEvent q)5829 protected void onDeliverToNext(QueuedInputEvent q) { 5830 if (mUnbufferedInputDispatch 5831 && q.mEvent instanceof MotionEvent 5832 && ((MotionEvent)q.mEvent).isTouchEvent() 5833 && isTerminalInputEvent(q.mEvent)) { 5834 mUnbufferedInputDispatch = false; 5835 scheduleConsumeBatchedInput(); 5836 } 5837 super.onDeliverToNext(q); 5838 } 5839 performFocusNavigation(KeyEvent event)5840 private boolean performFocusNavigation(KeyEvent event) { 5841 int direction = 0; 5842 switch (event.getKeyCode()) { 5843 case KeyEvent.KEYCODE_DPAD_LEFT: 5844 if (event.hasNoModifiers()) { 5845 direction = View.FOCUS_LEFT; 5846 } 5847 break; 5848 case KeyEvent.KEYCODE_DPAD_RIGHT: 5849 if (event.hasNoModifiers()) { 5850 direction = View.FOCUS_RIGHT; 5851 } 5852 break; 5853 case KeyEvent.KEYCODE_DPAD_UP: 5854 if (event.hasNoModifiers()) { 5855 direction = View.FOCUS_UP; 5856 } 5857 break; 5858 case KeyEvent.KEYCODE_DPAD_DOWN: 5859 if (event.hasNoModifiers()) { 5860 direction = View.FOCUS_DOWN; 5861 } 5862 break; 5863 case KeyEvent.KEYCODE_TAB: 5864 if (event.hasNoModifiers()) { 5865 direction = View.FOCUS_FORWARD; 5866 } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { 5867 direction = View.FOCUS_BACKWARD; 5868 } 5869 break; 5870 } 5871 if (direction != 0) { 5872 View focused = mView.findFocus(); 5873 if (focused != null) { 5874 View v = focused.focusSearch(direction); 5875 if (v != null && v != focused) { 5876 // do the math the get the interesting rect 5877 // of previous focused into the coord system of 5878 // newly focused view 5879 focused.getFocusedRect(mTempRect); 5880 if (mView instanceof ViewGroup) { 5881 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 5882 focused, mTempRect); 5883 ((ViewGroup) mView).offsetRectIntoDescendantCoords( 5884 v, mTempRect); 5885 } 5886 if (v.requestFocus(direction, mTempRect)) { 5887 playSoundEffect(SoundEffectConstants 5888 .getContantForFocusDirection(direction)); 5889 return true; 5890 } 5891 } 5892 5893 // Give the focused view a last chance to handle the dpad key. 5894 if (mView.dispatchUnhandledMove(focused, direction)) { 5895 return true; 5896 } 5897 } else { 5898 if (mView.restoreDefaultFocus()) { 5899 return true; 5900 } 5901 } 5902 } 5903 return false; 5904 } 5905 performKeyboardGroupNavigation(int direction)5906 private boolean performKeyboardGroupNavigation(int direction) { 5907 final View focused = mView.findFocus(); 5908 if (focused == null && mView.restoreDefaultFocus()) { 5909 return true; 5910 } 5911 View cluster = focused == null ? keyboardNavigationClusterSearch(null, direction) 5912 : focused.keyboardNavigationClusterSearch(null, direction); 5913 5914 // Since requestFocus only takes "real" focus directions (and therefore also 5915 // restoreFocusInCluster), convert forward/backward focus into FOCUS_DOWN. 5916 int realDirection = direction; 5917 if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) { 5918 realDirection = View.FOCUS_DOWN; 5919 } 5920 5921 if (cluster != null && cluster.isRootNamespace()) { 5922 // the default cluster. Try to find a non-clustered view to focus. 5923 if (cluster.restoreFocusNotInCluster()) { 5924 playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); 5925 return true; 5926 } 5927 // otherwise skip to next actual cluster 5928 cluster = keyboardNavigationClusterSearch(null, direction); 5929 } 5930 5931 if (cluster != null && cluster.restoreFocusInCluster(realDirection)) { 5932 playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); 5933 return true; 5934 } 5935 5936 return false; 5937 } 5938 processKeyEvent(QueuedInputEvent q)5939 private int processKeyEvent(QueuedInputEvent q) { 5940 final KeyEvent event = (KeyEvent)q.mEvent; 5941 5942 if (mUnhandledKeyManager.preViewDispatch(event)) { 5943 return FINISH_HANDLED; 5944 } 5945 5946 // Deliver the key to the view hierarchy. 5947 if (mView.dispatchKeyEvent(event)) { 5948 return FINISH_HANDLED; 5949 } 5950 5951 if (shouldDropInputEvent(q)) { 5952 return FINISH_NOT_HANDLED; 5953 } 5954 5955 // This dispatch is for windows that don't have a Window.Callback. Otherwise, 5956 // the Window.Callback usually will have already called this (see 5957 // DecorView.superDispatchKeyEvent) leaving this call a no-op. 5958 if (mUnhandledKeyManager.dispatch(mView, event)) { 5959 return FINISH_HANDLED; 5960 } 5961 5962 int groupNavigationDirection = 0; 5963 5964 if (event.getAction() == KeyEvent.ACTION_DOWN 5965 && event.getKeyCode() == KeyEvent.KEYCODE_TAB) { 5966 if (KeyEvent.metaStateHasModifiers(event.getMetaState(), KeyEvent.META_META_ON)) { 5967 groupNavigationDirection = View.FOCUS_FORWARD; 5968 } else if (KeyEvent.metaStateHasModifiers(event.getMetaState(), 5969 KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON)) { 5970 groupNavigationDirection = View.FOCUS_BACKWARD; 5971 } 5972 } 5973 5974 // If a modifier is held, try to interpret the key as a shortcut. 5975 if (event.getAction() == KeyEvent.ACTION_DOWN 5976 && !KeyEvent.metaStateHasNoModifiers(event.getMetaState()) 5977 && event.getRepeatCount() == 0 5978 && !KeyEvent.isModifierKey(event.getKeyCode()) 5979 && groupNavigationDirection == 0) { 5980 if (mView.dispatchKeyShortcutEvent(event)) { 5981 return FINISH_HANDLED; 5982 } 5983 if (shouldDropInputEvent(q)) { 5984 return FINISH_NOT_HANDLED; 5985 } 5986 } 5987 5988 // Apply the fallback event policy. 5989 if (mFallbackEventHandler.dispatchKeyEvent(event)) { 5990 return FINISH_HANDLED; 5991 } 5992 if (shouldDropInputEvent(q)) { 5993 return FINISH_NOT_HANDLED; 5994 } 5995 5996 // Handle automatic focus changes. 5997 if (event.getAction() == KeyEvent.ACTION_DOWN) { 5998 if (groupNavigationDirection != 0) { 5999 if (performKeyboardGroupNavigation(groupNavigationDirection)) { 6000 return FINISH_HANDLED; 6001 } 6002 } else { 6003 if (performFocusNavigation(event)) { 6004 return FINISH_HANDLED; 6005 } 6006 } 6007 } 6008 return FORWARD; 6009 } 6010 processPointerEvent(QueuedInputEvent q)6011 private int processPointerEvent(QueuedInputEvent q) { 6012 final MotionEvent event = (MotionEvent)q.mEvent; 6013 6014 mAttachInfo.mUnbufferedDispatchRequested = false; 6015 mAttachInfo.mHandlingPointerEvent = true; 6016 boolean handled = mView.dispatchPointerEvent(event); 6017 maybeUpdatePointerIcon(event); 6018 maybeUpdateTooltip(event); 6019 mAttachInfo.mHandlingPointerEvent = false; 6020 if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { 6021 mUnbufferedInputDispatch = true; 6022 if (mConsumeBatchedInputScheduled) { 6023 scheduleConsumeBatchedInputImmediately(); 6024 } 6025 } 6026 return handled ? FINISH_HANDLED : FORWARD; 6027 } 6028 maybeUpdatePointerIcon(MotionEvent event)6029 private void maybeUpdatePointerIcon(MotionEvent event) { 6030 if (event.getPointerCount() == 1 && event.isFromSource(InputDevice.SOURCE_MOUSE)) { 6031 if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER 6032 || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { 6033 // Other apps or the window manager may change the icon type outside of 6034 // this app, therefore the icon type has to be reset on enter/exit event. 6035 mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; 6036 } 6037 6038 if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { 6039 if (!updatePointerIcon(event) && 6040 event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) { 6041 mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; 6042 } 6043 } 6044 } 6045 } 6046 processTrackballEvent(QueuedInputEvent q)6047 private int processTrackballEvent(QueuedInputEvent q) { 6048 final MotionEvent event = (MotionEvent)q.mEvent; 6049 6050 if (event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) { 6051 if (!hasPointerCapture() || mView.dispatchCapturedPointerEvent(event)) { 6052 return FINISH_HANDLED; 6053 } 6054 } 6055 6056 if (mView.dispatchTrackballEvent(event)) { 6057 return FINISH_HANDLED; 6058 } 6059 return FORWARD; 6060 } 6061 processGenericMotionEvent(QueuedInputEvent q)6062 private int processGenericMotionEvent(QueuedInputEvent q) { 6063 final MotionEvent event = (MotionEvent)q.mEvent; 6064 6065 if (event.isFromSource(InputDevice.SOURCE_TOUCHPAD)) { 6066 if (hasPointerCapture() && mView.dispatchCapturedPointerEvent(event)) { 6067 return FINISH_HANDLED; 6068 } 6069 } 6070 6071 // Deliver the event to the view. 6072 if (mView.dispatchGenericMotionEvent(event)) { 6073 return FINISH_HANDLED; 6074 } 6075 return FORWARD; 6076 } 6077 } 6078 resetPointerIcon(MotionEvent event)6079 private void resetPointerIcon(MotionEvent event) { 6080 mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; 6081 updatePointerIcon(event); 6082 } 6083 updatePointerIcon(MotionEvent event)6084 private boolean updatePointerIcon(MotionEvent event) { 6085 final int pointerIndex = 0; 6086 final float x = event.getX(pointerIndex); 6087 final float y = event.getY(pointerIndex); 6088 if (mView == null) { 6089 // E.g. click outside a popup to dismiss it 6090 Slog.d(mTag, "updatePointerIcon called after view was removed"); 6091 return false; 6092 } 6093 if (x < 0 || x >= mView.getWidth() || y < 0 || y >= mView.getHeight()) { 6094 // E.g. when moving window divider with mouse 6095 Slog.d(mTag, "updatePointerIcon called with position out of bounds"); 6096 return false; 6097 } 6098 final PointerIcon pointerIcon = mView.onResolvePointerIcon(event, pointerIndex); 6099 final int pointerType = (pointerIcon != null) ? 6100 pointerIcon.getType() : PointerIcon.TYPE_DEFAULT; 6101 6102 if (mPointerIconType != pointerType) { 6103 mPointerIconType = pointerType; 6104 mCustomPointerIcon = null; 6105 if (mPointerIconType != PointerIcon.TYPE_CUSTOM) { 6106 InputManager.getInstance().setPointerIconType(pointerType); 6107 return true; 6108 } 6109 } 6110 if (mPointerIconType == PointerIcon.TYPE_CUSTOM && 6111 !pointerIcon.equals(mCustomPointerIcon)) { 6112 mCustomPointerIcon = pointerIcon; 6113 InputManager.getInstance().setCustomPointerIcon(mCustomPointerIcon); 6114 } 6115 return true; 6116 } 6117 maybeUpdateTooltip(MotionEvent event)6118 private void maybeUpdateTooltip(MotionEvent event) { 6119 if (event.getPointerCount() != 1) { 6120 return; 6121 } 6122 final int action = event.getActionMasked(); 6123 if (action != MotionEvent.ACTION_HOVER_ENTER 6124 && action != MotionEvent.ACTION_HOVER_MOVE 6125 && action != MotionEvent.ACTION_HOVER_EXIT) { 6126 return; 6127 } 6128 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 6129 if (manager.isEnabled() && manager.isTouchExplorationEnabled()) { 6130 return; 6131 } 6132 if (mView == null) { 6133 Slog.d(mTag, "maybeUpdateTooltip called after view was removed"); 6134 return; 6135 } 6136 mView.dispatchTooltipHoverEvent(event); 6137 } 6138 6139 /** 6140 * Performs synthesis of new input events from unhandled input events. 6141 */ 6142 final class SyntheticInputStage extends InputStage { 6143 private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler(); 6144 private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler(); 6145 private final SyntheticTouchNavigationHandler mTouchNavigation = 6146 new SyntheticTouchNavigationHandler(); 6147 private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler(); 6148 SyntheticInputStage()6149 public SyntheticInputStage() { 6150 super(null); 6151 } 6152 6153 @Override onProcess(QueuedInputEvent q)6154 protected int onProcess(QueuedInputEvent q) { 6155 q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED; 6156 if (q.mEvent instanceof MotionEvent) { 6157 final MotionEvent event = (MotionEvent)q.mEvent; 6158 final int source = event.getSource(); 6159 if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 6160 mTrackball.process(event); 6161 return FINISH_HANDLED; 6162 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 6163 mJoystick.process(event); 6164 return FINISH_HANDLED; 6165 } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) 6166 == InputDevice.SOURCE_TOUCH_NAVIGATION) { 6167 mTouchNavigation.process(event); 6168 return FINISH_HANDLED; 6169 } 6170 } else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) { 6171 mKeyboard.process((KeyEvent)q.mEvent); 6172 return FINISH_HANDLED; 6173 } 6174 6175 return FORWARD; 6176 } 6177 6178 @Override onDeliverToNext(QueuedInputEvent q)6179 protected void onDeliverToNext(QueuedInputEvent q) { 6180 if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) { 6181 // Cancel related synthetic events if any prior stage has handled the event. 6182 if (q.mEvent instanceof MotionEvent) { 6183 final MotionEvent event = (MotionEvent)q.mEvent; 6184 final int source = event.getSource(); 6185 if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 6186 mTrackball.cancel(); 6187 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 6188 mJoystick.cancel(); 6189 } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) 6190 == InputDevice.SOURCE_TOUCH_NAVIGATION) { 6191 mTouchNavigation.cancel(event); 6192 } 6193 } 6194 } 6195 super.onDeliverToNext(q); 6196 } 6197 6198 @Override onWindowFocusChanged(boolean hasWindowFocus)6199 protected void onWindowFocusChanged(boolean hasWindowFocus) { 6200 if (!hasWindowFocus) { 6201 mJoystick.cancel(); 6202 } 6203 } 6204 6205 @Override onDetachedFromWindow()6206 protected void onDetachedFromWindow() { 6207 mJoystick.cancel(); 6208 } 6209 } 6210 6211 /** 6212 * Creates dpad events from unhandled trackball movements. 6213 */ 6214 final class SyntheticTrackballHandler { 6215 private final TrackballAxis mX = new TrackballAxis(); 6216 private final TrackballAxis mY = new TrackballAxis(); 6217 private long mLastTime; 6218 process(MotionEvent event)6219 public void process(MotionEvent event) { 6220 // Translate the trackball event into DPAD keys and try to deliver those. 6221 long curTime = SystemClock.uptimeMillis(); 6222 if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) { 6223 // It has been too long since the last movement, 6224 // so restart at the beginning. 6225 mX.reset(0); 6226 mY.reset(0); 6227 mLastTime = curTime; 6228 } 6229 6230 final int action = event.getAction(); 6231 final int metaState = event.getMetaState(); 6232 switch (action) { 6233 case MotionEvent.ACTION_DOWN: 6234 mX.reset(2); 6235 mY.reset(2); 6236 enqueueInputEvent(new KeyEvent(curTime, curTime, 6237 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 6238 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 6239 InputDevice.SOURCE_KEYBOARD)); 6240 break; 6241 case MotionEvent.ACTION_UP: 6242 mX.reset(2); 6243 mY.reset(2); 6244 enqueueInputEvent(new KeyEvent(curTime, curTime, 6245 KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 6246 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 6247 InputDevice.SOURCE_KEYBOARD)); 6248 break; 6249 } 6250 6251 if (DEBUG_TRACKBALL) Log.v(mTag, "TB X=" + mX.position + " step=" 6252 + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration 6253 + " move=" + event.getX() 6254 + " / Y=" + mY.position + " step=" 6255 + mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration 6256 + " move=" + event.getY()); 6257 final float xOff = mX.collect(event.getX(), event.getEventTime(), "X"); 6258 final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y"); 6259 6260 // Generate DPAD events based on the trackball movement. 6261 // We pick the axis that has moved the most as the direction of 6262 // the DPAD. When we generate DPAD events for one axis, then the 6263 // other axis is reset -- we don't want to perform DPAD jumps due 6264 // to slight movements in the trackball when making major movements 6265 // along the other axis. 6266 int keycode = 0; 6267 int movement = 0; 6268 float accel = 1; 6269 if (xOff > yOff) { 6270 movement = mX.generate(); 6271 if (movement != 0) { 6272 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT 6273 : KeyEvent.KEYCODE_DPAD_LEFT; 6274 accel = mX.acceleration; 6275 mY.reset(2); 6276 } 6277 } else if (yOff > 0) { 6278 movement = mY.generate(); 6279 if (movement != 0) { 6280 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN 6281 : KeyEvent.KEYCODE_DPAD_UP; 6282 accel = mY.acceleration; 6283 mX.reset(2); 6284 } 6285 } 6286 6287 if (keycode != 0) { 6288 if (movement < 0) movement = -movement; 6289 int accelMovement = (int)(movement * accel); 6290 if (DEBUG_TRACKBALL) Log.v(mTag, "Move: movement=" + movement 6291 + " accelMovement=" + accelMovement 6292 + " accel=" + accel); 6293 if (accelMovement > movement) { 6294 if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: " 6295 + keycode); 6296 movement--; 6297 int repeatCount = accelMovement - movement; 6298 enqueueInputEvent(new KeyEvent(curTime, curTime, 6299 KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState, 6300 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 6301 InputDevice.SOURCE_KEYBOARD)); 6302 } 6303 while (movement > 0) { 6304 if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: " 6305 + keycode); 6306 movement--; 6307 curTime = SystemClock.uptimeMillis(); 6308 enqueueInputEvent(new KeyEvent(curTime, curTime, 6309 KeyEvent.ACTION_DOWN, keycode, 0, metaState, 6310 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 6311 InputDevice.SOURCE_KEYBOARD)); 6312 enqueueInputEvent(new KeyEvent(curTime, curTime, 6313 KeyEvent.ACTION_UP, keycode, 0, metaState, 6314 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 6315 InputDevice.SOURCE_KEYBOARD)); 6316 } 6317 mLastTime = curTime; 6318 } 6319 } 6320 cancel()6321 public void cancel() { 6322 mLastTime = Integer.MIN_VALUE; 6323 6324 // If we reach this, we consumed a trackball event. 6325 // Because we will not translate the trackball event into a key event, 6326 // touch mode will not exit, so we exit touch mode here. 6327 if (mView != null && mAdded) { 6328 ensureTouchMode(false); 6329 } 6330 } 6331 } 6332 6333 /** 6334 * Maintains state information for a single trackball axis, generating 6335 * discrete (DPAD) movements based on raw trackball motion. 6336 */ 6337 static final class TrackballAxis { 6338 /** 6339 * The maximum amount of acceleration we will apply. 6340 */ 6341 static final float MAX_ACCELERATION = 20; 6342 6343 /** 6344 * The maximum amount of time (in milliseconds) between events in order 6345 * for us to consider the user to be doing fast trackball movements, 6346 * and thus apply an acceleration. 6347 */ 6348 static final long FAST_MOVE_TIME = 150; 6349 6350 /** 6351 * Scaling factor to the time (in milliseconds) between events to how 6352 * much to multiple/divide the current acceleration. When movement 6353 * is < FAST_MOVE_TIME this multiplies the acceleration; when > 6354 * FAST_MOVE_TIME it divides it. 6355 */ 6356 static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); 6357 6358 static final float FIRST_MOVEMENT_THRESHOLD = 0.5f; 6359 static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f; 6360 static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f; 6361 6362 float position; 6363 float acceleration = 1; 6364 long lastMoveTime = 0; 6365 int step; 6366 int dir; 6367 int nonAccelMovement; 6368 reset(int _step)6369 void reset(int _step) { 6370 position = 0; 6371 acceleration = 1; 6372 lastMoveTime = 0; 6373 step = _step; 6374 dir = 0; 6375 } 6376 6377 /** 6378 * Add trackball movement into the state. If the direction of movement 6379 * has been reversed, the state is reset before adding the 6380 * movement (so that you don't have to compensate for any previously 6381 * collected movement before see the result of the movement in the 6382 * new direction). 6383 * 6384 * @return Returns the absolute value of the amount of movement 6385 * collected so far. 6386 */ collect(float off, long time, String axis)6387 float collect(float off, long time, String axis) { 6388 long normTime; 6389 if (off > 0) { 6390 normTime = (long)(off * FAST_MOVE_TIME); 6391 if (dir < 0) { 6392 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!"); 6393 position = 0; 6394 step = 0; 6395 acceleration = 1; 6396 lastMoveTime = 0; 6397 } 6398 dir = 1; 6399 } else if (off < 0) { 6400 normTime = (long)((-off) * FAST_MOVE_TIME); 6401 if (dir > 0) { 6402 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!"); 6403 position = 0; 6404 step = 0; 6405 acceleration = 1; 6406 lastMoveTime = 0; 6407 } 6408 dir = -1; 6409 } else { 6410 normTime = 0; 6411 } 6412 6413 // The number of milliseconds between each movement that is 6414 // considered "normal" and will not result in any acceleration 6415 // or deceleration, scaled by the offset we have here. 6416 if (normTime > 0) { 6417 long delta = time - lastMoveTime; 6418 lastMoveTime = time; 6419 float acc = acceleration; 6420 if (delta < normTime) { 6421 // The user is scrolling rapidly, so increase acceleration. 6422 float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR; 6423 if (scale > 1) acc *= scale; 6424 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off=" 6425 + off + " normTime=" + normTime + " delta=" + delta 6426 + " scale=" + scale + " acc=" + acc); 6427 acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION; 6428 } else { 6429 // The user is scrolling slowly, so decrease acceleration. 6430 float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR; 6431 if (scale > 1) acc /= scale; 6432 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off=" 6433 + off + " normTime=" + normTime + " delta=" + delta 6434 + " scale=" + scale + " acc=" + acc); 6435 acceleration = acc > 1 ? acc : 1; 6436 } 6437 } 6438 position += off; 6439 return Math.abs(position); 6440 } 6441 6442 /** 6443 * Generate the number of discrete movement events appropriate for 6444 * the currently collected trackball movement. 6445 * 6446 * @return Returns the number of discrete movements, either positive 6447 * or negative, or 0 if there is not enough trackball movement yet 6448 * for a discrete movement. 6449 */ generate()6450 int generate() { 6451 int movement = 0; 6452 nonAccelMovement = 0; 6453 do { 6454 final int dir = position >= 0 ? 1 : -1; 6455 switch (step) { 6456 // If we are going to execute the first step, then we want 6457 // to do this as soon as possible instead of waiting for 6458 // a full movement, in order to make things look responsive. 6459 case 0: 6460 if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) { 6461 return movement; 6462 } 6463 movement += dir; 6464 nonAccelMovement += dir; 6465 step = 1; 6466 break; 6467 // If we have generated the first movement, then we need 6468 // to wait for the second complete trackball motion before 6469 // generating the second discrete movement. 6470 case 1: 6471 if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) { 6472 return movement; 6473 } 6474 movement += dir; 6475 nonAccelMovement += dir; 6476 position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir; 6477 step = 2; 6478 break; 6479 // After the first two, we generate discrete movements 6480 // consistently with the trackball, applying an acceleration 6481 // if the trackball is moving quickly. This is a simple 6482 // acceleration on top of what we already compute based 6483 // on how quickly the wheel is being turned, to apply 6484 // a longer increasing acceleration to continuous movement 6485 // in one direction. 6486 default: 6487 if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) { 6488 return movement; 6489 } 6490 movement += dir; 6491 position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD; 6492 float acc = acceleration; 6493 acc *= 1.1f; 6494 acceleration = acc < MAX_ACCELERATION ? acc : acceleration; 6495 break; 6496 } 6497 } while (true); 6498 } 6499 } 6500 6501 /** 6502 * Creates dpad events from unhandled joystick movements. 6503 */ 6504 final class SyntheticJoystickHandler extends Handler { 6505 private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1; 6506 private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2; 6507 6508 private final JoystickAxesState mJoystickAxesState = new JoystickAxesState(); 6509 private final SparseArray<KeyEvent> mDeviceKeyEvents = new SparseArray<>(); 6510 6511 public SyntheticJoystickHandler() { 6512 super(true); 6513 } 6514 6515 @Override 6516 public void handleMessage(Message msg) { 6517 switch (msg.what) { 6518 case MSG_ENQUEUE_X_AXIS_KEY_REPEAT: 6519 case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: { 6520 if (mAttachInfo.mHasWindowFocus) { 6521 KeyEvent oldEvent = (KeyEvent) msg.obj; 6522 KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, 6523 SystemClock.uptimeMillis(), oldEvent.getRepeatCount() + 1); 6524 enqueueInputEvent(e); 6525 Message m = obtainMessage(msg.what, e); 6526 m.setAsynchronous(true); 6527 sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay()); 6528 } 6529 } break; 6530 } 6531 } 6532 6533 public void process(MotionEvent event) { 6534 switch(event.getActionMasked()) { 6535 case MotionEvent.ACTION_CANCEL: 6536 cancel(); 6537 break; 6538 case MotionEvent.ACTION_MOVE: 6539 update(event); 6540 break; 6541 default: 6542 Log.w(mTag, "Unexpected action: " + event.getActionMasked()); 6543 } 6544 } 6545 6546 private void cancel() { 6547 removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT); 6548 removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT); 6549 for (int i = 0; i < mDeviceKeyEvents.size(); i++) { 6550 final KeyEvent keyEvent = mDeviceKeyEvents.valueAt(i); 6551 if (keyEvent != null) { 6552 enqueueInputEvent(KeyEvent.changeTimeRepeat(keyEvent, 6553 SystemClock.uptimeMillis(), 0)); 6554 } 6555 } 6556 mDeviceKeyEvents.clear(); 6557 mJoystickAxesState.resetState(); 6558 } 6559 6560 private void update(MotionEvent event) { 6561 final int historySize = event.getHistorySize(); 6562 for (int h = 0; h < historySize; h++) { 6563 final long time = event.getHistoricalEventTime(h); 6564 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X, 6565 event.getHistoricalAxisValue(MotionEvent.AXIS_X, 0, h)); 6566 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y, 6567 event.getHistoricalAxisValue(MotionEvent.AXIS_Y, 0, h)); 6568 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X, 6569 event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_X, 0, h)); 6570 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y, 6571 event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_Y, 0, h)); 6572 } 6573 final long time = event.getEventTime(); 6574 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X, 6575 event.getAxisValue(MotionEvent.AXIS_X)); 6576 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y, 6577 event.getAxisValue(MotionEvent.AXIS_Y)); 6578 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X, 6579 event.getAxisValue(MotionEvent.AXIS_HAT_X)); 6580 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y, 6581 event.getAxisValue(MotionEvent.AXIS_HAT_Y)); 6582 } 6583 6584 final class JoystickAxesState { 6585 // State machine: from neutral state (no button press) can go into 6586 // button STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state, emitting an ACTION_DOWN event. 6587 // From STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state can go into neutral state, 6588 // emitting an ACTION_UP event. 6589 private static final int STATE_UP_OR_LEFT = -1; 6590 private static final int STATE_NEUTRAL = 0; 6591 private static final int STATE_DOWN_OR_RIGHT = 1; 6592 6593 final int[] mAxisStatesHat = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_HAT_X, AXIS_HAT_Y} 6594 final int[] mAxisStatesStick = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_X, AXIS_Y} 6595 6596 void resetState() { 6597 mAxisStatesHat[0] = STATE_NEUTRAL; 6598 mAxisStatesHat[1] = STATE_NEUTRAL; 6599 mAxisStatesStick[0] = STATE_NEUTRAL; 6600 mAxisStatesStick[1] = STATE_NEUTRAL; 6601 } 6602 6603 void updateStateForAxis(MotionEvent event, long time, int axis, float value) { 6604 // Emit KeyEvent if necessary 6605 // axis can be AXIS_X, AXIS_Y, AXIS_HAT_X, AXIS_HAT_Y 6606 final int axisStateIndex; 6607 final int repeatMessage; 6608 if (isXAxis(axis)) { 6609 axisStateIndex = 0; 6610 repeatMessage = MSG_ENQUEUE_X_AXIS_KEY_REPEAT; 6611 } else if (isYAxis(axis)) { 6612 axisStateIndex = 1; 6613 repeatMessage = MSG_ENQUEUE_Y_AXIS_KEY_REPEAT; 6614 } else { 6615 Log.e(mTag, "Unexpected axis " + axis + " in updateStateForAxis!"); 6616 return; 6617 } 6618 final int newState = joystickAxisValueToState(value); 6619 6620 final int currentState; 6621 if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) { 6622 currentState = mAxisStatesStick[axisStateIndex]; 6623 } else { 6624 currentState = mAxisStatesHat[axisStateIndex]; 6625 } 6626 6627 if (currentState == newState) { 6628 return; 6629 } 6630 6631 final int metaState = event.getMetaState(); 6632 final int deviceId = event.getDeviceId(); 6633 final int source = event.getSource(); 6634 6635 if (currentState == STATE_DOWN_OR_RIGHT || currentState == STATE_UP_OR_LEFT) { 6636 // send a button release event 6637 final int keyCode = joystickAxisAndStateToKeycode(axis, currentState); 6638 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 6639 enqueueInputEvent(new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode, 6640 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source)); 6641 // remove the corresponding pending UP event if focus lost/view detached 6642 mDeviceKeyEvents.put(deviceId, null); 6643 } 6644 removeMessages(repeatMessage); 6645 } 6646 6647 if (newState == STATE_DOWN_OR_RIGHT || newState == STATE_UP_OR_LEFT) { 6648 // send a button down event 6649 final int keyCode = joystickAxisAndStateToKeycode(axis, newState); 6650 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 6651 KeyEvent keyEvent = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode, 6652 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source); 6653 enqueueInputEvent(keyEvent); 6654 Message m = obtainMessage(repeatMessage, keyEvent); 6655 m.setAsynchronous(true); 6656 sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout()); 6657 // store the corresponding ACTION_UP event so that it can be sent 6658 // if focus is lost or root view is removed 6659 mDeviceKeyEvents.put(deviceId, 6660 new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode, 6661 0, metaState, deviceId, 0, 6662 KeyEvent.FLAG_FALLBACK | KeyEvent.FLAG_CANCELED, 6663 source)); 6664 } 6665 } 6666 if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) { 6667 mAxisStatesStick[axisStateIndex] = newState; 6668 } else { 6669 mAxisStatesHat[axisStateIndex] = newState; 6670 } 6671 } 6672 6673 private boolean isXAxis(int axis) { 6674 return axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_HAT_X; 6675 } 6676 private boolean isYAxis(int axis) { 6677 return axis == MotionEvent.AXIS_Y || axis == MotionEvent.AXIS_HAT_Y; 6678 } 6679 6680 private int joystickAxisAndStateToKeycode(int axis, int state) { 6681 if (isXAxis(axis) && state == STATE_UP_OR_LEFT) { 6682 return KeyEvent.KEYCODE_DPAD_LEFT; 6683 } 6684 if (isXAxis(axis) && state == STATE_DOWN_OR_RIGHT) { 6685 return KeyEvent.KEYCODE_DPAD_RIGHT; 6686 } 6687 if (isYAxis(axis) && state == STATE_UP_OR_LEFT) { 6688 return KeyEvent.KEYCODE_DPAD_UP; 6689 } 6690 if (isYAxis(axis) && state == STATE_DOWN_OR_RIGHT) { 6691 return KeyEvent.KEYCODE_DPAD_DOWN; 6692 } 6693 Log.e(mTag, "Unknown axis " + axis + " or direction " + state); 6694 return KeyEvent.KEYCODE_UNKNOWN; // should never happen 6695 } 6696 6697 private int joystickAxisValueToState(float value) { 6698 if (value >= 0.5f) { 6699 return STATE_DOWN_OR_RIGHT; 6700 } else if (value <= -0.5f) { 6701 return STATE_UP_OR_LEFT; 6702 } else { 6703 return STATE_NEUTRAL; 6704 } 6705 } 6706 } 6707 } 6708 6709 /** 6710 * Creates dpad events from unhandled touch navigation movements. 6711 */ 6712 final class SyntheticTouchNavigationHandler extends Handler { 6713 private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler"; 6714 private static final boolean LOCAL_DEBUG = false; 6715 6716 // Assumed nominal width and height in millimeters of a touch navigation pad, 6717 // if no resolution information is available from the input system. 6718 private static final float DEFAULT_WIDTH_MILLIMETERS = 48; 6719 private static final float DEFAULT_HEIGHT_MILLIMETERS = 48; 6720 6721 /* TODO: These constants should eventually be moved to ViewConfiguration. */ 6722 6723 // The nominal distance traveled to move by one unit. 6724 private static final int TICK_DISTANCE_MILLIMETERS = 12; 6725 6726 // Minimum and maximum fling velocity in ticks per second. 6727 // The minimum velocity should be set such that we perform enough ticks per 6728 // second that the fling appears to be fluid. For example, if we set the minimum 6729 // to 2 ticks per second, then there may be up to half a second delay between the next 6730 // to last and last ticks which is noticeably discrete and jerky. This value should 6731 // probably not be set to anything less than about 4. 6732 // If fling accuracy is a problem then consider tuning the tick distance instead. 6733 private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f; 6734 private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f; 6735 6736 // Fling velocity decay factor applied after each new key is emitted. 6737 // This parameter controls the deceleration and overall duration of the fling. 6738 // The fling stops automatically when its velocity drops below the minimum 6739 // fling velocity defined above. 6740 private static final float FLING_TICK_DECAY = 0.8f; 6741 6742 /* The input device that we are tracking. */ 6743 6744 private int mCurrentDeviceId = -1; 6745 private int mCurrentSource; 6746 private boolean mCurrentDeviceSupported; 6747 6748 /* Configuration for the current input device. */ 6749 6750 // The scaled tick distance. A movement of this amount should generally translate 6751 // into a single dpad event in a given direction. 6752 private float mConfigTickDistance; 6753 6754 // The minimum and maximum scaled fling velocity. 6755 private float mConfigMinFlingVelocity; 6756 private float mConfigMaxFlingVelocity; 6757 6758 /* Tracking state. */ 6759 6760 // The velocity tracker for detecting flings. 6761 private VelocityTracker mVelocityTracker; 6762 6763 // The active pointer id, or -1 if none. 6764 private int mActivePointerId = -1; 6765 6766 // Location where tracking started. 6767 private float mStartX; 6768 private float mStartY; 6769 6770 // Most recently observed position. 6771 private float mLastX; 6772 private float mLastY; 6773 6774 // Accumulated movement delta since the last direction key was sent. 6775 private float mAccumulatedX; 6776 private float mAccumulatedY; 6777 6778 // Set to true if any movement was delivered to the app. 6779 // Implies that tap slop was exceeded. 6780 private boolean mConsumedMovement; 6781 6782 // The most recently sent key down event. 6783 // The keycode remains set until the direction changes or a fling ends 6784 // so that repeated key events may be generated as required. 6785 private long mPendingKeyDownTime; 6786 private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; 6787 private int mPendingKeyRepeatCount; 6788 private int mPendingKeyMetaState; 6789 6790 // The current fling velocity while a fling is in progress. 6791 private boolean mFlinging; 6792 private float mFlingVelocity; 6793 6794 public SyntheticTouchNavigationHandler() { 6795 super(true); 6796 } 6797 6798 public void process(MotionEvent event) { 6799 // Update the current device information. 6800 final long time = event.getEventTime(); 6801 final int deviceId = event.getDeviceId(); 6802 final int source = event.getSource(); 6803 if (mCurrentDeviceId != deviceId || mCurrentSource != source) { 6804 finishKeys(time); 6805 finishTracking(time); 6806 mCurrentDeviceId = deviceId; 6807 mCurrentSource = source; 6808 mCurrentDeviceSupported = false; 6809 InputDevice device = event.getDevice(); 6810 if (device != null) { 6811 // In order to support an input device, we must know certain 6812 // characteristics about it, such as its size and resolution. 6813 InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X); 6814 InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y); 6815 if (xRange != null && yRange != null) { 6816 mCurrentDeviceSupported = true; 6817 6818 // Infer the resolution if it not actually known. 6819 float xRes = xRange.getResolution(); 6820 if (xRes <= 0) { 6821 xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS; 6822 } 6823 float yRes = yRange.getResolution(); 6824 if (yRes <= 0) { 6825 yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS; 6826 } 6827 float nominalRes = (xRes + yRes) * 0.5f; 6828 6829 // Precompute all of the configuration thresholds we will need. 6830 mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes; 6831 mConfigMinFlingVelocity = 6832 MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; 6833 mConfigMaxFlingVelocity = 6834 MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; 6835 6836 if (LOCAL_DEBUG) { 6837 Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId 6838 + " (" + Integer.toHexString(mCurrentSource) + "): " 6839 + ", mConfigTickDistance=" + mConfigTickDistance 6840 + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity 6841 + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity); 6842 } 6843 } 6844 } 6845 } 6846 if (!mCurrentDeviceSupported) { 6847 return; 6848 } 6849 6850 // Handle the event. 6851 final int action = event.getActionMasked(); 6852 switch (action) { 6853 case MotionEvent.ACTION_DOWN: { 6854 boolean caughtFling = mFlinging; 6855 finishKeys(time); 6856 finishTracking(time); 6857 mActivePointerId = event.getPointerId(0); 6858 mVelocityTracker = VelocityTracker.obtain(); 6859 mVelocityTracker.addMovement(event); 6860 mStartX = event.getX(); 6861 mStartY = event.getY(); 6862 mLastX = mStartX; 6863 mLastY = mStartY; 6864 mAccumulatedX = 0; 6865 mAccumulatedY = 0; 6866 6867 // If we caught a fling, then pretend that the tap slop has already 6868 // been exceeded to suppress taps whose only purpose is to stop the fling. 6869 mConsumedMovement = caughtFling; 6870 break; 6871 } 6872 6873 case MotionEvent.ACTION_MOVE: 6874 case MotionEvent.ACTION_UP: { 6875 if (mActivePointerId < 0) { 6876 break; 6877 } 6878 final int index = event.findPointerIndex(mActivePointerId); 6879 if (index < 0) { 6880 finishKeys(time); 6881 finishTracking(time); 6882 break; 6883 } 6884 6885 mVelocityTracker.addMovement(event); 6886 final float x = event.getX(index); 6887 final float y = event.getY(index); 6888 mAccumulatedX += x - mLastX; 6889 mAccumulatedY += y - mLastY; 6890 mLastX = x; 6891 mLastY = y; 6892 6893 // Consume any accumulated movement so far. 6894 final int metaState = event.getMetaState(); 6895 consumeAccumulatedMovement(time, metaState); 6896 6897 // Detect taps and flings. 6898 if (action == MotionEvent.ACTION_UP) { 6899 if (mConsumedMovement && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { 6900 // It might be a fling. 6901 mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity); 6902 final float vx = mVelocityTracker.getXVelocity(mActivePointerId); 6903 final float vy = mVelocityTracker.getYVelocity(mActivePointerId); 6904 if (!startFling(time, vx, vy)) { 6905 finishKeys(time); 6906 } 6907 } 6908 finishTracking(time); 6909 } 6910 break; 6911 } 6912 6913 case MotionEvent.ACTION_CANCEL: { 6914 finishKeys(time); 6915 finishTracking(time); 6916 break; 6917 } 6918 } 6919 } 6920 6921 public void cancel(MotionEvent event) { 6922 if (mCurrentDeviceId == event.getDeviceId() 6923 && mCurrentSource == event.getSource()) { 6924 final long time = event.getEventTime(); 6925 finishKeys(time); 6926 finishTracking(time); 6927 } 6928 } 6929 6930 private void finishKeys(long time) { 6931 cancelFling(); 6932 sendKeyUp(time); 6933 } 6934 6935 private void finishTracking(long time) { 6936 if (mActivePointerId >= 0) { 6937 mActivePointerId = -1; 6938 mVelocityTracker.recycle(); 6939 mVelocityTracker = null; 6940 } 6941 } 6942 6943 private void consumeAccumulatedMovement(long time, int metaState) { 6944 final float absX = Math.abs(mAccumulatedX); 6945 final float absY = Math.abs(mAccumulatedY); 6946 if (absX >= absY) { 6947 if (absX >= mConfigTickDistance) { 6948 mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX, 6949 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT); 6950 mAccumulatedY = 0; 6951 mConsumedMovement = true; 6952 } 6953 } else { 6954 if (absY >= mConfigTickDistance) { 6955 mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY, 6956 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN); 6957 mAccumulatedX = 0; 6958 mConsumedMovement = true; 6959 } 6960 } 6961 } 6962 6963 private float consumeAccumulatedMovement(long time, int metaState, 6964 float accumulator, int negativeKeyCode, int positiveKeyCode) { 6965 while (accumulator <= -mConfigTickDistance) { 6966 sendKeyDownOrRepeat(time, negativeKeyCode, metaState); 6967 accumulator += mConfigTickDistance; 6968 } 6969 while (accumulator >= mConfigTickDistance) { 6970 sendKeyDownOrRepeat(time, positiveKeyCode, metaState); 6971 accumulator -= mConfigTickDistance; 6972 } 6973 return accumulator; 6974 } 6975 6976 private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) { 6977 if (mPendingKeyCode != keyCode) { 6978 sendKeyUp(time); 6979 mPendingKeyDownTime = time; 6980 mPendingKeyCode = keyCode; 6981 mPendingKeyRepeatCount = 0; 6982 } else { 6983 mPendingKeyRepeatCount += 1; 6984 } 6985 mPendingKeyMetaState = metaState; 6986 6987 // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1 6988 // but it doesn't quite make sense when simulating the events in this way. 6989 if (LOCAL_DEBUG) { 6990 Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode 6991 + ", repeatCount=" + mPendingKeyRepeatCount 6992 + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); 6993 } 6994 enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, 6995 KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount, 6996 mPendingKeyMetaState, mCurrentDeviceId, 6997 KeyEvent.FLAG_FALLBACK, mCurrentSource)); 6998 } 6999 7000 private void sendKeyUp(long time) { 7001 if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { 7002 if (LOCAL_DEBUG) { 7003 Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode 7004 + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); 7005 } 7006 enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, 7007 KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState, 7008 mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK, 7009 mCurrentSource)); 7010 mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; 7011 } 7012 } 7013 7014 private boolean startFling(long time, float vx, float vy) { 7015 if (LOCAL_DEBUG) { 7016 Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy 7017 + ", min=" + mConfigMinFlingVelocity); 7018 } 7019 7020 // Flings must be oriented in the same direction as the preceding movements. 7021 switch (mPendingKeyCode) { 7022 case KeyEvent.KEYCODE_DPAD_LEFT: 7023 if (-vx >= mConfigMinFlingVelocity 7024 && Math.abs(vy) < mConfigMinFlingVelocity) { 7025 mFlingVelocity = -vx; 7026 break; 7027 } 7028 return false; 7029 7030 case KeyEvent.KEYCODE_DPAD_RIGHT: 7031 if (vx >= mConfigMinFlingVelocity 7032 && Math.abs(vy) < mConfigMinFlingVelocity) { 7033 mFlingVelocity = vx; 7034 break; 7035 } 7036 return false; 7037 7038 case KeyEvent.KEYCODE_DPAD_UP: 7039 if (-vy >= mConfigMinFlingVelocity 7040 && Math.abs(vx) < mConfigMinFlingVelocity) { 7041 mFlingVelocity = -vy; 7042 break; 7043 } 7044 return false; 7045 7046 case KeyEvent.KEYCODE_DPAD_DOWN: 7047 if (vy >= mConfigMinFlingVelocity 7048 && Math.abs(vx) < mConfigMinFlingVelocity) { 7049 mFlingVelocity = vy; 7050 break; 7051 } 7052 return false; 7053 } 7054 7055 // Post the first fling event. 7056 mFlinging = postFling(time); 7057 return mFlinging; 7058 } 7059 7060 private boolean postFling(long time) { 7061 // The idea here is to estimate the time when the pointer would have 7062 // traveled one tick distance unit given the current fling velocity. 7063 // This effect creates continuity of motion. 7064 if (mFlingVelocity >= mConfigMinFlingVelocity) { 7065 long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000); 7066 postAtTime(mFlingRunnable, time + delay); 7067 if (LOCAL_DEBUG) { 7068 Log.d(LOCAL_TAG, "Posted fling: velocity=" 7069 + mFlingVelocity + ", delay=" + delay 7070 + ", keyCode=" + mPendingKeyCode); 7071 } 7072 return true; 7073 } 7074 return false; 7075 } 7076 7077 private void cancelFling() { 7078 if (mFlinging) { 7079 removeCallbacks(mFlingRunnable); 7080 mFlinging = false; 7081 } 7082 } 7083 7084 private final Runnable mFlingRunnable = new Runnable() { 7085 @Override 7086 public void run() { 7087 final long time = SystemClock.uptimeMillis(); 7088 sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState); 7089 mFlingVelocity *= FLING_TICK_DECAY; 7090 if (!postFling(time)) { 7091 mFlinging = false; 7092 finishKeys(time); 7093 } 7094 } 7095 }; 7096 } 7097 7098 final class SyntheticKeyboardHandler { 7099 public void process(KeyEvent event) { 7100 if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) { 7101 return; 7102 } 7103 7104 final KeyCharacterMap kcm = event.getKeyCharacterMap(); 7105 final int keyCode = event.getKeyCode(); 7106 final int metaState = event.getMetaState(); 7107 7108 // Check for fallback actions specified by the key character map. 7109 KeyCharacterMap.FallbackAction fallbackAction = 7110 kcm.getFallbackAction(keyCode, metaState); 7111 if (fallbackAction != null) { 7112 final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK; 7113 KeyEvent fallbackEvent = KeyEvent.obtain( 7114 event.getDownTime(), event.getEventTime(), 7115 event.getAction(), fallbackAction.keyCode, 7116 event.getRepeatCount(), fallbackAction.metaState, 7117 event.getDeviceId(), event.getScanCode(), 7118 flags, event.getSource(), null); 7119 fallbackAction.recycle(); 7120 enqueueInputEvent(fallbackEvent); 7121 } 7122 } 7123 } 7124 7125 /** 7126 * Returns true if the key is used for keyboard navigation. 7127 * @param keyEvent The key event. 7128 * @return True if the key is used for keyboard navigation. 7129 */ 7130 private static boolean isNavigationKey(KeyEvent keyEvent) { 7131 switch (keyEvent.getKeyCode()) { 7132 case KeyEvent.KEYCODE_DPAD_LEFT: 7133 case KeyEvent.KEYCODE_DPAD_RIGHT: 7134 case KeyEvent.KEYCODE_DPAD_UP: 7135 case KeyEvent.KEYCODE_DPAD_DOWN: 7136 case KeyEvent.KEYCODE_DPAD_CENTER: 7137 case KeyEvent.KEYCODE_PAGE_UP: 7138 case KeyEvent.KEYCODE_PAGE_DOWN: 7139 case KeyEvent.KEYCODE_MOVE_HOME: 7140 case KeyEvent.KEYCODE_MOVE_END: 7141 case KeyEvent.KEYCODE_TAB: 7142 case KeyEvent.KEYCODE_SPACE: 7143 case KeyEvent.KEYCODE_ENTER: 7144 return true; 7145 } 7146 return false; 7147 } 7148 7149 /** 7150 * Returns true if the key is used for typing. 7151 * @param keyEvent The key event. 7152 * @return True if the key is used for typing. 7153 */ 7154 private static boolean isTypingKey(KeyEvent keyEvent) { 7155 return keyEvent.getUnicodeChar() > 0; 7156 } 7157 7158 /** 7159 * See if the key event means we should leave touch mode (and leave touch mode if so). 7160 * @param event The key event. 7161 * @return Whether this key event should be consumed (meaning the act of 7162 * leaving touch mode alone is considered the event). 7163 */ 7164 private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { 7165 // Only relevant in touch mode. 7166 if (!mAttachInfo.mInTouchMode) { 7167 return false; 7168 } 7169 7170 // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. 7171 final int action = event.getAction(); 7172 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { 7173 return false; 7174 } 7175 7176 // Don't leave touch mode if the IME told us not to. 7177 if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { 7178 return false; 7179 } 7180 7181 // If the key can be used for keyboard navigation then leave touch mode 7182 // and select a focused view if needed (in ensureTouchMode). 7183 // When a new focused view is selected, we consume the navigation key because 7184 // navigation doesn't make much sense unless a view already has focus so 7185 // the key's purpose is to set focus. 7186 if (isNavigationKey(event)) { 7187 return ensureTouchMode(false); 7188 } 7189 7190 // If the key can be used for typing then leave touch mode 7191 // and select a focused view if needed (in ensureTouchMode). 7192 // Always allow the view to process the typing key. 7193 if (isTypingKey(event)) { 7194 ensureTouchMode(false); 7195 return false; 7196 } 7197 7198 return false; 7199 } 7200 7201 /* drag/drop */ 7202 @UnsupportedAppUsage 7203 void setLocalDragState(Object obj) { 7204 mLocalDragState = obj; 7205 } 7206 7207 private void handleDragEvent(DragEvent event) { 7208 // From the root, only drag start/end/location are dispatched. entered/exited 7209 // are determined and dispatched by the viewgroup hierarchy, who then report 7210 // that back here for ultimate reporting back to the framework. 7211 if (mView != null && mAdded) { 7212 final int what = event.mAction; 7213 7214 // Cache the drag description when the operation starts, then fill it in 7215 // on subsequent calls as a convenience 7216 if (what == DragEvent.ACTION_DRAG_STARTED) { 7217 mCurrentDragView = null; // Start the current-recipient tracking 7218 mDragDescription = event.mClipDescription; 7219 } else { 7220 if (what == DragEvent.ACTION_DRAG_ENDED) { 7221 mDragDescription = null; 7222 } 7223 event.mClipDescription = mDragDescription; 7224 } 7225 7226 if (what == DragEvent.ACTION_DRAG_EXITED) { 7227 // A direct EXITED event means that the window manager knows we've just crossed 7228 // a window boundary, so the current drag target within this one must have 7229 // just been exited. Send the EXITED notification to the current drag view, if any. 7230 if (View.sCascadedDragDrop) { 7231 mView.dispatchDragEnterExitInPreN(event); 7232 } 7233 setDragFocus(null, event); 7234 } else { 7235 // For events with a [screen] location, translate into window coordinates 7236 if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { 7237 mDragPoint.set(event.mX, event.mY); 7238 if (mTranslator != null) { 7239 mTranslator.translatePointInScreenToAppWindow(mDragPoint); 7240 } 7241 7242 if (mCurScrollY != 0) { 7243 mDragPoint.offset(0, mCurScrollY); 7244 } 7245 7246 event.mX = mDragPoint.x; 7247 event.mY = mDragPoint.y; 7248 } 7249 7250 // Remember who the current drag target is pre-dispatch 7251 final View prevDragView = mCurrentDragView; 7252 7253 if (what == DragEvent.ACTION_DROP && event.mClipData != null) { 7254 event.mClipData.prepareToEnterProcess(); 7255 } 7256 7257 // Now dispatch the drag/drop event 7258 boolean result = mView.dispatchDragEvent(event); 7259 7260 if (what == DragEvent.ACTION_DRAG_LOCATION && !event.mEventHandlerWasCalled) { 7261 // If the LOCATION event wasn't delivered to any handler, no view now has a drag 7262 // focus. 7263 setDragFocus(null, event); 7264 } 7265 7266 // If we changed apparent drag target, tell the OS about it 7267 if (prevDragView != mCurrentDragView) { 7268 try { 7269 if (prevDragView != null) { 7270 mWindowSession.dragRecipientExited(mWindow); 7271 } 7272 if (mCurrentDragView != null) { 7273 mWindowSession.dragRecipientEntered(mWindow); 7274 } 7275 } catch (RemoteException e) { 7276 Slog.e(mTag, "Unable to note drag target change"); 7277 } 7278 } 7279 7280 // Report the drop result when we're done 7281 if (what == DragEvent.ACTION_DROP) { 7282 try { 7283 Log.i(mTag, "Reporting drop result: " + result); 7284 mWindowSession.reportDropResult(mWindow, result); 7285 } catch (RemoteException e) { 7286 Log.e(mTag, "Unable to report drop result"); 7287 } 7288 } 7289 7290 // When the drag operation ends, reset drag-related state 7291 if (what == DragEvent.ACTION_DRAG_ENDED) { 7292 mCurrentDragView = null; 7293 setLocalDragState(null); 7294 mAttachInfo.mDragToken = null; 7295 if (mAttachInfo.mDragSurface != null) { 7296 mAttachInfo.mDragSurface.release(); 7297 mAttachInfo.mDragSurface = null; 7298 } 7299 } 7300 } 7301 } 7302 event.recycle(); 7303 } 7304 7305 /** 7306 * Notify that the window title changed 7307 */ 7308 public void onWindowTitleChanged() { 7309 mAttachInfo.mForceReportNewAttributes = true; 7310 } 7311 7312 public void handleDispatchWindowShown() { 7313 mAttachInfo.mTreeObserver.dispatchOnWindowShown(); 7314 } 7315 7316 public void handleRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) { 7317 Bundle data = new Bundle(); 7318 ArrayList<KeyboardShortcutGroup> list = new ArrayList<>(); 7319 if (mView != null) { 7320 mView.requestKeyboardShortcuts(list, deviceId); 7321 } 7322 data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list); 7323 try { 7324 receiver.send(0, data); 7325 } catch (RemoteException e) { 7326 } 7327 } 7328 7329 @UnsupportedAppUsage 7330 public void getLastTouchPoint(Point outLocation) { 7331 outLocation.x = (int) mLastTouchPoint.x; 7332 outLocation.y = (int) mLastTouchPoint.y; 7333 } 7334 7335 public int getLastTouchSource() { 7336 return mLastTouchSource; 7337 } 7338 7339 public void setDragFocus(View newDragTarget, DragEvent event) { 7340 if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) { 7341 // Send EXITED and ENTERED notifications to the old and new drag focus views. 7342 7343 final float tx = event.mX; 7344 final float ty = event.mY; 7345 final int action = event.mAction; 7346 final ClipData td = event.mClipData; 7347 // Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED. 7348 event.mX = 0; 7349 event.mY = 0; 7350 event.mClipData = null; 7351 7352 if (mCurrentDragView != null) { 7353 event.mAction = DragEvent.ACTION_DRAG_EXITED; 7354 mCurrentDragView.callDragEventHandler(event); 7355 } 7356 7357 if (newDragTarget != null) { 7358 event.mAction = DragEvent.ACTION_DRAG_ENTERED; 7359 newDragTarget.callDragEventHandler(event); 7360 } 7361 7362 event.mAction = action; 7363 event.mX = tx; 7364 event.mY = ty; 7365 event.mClipData = td; 7366 } 7367 7368 mCurrentDragView = newDragTarget; 7369 } 7370 7371 private AudioManager getAudioManager() { 7372 if (mView == null) { 7373 throw new IllegalStateException("getAudioManager called when there is no mView"); 7374 } 7375 if (mAudioManager == null) { 7376 mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE); 7377 } 7378 return mAudioManager; 7379 } 7380 7381 private @Nullable AutofillManager getAutofillManager() { 7382 if (mView instanceof ViewGroup) { 7383 ViewGroup decorView = (ViewGroup) mView; 7384 if (decorView.getChildCount() > 0) { 7385 // We cannot use decorView's Context for querying AutofillManager: DecorView's 7386 // context is based on Application Context, it would allocate a different 7387 // AutofillManager instance. 7388 return decorView.getChildAt(0).getContext() 7389 .getSystemService(AutofillManager.class); 7390 } 7391 } 7392 return null; 7393 } 7394 isAutofillUiShowing()7395 private boolean isAutofillUiShowing() { 7396 AutofillManager afm = getAutofillManager(); 7397 if (afm == null) { 7398 return false; 7399 } 7400 return afm.isAutofillUiShowing(); 7401 } 7402 getAccessibilityInteractionController()7403 public AccessibilityInteractionController getAccessibilityInteractionController() { 7404 if (mView == null) { 7405 throw new IllegalStateException("getAccessibilityInteractionController" 7406 + " called when there is no mView"); 7407 } 7408 if (mAccessibilityInteractionController == null) { 7409 mAccessibilityInteractionController = new AccessibilityInteractionController(this); 7410 } 7411 return mAccessibilityInteractionController; 7412 } 7413 relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending)7414 private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, 7415 boolean insetsPending) throws RemoteException { 7416 7417 float appScale = mAttachInfo.mApplicationScale; 7418 boolean restore = false; 7419 if (params != null && mTranslator != null) { 7420 restore = true; 7421 params.backup(); 7422 mTranslator.translateWindowLayout(params); 7423 } 7424 7425 if (params != null) { 7426 if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params); 7427 7428 if (mOrigWindowType != params.type) { 7429 // For compatibility with old apps, don't crash here. 7430 if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 7431 Slog.w(mTag, "Window type can not be changed after " 7432 + "the window is added; ignoring change of " + mView); 7433 params.type = mOrigWindowType; 7434 } 7435 } 7436 } 7437 7438 long frameNumber = -1; 7439 if (mSurface.isValid()) { 7440 frameNumber = mSurface.getNextFrameNumber(); 7441 } 7442 7443 int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params, 7444 (int) (mView.getMeasuredWidth() * appScale + 0.5f), 7445 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, 7446 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, 7447 mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame, 7448 mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, 7449 mTempControls, mSurfaceSize, mBlastSurfaceControl); 7450 if (mSurfaceControl.isValid()) { 7451 if (!useBLAST()) { 7452 mSurface.copyFrom(mSurfaceControl); 7453 } else { 7454 final Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x, 7455 mSurfaceSize.y); 7456 // If blastSurface == null that means it hasn't changed since the last time we 7457 // called. In this situation, avoid calling transferFrom as we would then 7458 // inc the generation ID and cause EGL resources to be recreated. 7459 if (blastSurface != null) { 7460 mSurface.transferFrom(blastSurface); 7461 } 7462 } 7463 } else { 7464 destroySurface(); 7465 } 7466 7467 mPendingAlwaysConsumeSystemBars = 7468 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0; 7469 7470 if (restore) { 7471 params.restore(); 7472 } 7473 7474 if (mTranslator != null) { 7475 mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame); 7476 } 7477 setFrame(mTmpFrame); 7478 mInsetsController.onStateChanged(mTempInsets); 7479 mInsetsController.onControlsChanged(mTempControls); 7480 return relayoutResult; 7481 } 7482 setFrame(Rect frame)7483 private void setFrame(Rect frame) { 7484 mWinFrame.set(frame); 7485 mInsetsController.onFrameChanged(frame); 7486 } 7487 7488 /** 7489 * {@inheritDoc} 7490 */ 7491 @Override playSoundEffect(int effectId)7492 public void playSoundEffect(int effectId) { 7493 checkThread(); 7494 7495 try { 7496 final AudioManager audioManager = getAudioManager(); 7497 7498 switch (effectId) { 7499 case SoundEffectConstants.CLICK: 7500 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 7501 return; 7502 case SoundEffectConstants.NAVIGATION_DOWN: 7503 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); 7504 return; 7505 case SoundEffectConstants.NAVIGATION_LEFT: 7506 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); 7507 return; 7508 case SoundEffectConstants.NAVIGATION_RIGHT: 7509 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); 7510 return; 7511 case SoundEffectConstants.NAVIGATION_UP: 7512 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); 7513 return; 7514 default: 7515 throw new IllegalArgumentException("unknown effect id " + effectId + 7516 " not defined in " + SoundEffectConstants.class.getCanonicalName()); 7517 } 7518 } catch (IllegalStateException e) { 7519 // Exception thrown by getAudioManager() when mView is null 7520 Log.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e); 7521 e.printStackTrace(); 7522 } 7523 } 7524 7525 /** 7526 * {@inheritDoc} 7527 */ 7528 @Override performHapticFeedback(int effectId, boolean always)7529 public boolean performHapticFeedback(int effectId, boolean always) { 7530 try { 7531 return mWindowSession.performHapticFeedback(effectId, always); 7532 } catch (RemoteException e) { 7533 return false; 7534 } 7535 } 7536 7537 /** 7538 * {@inheritDoc} 7539 */ 7540 @Override focusSearch(View focused, int direction)7541 public View focusSearch(View focused, int direction) { 7542 checkThread(); 7543 if (!(mView instanceof ViewGroup)) { 7544 return null; 7545 } 7546 return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction); 7547 } 7548 7549 /** 7550 * {@inheritDoc} 7551 */ 7552 @Override keyboardNavigationClusterSearch(View currentCluster, @FocusDirection int direction)7553 public View keyboardNavigationClusterSearch(View currentCluster, 7554 @FocusDirection int direction) { 7555 checkThread(); 7556 return FocusFinder.getInstance().findNextKeyboardNavigationCluster( 7557 mView, currentCluster, direction); 7558 } 7559 debug()7560 public void debug() { 7561 mView.debug(); 7562 } 7563 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)7564 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 7565 String innerPrefix = prefix + " "; 7566 writer.print(prefix); writer.println("ViewRoot:"); 7567 writer.print(innerPrefix); writer.print("mAdded="); writer.print(mAdded); 7568 writer.print(" mRemoved="); writer.println(mRemoved); 7569 writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled="); 7570 writer.println(mConsumeBatchedInputScheduled); 7571 writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled="); 7572 writer.println(mConsumeBatchedInputImmediatelyScheduled); 7573 writer.print(innerPrefix); writer.print("mPendingInputEventCount="); 7574 writer.println(mPendingInputEventCount); 7575 writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled="); 7576 writer.println(mProcessInputEventsScheduled); 7577 writer.print(innerPrefix); writer.print("mTraversalScheduled="); 7578 writer.print(mTraversalScheduled); 7579 writer.print(innerPrefix); writer.print("mIsAmbientMode="); 7580 writer.print(mIsAmbientMode); 7581 writer.print(innerPrefix); writer.print("mUnbufferedInputSource="); 7582 writer.print(Integer.toHexString(mUnbufferedInputSource)); 7583 7584 if (mTraversalScheduled) { 7585 writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")"); 7586 } else { 7587 writer.println(); 7588 } 7589 mFirstInputStage.dump(innerPrefix, writer); 7590 7591 mChoreographer.dump(prefix, writer); 7592 7593 mInsetsController.dump(prefix, writer); 7594 7595 writer.print(prefix); writer.println("View Hierarchy:"); 7596 dumpViewHierarchy(innerPrefix, writer, mView); 7597 } 7598 dumpViewHierarchy(String prefix, PrintWriter writer, View view)7599 private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) { 7600 writer.print(prefix); 7601 if (view == null) { 7602 writer.println("null"); 7603 return; 7604 } 7605 writer.println(view.toString()); 7606 if (!(view instanceof ViewGroup)) { 7607 return; 7608 } 7609 ViewGroup grp = (ViewGroup)view; 7610 final int N = grp.getChildCount(); 7611 if (N <= 0) { 7612 return; 7613 } 7614 prefix = prefix + " "; 7615 for (int i=0; i<N; i++) { 7616 dumpViewHierarchy(prefix, writer, grp.getChildAt(i)); 7617 } 7618 } 7619 7620 static final class GfxInfo { 7621 public int viewCount; 7622 public long renderNodeMemoryUsage; 7623 public long renderNodeMemoryAllocated; 7624 add(GfxInfo other)7625 void add(GfxInfo other) { 7626 viewCount += other.viewCount; 7627 renderNodeMemoryUsage += other.renderNodeMemoryUsage; 7628 renderNodeMemoryAllocated += other.renderNodeMemoryAllocated; 7629 } 7630 } 7631 getGfxInfo()7632 GfxInfo getGfxInfo() { 7633 GfxInfo info = new GfxInfo(); 7634 if (mView != null) { 7635 appendGfxInfo(mView, info); 7636 } 7637 return info; 7638 } 7639 computeRenderNodeUsage(RenderNode node, GfxInfo info)7640 private static void computeRenderNodeUsage(RenderNode node, GfxInfo info) { 7641 if (node == null) return; 7642 info.renderNodeMemoryUsage += node.computeApproximateMemoryUsage(); 7643 info.renderNodeMemoryAllocated += node.computeApproximateMemoryAllocated(); 7644 } 7645 appendGfxInfo(View view, GfxInfo info)7646 private static void appendGfxInfo(View view, GfxInfo info) { 7647 info.viewCount++; 7648 computeRenderNodeUsage(view.mRenderNode, info); 7649 computeRenderNodeUsage(view.mBackgroundRenderNode, info); 7650 if (view instanceof ViewGroup) { 7651 ViewGroup group = (ViewGroup) view; 7652 7653 int count = group.getChildCount(); 7654 for (int i = 0; i < count; i++) { 7655 appendGfxInfo(group.getChildAt(i), info); 7656 } 7657 } 7658 } 7659 7660 /** 7661 * @param immediate True, do now if not in traversal. False, put on queue and do later. 7662 * @return True, request has been queued. False, request has been completed. 7663 */ die(boolean immediate)7664 boolean die(boolean immediate) { 7665 // Make sure we do execute immediately if we are in the middle of a traversal or the damage 7666 // done by dispatchDetachedFromWindow will cause havoc on return. 7667 if (immediate && !mIsInTraversal) { 7668 doDie(); 7669 return false; 7670 } 7671 7672 if (!mIsDrawing) { 7673 destroyHardwareRenderer(); 7674 } else { 7675 Log.e(mTag, "Attempting to destroy the window while drawing!\n" + 7676 " window=" + this + ", title=" + mWindowAttributes.getTitle()); 7677 } 7678 mHandler.sendEmptyMessage(MSG_DIE); 7679 return true; 7680 } 7681 doDie()7682 void doDie() { 7683 checkThread(); 7684 if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface); 7685 synchronized (this) { 7686 if (mRemoved) { 7687 return; 7688 } 7689 mRemoved = true; 7690 if (mAdded) { 7691 dispatchDetachedFromWindow(); 7692 } 7693 7694 if (mAdded && !mFirst) { 7695 destroyHardwareRenderer(); 7696 7697 if (mView != null) { 7698 int viewVisibility = mView.getVisibility(); 7699 boolean viewVisibilityChanged = mViewVisibility != viewVisibility; 7700 if (mWindowAttributesChanged || viewVisibilityChanged) { 7701 // If layout params have been changed, first give them 7702 // to the window manager to make sure it has the correct 7703 // animation info. 7704 try { 7705 if ((relayoutWindow(mWindowAttributes, viewVisibility, false) 7706 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 7707 mWindowSession.finishDrawing( 7708 mWindow, null /* postDrawTransaction */); 7709 } 7710 } catch (RemoteException e) { 7711 } 7712 } 7713 7714 destroySurface(); 7715 } 7716 } 7717 7718 mAdded = false; 7719 } 7720 WindowManagerGlobal.getInstance().doRemoveView(this); 7721 } 7722 requestUpdateConfiguration(Configuration config)7723 public void requestUpdateConfiguration(Configuration config) { 7724 Message msg = mHandler.obtainMessage(MSG_UPDATE_CONFIGURATION, config); 7725 mHandler.sendMessage(msg); 7726 } 7727 loadSystemProperties()7728 public void loadSystemProperties() { 7729 mHandler.post(new Runnable() { 7730 @Override 7731 public void run() { 7732 // Profiling 7733 mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false); 7734 profileRendering(mAttachInfo.mHasWindowFocus); 7735 7736 // Hardware rendering 7737 if (mAttachInfo.mThreadedRenderer != null) { 7738 if (mAttachInfo.mThreadedRenderer.loadSystemProperties()) { 7739 invalidate(); 7740 } 7741 } 7742 7743 // Layout debugging 7744 boolean layout = DisplayProperties.debug_layout().orElse(false); 7745 if (layout != mAttachInfo.mDebugLayout) { 7746 mAttachInfo.mDebugLayout = layout; 7747 if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { 7748 mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200); 7749 } 7750 } 7751 } 7752 }); 7753 } 7754 destroyHardwareRenderer()7755 private void destroyHardwareRenderer() { 7756 ThreadedRenderer hardwareRenderer = mAttachInfo.mThreadedRenderer; 7757 7758 if (hardwareRenderer != null) { 7759 if (mView != null) { 7760 hardwareRenderer.destroyHardwareResources(mView); 7761 } 7762 hardwareRenderer.destroy(); 7763 hardwareRenderer.setRequested(false); 7764 7765 mAttachInfo.mThreadedRenderer = null; 7766 mAttachInfo.mHardwareAccelerated = false; 7767 } 7768 } 7769 7770 @UnsupportedAppUsage dispatchResized(Rect frame, Rect contentInsets, Rect visibleInsets, Rect stableInsets, boolean reportDraw, MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, DisplayCutout.ParcelableWrapper displayCutout)7771 private void dispatchResized(Rect frame, Rect contentInsets, 7772 Rect visibleInsets, Rect stableInsets, boolean reportDraw, 7773 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, 7774 boolean alwaysConsumeSystemBars, int displayId, 7775 DisplayCutout.ParcelableWrapper displayCutout) { 7776 if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString() 7777 + " contentInsets=" + contentInsets.toShortString() 7778 + " visibleInsets=" + visibleInsets.toShortString() 7779 + " reportDraw=" + reportDraw 7780 + " backDropFrame=" + backDropFrame); 7781 7782 // Tell all listeners that we are resizing the window so that the chrome can get 7783 // updated as fast as possible on a separate thread, 7784 if (mDragResizing && mUseMTRenderer) { 7785 boolean fullscreen = frame.equals(backDropFrame); 7786 synchronized (mWindowCallbacks) { 7787 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 7788 mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen, 7789 visibleInsets, stableInsets); 7790 } 7791 } 7792 } 7793 7794 Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED); 7795 if (mTranslator != null) { 7796 mTranslator.translateRectInScreenToAppWindow(frame); 7797 mTranslator.translateRectInScreenToAppWindow(contentInsets); 7798 mTranslator.translateRectInScreenToAppWindow(visibleInsets); 7799 } 7800 SomeArgs args = SomeArgs.obtain(); 7801 final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid()); 7802 args.arg1 = sameProcessCall ? new Rect(frame) : frame; 7803 args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets; 7804 args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets; 7805 args.arg4 = sameProcessCall && mergedConfiguration != null 7806 ? new MergedConfiguration(mergedConfiguration) : mergedConfiguration; 7807 args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets; 7808 args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame; 7809 args.arg9 = displayCutout.get(); // DisplayCutout is immutable. 7810 args.argi1 = forceLayout ? 1 : 0; 7811 args.argi2 = alwaysConsumeSystemBars ? 1 : 0; 7812 args.argi3 = displayId; 7813 msg.obj = args; 7814 mHandler.sendMessage(msg); 7815 } 7816 dispatchInsetsChanged(InsetsState insetsState)7817 private void dispatchInsetsChanged(InsetsState insetsState) { 7818 if (Binder.getCallingPid() == android.os.Process.myPid()) { 7819 insetsState = new InsetsState(insetsState, true /* copySource */); 7820 } 7821 mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget(); 7822 } 7823 dispatchInsetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)7824 private void dispatchInsetsControlChanged(InsetsState insetsState, 7825 InsetsSourceControl[] activeControls) { 7826 if (Binder.getCallingPid() == android.os.Process.myPid()) { 7827 insetsState = new InsetsState(insetsState, true /* copySource */); 7828 if (activeControls != null) { 7829 for (int i = activeControls.length - 1; i >= 0; i--) { 7830 activeControls[i] = new InsetsSourceControl(activeControls[i]); 7831 } 7832 } 7833 } 7834 SomeArgs args = SomeArgs.obtain(); 7835 args.arg1 = insetsState; 7836 args.arg2 = activeControls; 7837 mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget(); 7838 } 7839 showInsets(@nsetsType int types, boolean fromIme)7840 private void showInsets(@InsetsType int types, boolean fromIme) { 7841 mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget(); 7842 } 7843 hideInsets(@nsetsType int types, boolean fromIme)7844 private void hideInsets(@InsetsType int types, boolean fromIme) { 7845 mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget(); 7846 } 7847 dispatchMoved(int newX, int newY)7848 public void dispatchMoved(int newX, int newY) { 7849 if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY); 7850 if (mTranslator != null) { 7851 PointF point = new PointF(newX, newY); 7852 mTranslator.translatePointInScreenToAppWindow(point); 7853 newX = (int) (point.x + 0.5); 7854 newY = (int) (point.y + 0.5); 7855 } 7856 Message msg = mHandler.obtainMessage(MSG_WINDOW_MOVED, newX, newY); 7857 mHandler.sendMessage(msg); 7858 } 7859 7860 /** 7861 * Represents a pending input event that is waiting in a queue. 7862 * 7863 * Input events are processed in serial order by the timestamp specified by 7864 * {@link InputEvent#getEventTimeNano()}. In general, the input dispatcher delivers 7865 * one input event to the application at a time and waits for the application 7866 * to finish handling it before delivering the next one. 7867 * 7868 * However, because the application or IME can synthesize and inject multiple 7869 * key events at a time without going through the input dispatcher, we end up 7870 * needing a queue on the application's side. 7871 */ 7872 private static final class QueuedInputEvent { 7873 public static final int FLAG_DELIVER_POST_IME = 1 << 0; 7874 public static final int FLAG_DEFERRED = 1 << 1; 7875 public static final int FLAG_FINISHED = 1 << 2; 7876 public static final int FLAG_FINISHED_HANDLED = 1 << 3; 7877 public static final int FLAG_RESYNTHESIZED = 1 << 4; 7878 public static final int FLAG_UNHANDLED = 1 << 5; 7879 public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6; 7880 7881 public QueuedInputEvent mNext; 7882 7883 public InputEvent mEvent; 7884 public InputEventReceiver mReceiver; 7885 public int mFlags; 7886 shouldSkipIme()7887 public boolean shouldSkipIme() { 7888 if ((mFlags & FLAG_DELIVER_POST_IME) != 0) { 7889 return true; 7890 } 7891 return mEvent instanceof MotionEvent 7892 && (mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) 7893 || mEvent.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER)); 7894 } 7895 shouldSendToSynthesizer()7896 public boolean shouldSendToSynthesizer() { 7897 if ((mFlags & FLAG_UNHANDLED) != 0) { 7898 return true; 7899 } 7900 7901 return false; 7902 } 7903 7904 @Override toString()7905 public String toString() { 7906 StringBuilder sb = new StringBuilder("QueuedInputEvent{flags="); 7907 boolean hasPrevious = false; 7908 hasPrevious = flagToString("DELIVER_POST_IME", FLAG_DELIVER_POST_IME, hasPrevious, sb); 7909 hasPrevious = flagToString("DEFERRED", FLAG_DEFERRED, hasPrevious, sb); 7910 hasPrevious = flagToString("FINISHED", FLAG_FINISHED, hasPrevious, sb); 7911 hasPrevious = flagToString("FINISHED_HANDLED", FLAG_FINISHED_HANDLED, hasPrevious, sb); 7912 hasPrevious = flagToString("RESYNTHESIZED", FLAG_RESYNTHESIZED, hasPrevious, sb); 7913 hasPrevious = flagToString("UNHANDLED", FLAG_UNHANDLED, hasPrevious, sb); 7914 if (!hasPrevious) { 7915 sb.append("0"); 7916 } 7917 sb.append(", hasNextQueuedEvent=" + (mEvent != null ? "true" : "false")); 7918 sb.append(", hasInputEventReceiver=" + (mReceiver != null ? "true" : "false")); 7919 sb.append(", mEvent=" + mEvent + "}"); 7920 return sb.toString(); 7921 } 7922 flagToString(String name, int flag, boolean hasPrevious, StringBuilder sb)7923 private boolean flagToString(String name, int flag, 7924 boolean hasPrevious, StringBuilder sb) { 7925 if ((mFlags & flag) != 0) { 7926 if (hasPrevious) { 7927 sb.append("|"); 7928 } 7929 sb.append(name); 7930 return true; 7931 } 7932 return hasPrevious; 7933 } 7934 } 7935 obtainQueuedInputEvent(InputEvent event, InputEventReceiver receiver, int flags)7936 private QueuedInputEvent obtainQueuedInputEvent(InputEvent event, 7937 InputEventReceiver receiver, int flags) { 7938 QueuedInputEvent q = mQueuedInputEventPool; 7939 if (q != null) { 7940 mQueuedInputEventPoolSize -= 1; 7941 mQueuedInputEventPool = q.mNext; 7942 q.mNext = null; 7943 } else { 7944 q = new QueuedInputEvent(); 7945 } 7946 7947 q.mEvent = event; 7948 q.mReceiver = receiver; 7949 q.mFlags = flags; 7950 return q; 7951 } 7952 recycleQueuedInputEvent(QueuedInputEvent q)7953 private void recycleQueuedInputEvent(QueuedInputEvent q) { 7954 q.mEvent = null; 7955 q.mReceiver = null; 7956 7957 if (mQueuedInputEventPoolSize < MAX_QUEUED_INPUT_EVENT_POOL_SIZE) { 7958 mQueuedInputEventPoolSize += 1; 7959 q.mNext = mQueuedInputEventPool; 7960 mQueuedInputEventPool = q; 7961 } 7962 } 7963 7964 @UnsupportedAppUsage enqueueInputEvent(InputEvent event)7965 void enqueueInputEvent(InputEvent event) { 7966 enqueueInputEvent(event, null, 0, false); 7967 } 7968 7969 @UnsupportedAppUsage enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately)7970 void enqueueInputEvent(InputEvent event, 7971 InputEventReceiver receiver, int flags, boolean processImmediately) { 7972 QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); 7973 7974 // Always enqueue the input event in order, regardless of its time stamp. 7975 // We do this because the application or the IME may inject key events 7976 // in response to touch events and we want to ensure that the injected keys 7977 // are processed in the order they were received and we cannot trust that 7978 // the time stamp of injected events are monotonic. 7979 QueuedInputEvent last = mPendingInputEventTail; 7980 if (last == null) { 7981 mPendingInputEventHead = q; 7982 mPendingInputEventTail = q; 7983 } else { 7984 last.mNext = q; 7985 mPendingInputEventTail = q; 7986 } 7987 mPendingInputEventCount += 1; 7988 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, 7989 mPendingInputEventCount); 7990 7991 if (processImmediately) { 7992 doProcessInputEvents(); 7993 } else { 7994 scheduleProcessInputEvents(); 7995 } 7996 } 7997 scheduleProcessInputEvents()7998 private void scheduleProcessInputEvents() { 7999 if (!mProcessInputEventsScheduled) { 8000 mProcessInputEventsScheduled = true; 8001 Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS); 8002 msg.setAsynchronous(true); 8003 mHandler.sendMessage(msg); 8004 } 8005 } 8006 doProcessInputEvents()8007 void doProcessInputEvents() { 8008 // Deliver all pending input events in the queue. 8009 while (mPendingInputEventHead != null) { 8010 QueuedInputEvent q = mPendingInputEventHead; 8011 mPendingInputEventHead = q.mNext; 8012 if (mPendingInputEventHead == null) { 8013 mPendingInputEventTail = null; 8014 } 8015 q.mNext = null; 8016 8017 mPendingInputEventCount -= 1; 8018 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, 8019 mPendingInputEventCount); 8020 8021 long eventTime = q.mEvent.getEventTimeNano(); 8022 long oldestEventTime = eventTime; 8023 if (q.mEvent instanceof MotionEvent) { 8024 MotionEvent me = (MotionEvent)q.mEvent; 8025 if (me.getHistorySize() > 0) { 8026 oldestEventTime = me.getHistoricalEventTimeNano(0); 8027 } 8028 } 8029 mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime); 8030 8031 deliverInputEvent(q); 8032 } 8033 8034 // We are done processing all input events that we can process right now 8035 // so we can clear the pending flag immediately. 8036 if (mProcessInputEventsScheduled) { 8037 mProcessInputEventsScheduled = false; 8038 mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); 8039 } 8040 } 8041 deliverInputEvent(QueuedInputEvent q)8042 private void deliverInputEvent(QueuedInputEvent q) { 8043 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent", 8044 q.mEvent.getId()); 8045 8046 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 8047 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x" 8048 + Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano=" 8049 + q.mEvent.getEventTimeNano() + " id=0x" 8050 + Integer.toHexString(q.mEvent.getId())); 8051 } 8052 try { 8053 if (mInputEventConsistencyVerifier != null) { 8054 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "verifyEventConsistency"); 8055 try { 8056 mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0); 8057 } finally { 8058 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 8059 } 8060 } 8061 8062 InputStage stage; 8063 if (q.shouldSendToSynthesizer()) { 8064 stage = mSyntheticInputStage; 8065 } else { 8066 stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; 8067 } 8068 8069 if (q.mEvent instanceof KeyEvent) { 8070 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager"); 8071 try { 8072 mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); 8073 } finally { 8074 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 8075 } 8076 } 8077 8078 if (stage != null) { 8079 handleWindowFocusChanged(); 8080 stage.deliver(q); 8081 } else { 8082 finishInputEvent(q); 8083 } 8084 } finally { 8085 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 8086 } 8087 } 8088 finishInputEvent(QueuedInputEvent q)8089 private void finishInputEvent(QueuedInputEvent q) { 8090 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent", 8091 q.mEvent.getId()); 8092 8093 if (q.mReceiver != null) { 8094 boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; 8095 boolean modified = (q.mFlags & QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY) != 0; 8096 if (modified) { 8097 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventBeforeFinish"); 8098 InputEvent processedEvent; 8099 try { 8100 processedEvent = 8101 mInputCompatProcessor.processInputEventBeforeFinish(q.mEvent); 8102 } finally { 8103 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 8104 } 8105 if (processedEvent != null) { 8106 q.mReceiver.finishInputEvent(processedEvent, handled); 8107 } 8108 } else { 8109 q.mReceiver.finishInputEvent(q.mEvent, handled); 8110 } 8111 } else { 8112 q.mEvent.recycleIfNeededAfterDispatch(); 8113 } 8114 8115 recycleQueuedInputEvent(q); 8116 } 8117 isTerminalInputEvent(InputEvent event)8118 static boolean isTerminalInputEvent(InputEvent event) { 8119 if (event instanceof KeyEvent) { 8120 final KeyEvent keyEvent = (KeyEvent)event; 8121 return keyEvent.getAction() == KeyEvent.ACTION_UP; 8122 } else { 8123 final MotionEvent motionEvent = (MotionEvent)event; 8124 final int action = motionEvent.getAction(); 8125 return action == MotionEvent.ACTION_UP 8126 || action == MotionEvent.ACTION_CANCEL 8127 || action == MotionEvent.ACTION_HOVER_EXIT; 8128 } 8129 } 8130 scheduleConsumeBatchedInput()8131 void scheduleConsumeBatchedInput() { 8132 // If anything is currently scheduled to consume batched input then there's no point in 8133 // scheduling it again. 8134 if (!mConsumeBatchedInputScheduled && !mConsumeBatchedInputImmediatelyScheduled) { 8135 mConsumeBatchedInputScheduled = true; 8136 mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, 8137 mConsumedBatchedInputRunnable, null); 8138 } 8139 } 8140 unscheduleConsumeBatchedInput()8141 void unscheduleConsumeBatchedInput() { 8142 if (mConsumeBatchedInputScheduled) { 8143 mConsumeBatchedInputScheduled = false; 8144 mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, 8145 mConsumedBatchedInputRunnable, null); 8146 } 8147 } 8148 scheduleConsumeBatchedInputImmediately()8149 void scheduleConsumeBatchedInputImmediately() { 8150 if (!mConsumeBatchedInputImmediatelyScheduled) { 8151 unscheduleConsumeBatchedInput(); 8152 mConsumeBatchedInputImmediatelyScheduled = true; 8153 mHandler.post(mConsumeBatchedInputImmediatelyRunnable); 8154 } 8155 } 8156 doConsumeBatchedInput(long frameTimeNanos)8157 boolean doConsumeBatchedInput(long frameTimeNanos) { 8158 final boolean consumedBatches; 8159 if (mInputEventReceiver != null) { 8160 consumedBatches = mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos); 8161 } else { 8162 consumedBatches = false; 8163 } 8164 doProcessInputEvents(); 8165 return consumedBatches; 8166 } 8167 8168 final class TraversalRunnable implements Runnable { 8169 @Override run()8170 public void run() { 8171 doTraversal(); 8172 } 8173 } 8174 final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 8175 8176 final class WindowInputEventReceiver extends InputEventReceiver { WindowInputEventReceiver(InputChannel inputChannel, Looper looper)8177 public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { 8178 super(inputChannel, looper); 8179 } 8180 8181 @Override onInputEvent(InputEvent event)8182 public void onInputEvent(InputEvent event) { 8183 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility"); 8184 List<InputEvent> processedEvents; 8185 try { 8186 processedEvents = 8187 mInputCompatProcessor.processInputEventForCompatibility(event); 8188 } finally { 8189 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 8190 } 8191 if (processedEvents != null) { 8192 if (processedEvents.isEmpty()) { 8193 // InputEvent consumed by mInputCompatProcessor 8194 finishInputEvent(event, true); 8195 } else { 8196 for (int i = 0; i < processedEvents.size(); i++) { 8197 enqueueInputEvent( 8198 processedEvents.get(i), this, 8199 QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true); 8200 } 8201 } 8202 } else { 8203 enqueueInputEvent(event, this, 0, true); 8204 } 8205 } 8206 8207 @Override onBatchedInputEventPending(int source)8208 public void onBatchedInputEventPending(int source) { 8209 // mStopped: There will be no more choreographer callbacks if we are stopped, 8210 // so we must consume all input immediately to prevent ANR 8211 final boolean unbuffered = mUnbufferedInputDispatch 8212 || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE 8213 || mStopped; 8214 if (unbuffered) { 8215 if (mConsumeBatchedInputScheduled) { 8216 unscheduleConsumeBatchedInput(); 8217 } 8218 // Consume event immediately if unbuffered input dispatch has been requested. 8219 consumeBatchedInputEvents(-1); 8220 return; 8221 } 8222 scheduleConsumeBatchedInput(); 8223 } 8224 8225 @Override onFocusEvent(boolean hasFocus, boolean inTouchMode)8226 public void onFocusEvent(boolean hasFocus, boolean inTouchMode) { 8227 windowFocusChanged(hasFocus, inTouchMode); 8228 } 8229 8230 @Override dispose()8231 public void dispose() { 8232 unscheduleConsumeBatchedInput(); 8233 super.dispose(); 8234 } 8235 } 8236 WindowInputEventReceiver mInputEventReceiver; 8237 8238 final class ConsumeBatchedInputRunnable implements Runnable { 8239 @Override run()8240 public void run() { 8241 mConsumeBatchedInputScheduled = false; 8242 if (doConsumeBatchedInput(mChoreographer.getFrameTimeNanos())) { 8243 // If we consumed a batch here, we want to go ahead and schedule the 8244 // consumption of batched input events on the next frame. Otherwise, we would 8245 // wait until we have more input events pending and might get starved by other 8246 // things occurring in the process. 8247 scheduleConsumeBatchedInput(); 8248 } 8249 } 8250 } 8251 final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable = 8252 new ConsumeBatchedInputRunnable(); 8253 boolean mConsumeBatchedInputScheduled; 8254 8255 final class ConsumeBatchedInputImmediatelyRunnable implements Runnable { 8256 @Override run()8257 public void run() { 8258 mConsumeBatchedInputImmediatelyScheduled = false; 8259 doConsumeBatchedInput(-1); 8260 } 8261 } 8262 final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable = 8263 new ConsumeBatchedInputImmediatelyRunnable(); 8264 boolean mConsumeBatchedInputImmediatelyScheduled; 8265 8266 final class InvalidateOnAnimationRunnable implements Runnable { 8267 private boolean mPosted; 8268 private final ArrayList<View> mViews = new ArrayList<View>(); 8269 private final ArrayList<AttachInfo.InvalidateInfo> mViewRects = 8270 new ArrayList<AttachInfo.InvalidateInfo>(); 8271 private View[] mTempViews; 8272 private AttachInfo.InvalidateInfo[] mTempViewRects; 8273 addView(View view)8274 public void addView(View view) { 8275 synchronized (this) { 8276 mViews.add(view); 8277 postIfNeededLocked(); 8278 } 8279 } 8280 addViewRect(AttachInfo.InvalidateInfo info)8281 public void addViewRect(AttachInfo.InvalidateInfo info) { 8282 synchronized (this) { 8283 mViewRects.add(info); 8284 postIfNeededLocked(); 8285 } 8286 } 8287 removeView(View view)8288 public void removeView(View view) { 8289 synchronized (this) { 8290 mViews.remove(view); 8291 8292 for (int i = mViewRects.size(); i-- > 0; ) { 8293 AttachInfo.InvalidateInfo info = mViewRects.get(i); 8294 if (info.target == view) { 8295 mViewRects.remove(i); 8296 info.recycle(); 8297 } 8298 } 8299 8300 if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) { 8301 mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null); 8302 mPosted = false; 8303 } 8304 } 8305 } 8306 8307 @Override run()8308 public void run() { 8309 final int viewCount; 8310 final int viewRectCount; 8311 synchronized (this) { 8312 mPosted = false; 8313 8314 viewCount = mViews.size(); 8315 if (viewCount != 0) { 8316 mTempViews = mViews.toArray(mTempViews != null 8317 ? mTempViews : new View[viewCount]); 8318 mViews.clear(); 8319 } 8320 8321 viewRectCount = mViewRects.size(); 8322 if (viewRectCount != 0) { 8323 mTempViewRects = mViewRects.toArray(mTempViewRects != null 8324 ? mTempViewRects : new AttachInfo.InvalidateInfo[viewRectCount]); 8325 mViewRects.clear(); 8326 } 8327 } 8328 8329 for (int i = 0; i < viewCount; i++) { 8330 mTempViews[i].invalidate(); 8331 mTempViews[i] = null; 8332 } 8333 8334 for (int i = 0; i < viewRectCount; i++) { 8335 final View.AttachInfo.InvalidateInfo info = mTempViewRects[i]; 8336 info.target.invalidate(info.left, info.top, info.right, info.bottom); 8337 info.recycle(); 8338 } 8339 } 8340 postIfNeededLocked()8341 private void postIfNeededLocked() { 8342 if (!mPosted) { 8343 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); 8344 mPosted = true; 8345 } 8346 } 8347 } 8348 final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable = 8349 new InvalidateOnAnimationRunnable(); 8350 dispatchInvalidateDelayed(View view, long delayMilliseconds)8351 public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { 8352 Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); 8353 mHandler.sendMessageDelayed(msg, delayMilliseconds); 8354 } 8355 dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info, long delayMilliseconds)8356 public void dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info, 8357 long delayMilliseconds) { 8358 final Message msg = mHandler.obtainMessage(MSG_INVALIDATE_RECT, info); 8359 mHandler.sendMessageDelayed(msg, delayMilliseconds); 8360 } 8361 dispatchInvalidateOnAnimation(View view)8362 public void dispatchInvalidateOnAnimation(View view) { 8363 mInvalidateOnAnimationRunnable.addView(view); 8364 } 8365 dispatchInvalidateRectOnAnimation(AttachInfo.InvalidateInfo info)8366 public void dispatchInvalidateRectOnAnimation(AttachInfo.InvalidateInfo info) { 8367 mInvalidateOnAnimationRunnable.addViewRect(info); 8368 } 8369 8370 @UnsupportedAppUsage cancelInvalidate(View view)8371 public void cancelInvalidate(View view) { 8372 mHandler.removeMessages(MSG_INVALIDATE, view); 8373 // fixme: might leak the AttachInfo.InvalidateInfo objects instead of returning 8374 // them to the pool 8375 mHandler.removeMessages(MSG_INVALIDATE_RECT, view); 8376 mInvalidateOnAnimationRunnable.removeView(view); 8377 } 8378 8379 @UnsupportedAppUsage dispatchInputEvent(InputEvent event)8380 public void dispatchInputEvent(InputEvent event) { 8381 dispatchInputEvent(event, null); 8382 } 8383 8384 @UnsupportedAppUsage dispatchInputEvent(InputEvent event, InputEventReceiver receiver)8385 public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { 8386 SomeArgs args = SomeArgs.obtain(); 8387 args.arg1 = event; 8388 args.arg2 = receiver; 8389 Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args); 8390 msg.setAsynchronous(true); 8391 mHandler.sendMessage(msg); 8392 } 8393 synthesizeInputEvent(InputEvent event)8394 public void synthesizeInputEvent(InputEvent event) { 8395 Message msg = mHandler.obtainMessage(MSG_SYNTHESIZE_INPUT_EVENT, event); 8396 msg.setAsynchronous(true); 8397 mHandler.sendMessage(msg); 8398 } 8399 8400 @UnsupportedAppUsage dispatchKeyFromIme(KeyEvent event)8401 public void dispatchKeyFromIme(KeyEvent event) { 8402 Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event); 8403 msg.setAsynchronous(true); 8404 mHandler.sendMessage(msg); 8405 } 8406 dispatchKeyFromAutofill(KeyEvent event)8407 public void dispatchKeyFromAutofill(KeyEvent event) { 8408 Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_AUTOFILL, event); 8409 msg.setAsynchronous(true); 8410 mHandler.sendMessage(msg); 8411 } 8412 8413 /** 8414 * Reinject unhandled {@link InputEvent}s in order to synthesize fallbacks events. 8415 * 8416 * Note that it is the responsibility of the caller of this API to recycle the InputEvent it 8417 * passes in. 8418 */ 8419 @UnsupportedAppUsage dispatchUnhandledInputEvent(InputEvent event)8420 public void dispatchUnhandledInputEvent(InputEvent event) { 8421 if (event instanceof MotionEvent) { 8422 event = MotionEvent.obtain((MotionEvent) event); 8423 } 8424 synthesizeInputEvent(event); 8425 } 8426 dispatchAppVisibility(boolean visible)8427 public void dispatchAppVisibility(boolean visible) { 8428 Message msg = mHandler.obtainMessage(MSG_DISPATCH_APP_VISIBILITY); 8429 msg.arg1 = visible ? 1 : 0; 8430 mHandler.sendMessage(msg); 8431 } 8432 dispatchGetNewSurface()8433 public void dispatchGetNewSurface() { 8434 Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE); 8435 mHandler.sendMessage(msg); 8436 } 8437 8438 /** 8439 * Dispatch the offset changed. 8440 * 8441 * @param offset the offset of this view in the parent window. 8442 */ dispatchLocationInParentDisplayChanged(Point offset)8443 public void dispatchLocationInParentDisplayChanged(Point offset) { 8444 Message msg = 8445 mHandler.obtainMessage(MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED, offset.x, offset.y); 8446 mHandler.sendMessage(msg); 8447 } 8448 windowFocusChanged(boolean hasFocus, boolean inTouchMode)8449 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 8450 synchronized (this) { 8451 mWindowFocusChanged = true; 8452 mUpcomingWindowFocus = hasFocus; 8453 mUpcomingInTouchMode = inTouchMode; 8454 } 8455 Message msg = Message.obtain(); 8456 msg.what = MSG_WINDOW_FOCUS_CHANGED; 8457 mHandler.sendMessage(msg); 8458 } 8459 dispatchWindowShown()8460 public void dispatchWindowShown() { 8461 mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN); 8462 } 8463 dispatchCloseSystemDialogs(String reason)8464 public void dispatchCloseSystemDialogs(String reason) { 8465 Message msg = Message.obtain(); 8466 msg.what = MSG_CLOSE_SYSTEM_DIALOGS; 8467 msg.obj = reason; 8468 mHandler.sendMessage(msg); 8469 } 8470 dispatchDragEvent(DragEvent event)8471 public void dispatchDragEvent(DragEvent event) { 8472 final int what; 8473 if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) { 8474 what = MSG_DISPATCH_DRAG_LOCATION_EVENT; 8475 mHandler.removeMessages(what); 8476 } else { 8477 what = MSG_DISPATCH_DRAG_EVENT; 8478 } 8479 Message msg = mHandler.obtainMessage(what, event); 8480 mHandler.sendMessage(msg); 8481 } 8482 updatePointerIcon(float x, float y)8483 public void updatePointerIcon(float x, float y) { 8484 final int what = MSG_UPDATE_POINTER_ICON; 8485 mHandler.removeMessages(what); 8486 final long now = SystemClock.uptimeMillis(); 8487 final MotionEvent event = MotionEvent.obtain( 8488 0, now, MotionEvent.ACTION_HOVER_MOVE, x, y, 0); 8489 Message msg = mHandler.obtainMessage(what, event); 8490 mHandler.sendMessage(msg); 8491 } 8492 8493 // TODO(118118435): Remove this after migration dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue, int localChanges)8494 public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, 8495 int localValue, int localChanges) { 8496 SystemUiVisibilityInfo args = new SystemUiVisibilityInfo(); 8497 args.seq = seq; 8498 args.globalVisibility = globalVisibility; 8499 args.localValue = localValue; 8500 args.localChanges = localChanges; 8501 mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); 8502 } 8503 dispatchCheckFocus()8504 public void dispatchCheckFocus() { 8505 if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) { 8506 // This will result in a call to checkFocus() below. 8507 mHandler.sendEmptyMessage(MSG_CHECK_FOCUS); 8508 } 8509 } 8510 dispatchRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId)8511 public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) { 8512 mHandler.obtainMessage( 8513 MSG_REQUEST_KEYBOARD_SHORTCUTS, deviceId, 0, receiver).sendToTarget(); 8514 } 8515 dispatchPointerCaptureChanged(boolean on)8516 public void dispatchPointerCaptureChanged(boolean on) { 8517 final int what = MSG_POINTER_CAPTURE_CHANGED; 8518 mHandler.removeMessages(what); 8519 Message msg = mHandler.obtainMessage(what); 8520 msg.arg1 = on ? 1 : 0; 8521 mHandler.sendMessage(msg); 8522 } 8523 8524 /** 8525 * Post a callback to send a 8526 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 8527 * This event is send at most once every 8528 * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}. 8529 */ postSendWindowContentChangedCallback(View source, int changeType)8530 private void postSendWindowContentChangedCallback(View source, int changeType) { 8531 if (mSendWindowContentChangedAccessibilityEvent == null) { 8532 mSendWindowContentChangedAccessibilityEvent = 8533 new SendWindowContentChangedAccessibilityEvent(); 8534 } 8535 mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType); 8536 } 8537 8538 /** 8539 * Remove a posted callback to send a 8540 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 8541 */ removeSendWindowContentChangedCallback()8542 private void removeSendWindowContentChangedCallback() { 8543 if (mSendWindowContentChangedAccessibilityEvent != null) { 8544 mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent); 8545 } 8546 } 8547 8548 @Override showContextMenuForChild(View originalView)8549 public boolean showContextMenuForChild(View originalView) { 8550 return false; 8551 } 8552 8553 @Override showContextMenuForChild(View originalView, float x, float y)8554 public boolean showContextMenuForChild(View originalView, float x, float y) { 8555 return false; 8556 } 8557 8558 @Override startActionModeForChild(View originalView, ActionMode.Callback callback)8559 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { 8560 return null; 8561 } 8562 8563 @Override startActionModeForChild( View originalView, ActionMode.Callback callback, int type)8564 public ActionMode startActionModeForChild( 8565 View originalView, ActionMode.Callback callback, int type) { 8566 return null; 8567 } 8568 8569 @Override createContextMenu(ContextMenu menu)8570 public void createContextMenu(ContextMenu menu) { 8571 } 8572 8573 @Override childDrawableStateChanged(View child)8574 public void childDrawableStateChanged(View child) { 8575 } 8576 8577 @Override requestSendAccessibilityEvent(View child, AccessibilityEvent event)8578 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { 8579 if (mView == null || mStopped || mPausedForTransition) { 8580 return false; 8581 } 8582 8583 // Immediately flush pending content changed event (if any) to preserve event order 8584 if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 8585 && mSendWindowContentChangedAccessibilityEvent != null 8586 && mSendWindowContentChangedAccessibilityEvent.mSource != null) { 8587 mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun(); 8588 } 8589 8590 // Intercept accessibility focus events fired by virtual nodes to keep 8591 // track of accessibility focus position in such nodes. 8592 final int eventType = event.getEventType(); 8593 final View source = getSourceForAccessibilityEvent(event); 8594 switch (eventType) { 8595 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 8596 if (source != null) { 8597 AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); 8598 if (provider != null) { 8599 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( 8600 event.getSourceNodeId()); 8601 final AccessibilityNodeInfo node; 8602 node = provider.createAccessibilityNodeInfo(virtualNodeId); 8603 setAccessibilityFocus(source, node); 8604 } 8605 } 8606 } break; 8607 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { 8608 if (source != null && source.getAccessibilityNodeProvider() != null) { 8609 setAccessibilityFocus(null, null); 8610 } 8611 } break; 8612 8613 8614 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: { 8615 handleWindowContentChangedEvent(event); 8616 } break; 8617 } 8618 mAccessibilityManager.sendAccessibilityEvent(event); 8619 return true; 8620 } 8621 getSourceForAccessibilityEvent(AccessibilityEvent event)8622 private View getSourceForAccessibilityEvent(AccessibilityEvent event) { 8623 final long sourceNodeId = event.getSourceNodeId(); 8624 final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( 8625 sourceNodeId); 8626 return AccessibilityNodeIdManager.getInstance().findView(accessibilityViewId); 8627 } 8628 8629 /** 8630 * Updates the focused virtual view, when necessary, in response to a 8631 * content changed event. 8632 * <p> 8633 * This is necessary to get updated bounds after a position change. 8634 * 8635 * @param event an accessibility event of type 8636 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} 8637 */ handleWindowContentChangedEvent(AccessibilityEvent event)8638 private void handleWindowContentChangedEvent(AccessibilityEvent event) { 8639 final View focusedHost = mAccessibilityFocusedHost; 8640 if (focusedHost == null || mAccessibilityFocusedVirtualView == null) { 8641 // No virtual view focused, nothing to do here. 8642 return; 8643 } 8644 8645 final AccessibilityNodeProvider provider = focusedHost.getAccessibilityNodeProvider(); 8646 if (provider == null) { 8647 // Error state: virtual view with no provider. Clear focus. 8648 mAccessibilityFocusedHost = null; 8649 mAccessibilityFocusedVirtualView = null; 8650 focusedHost.clearAccessibilityFocusNoCallbacks(0); 8651 return; 8652 } 8653 8654 // We only care about change types that may affect the bounds of the 8655 // focused virtual view. 8656 final int changes = event.getContentChangeTypes(); 8657 if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) == 0 8658 && changes != AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) { 8659 return; 8660 } 8661 8662 final long eventSourceNodeId = event.getSourceNodeId(); 8663 final int changedViewId = AccessibilityNodeInfo.getAccessibilityViewId(eventSourceNodeId); 8664 8665 // Search up the tree for subtree containment. 8666 boolean hostInSubtree = false; 8667 View root = mAccessibilityFocusedHost; 8668 while (root != null && !hostInSubtree) { 8669 if (changedViewId == root.getAccessibilityViewId()) { 8670 hostInSubtree = true; 8671 } else { 8672 final ViewParent parent = root.getParent(); 8673 if (parent instanceof View) { 8674 root = (View) parent; 8675 } else { 8676 root = null; 8677 } 8678 } 8679 } 8680 8681 // We care only about changes in subtrees containing the host view. 8682 if (!hostInSubtree) { 8683 return; 8684 } 8685 8686 final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId(); 8687 int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId); 8688 8689 // Refresh the node for the focused virtual view. 8690 final Rect oldBounds = mTempRect; 8691 mAccessibilityFocusedVirtualView.getBoundsInScreen(oldBounds); 8692 mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId); 8693 if (mAccessibilityFocusedVirtualView == null) { 8694 // Error state: The node no longer exists. Clear focus. 8695 mAccessibilityFocusedHost = null; 8696 focusedHost.clearAccessibilityFocusNoCallbacks(0); 8697 8698 // This will probably fail, but try to keep the provider's internal 8699 // state consistent by clearing focus. 8700 provider.performAction(focusedChildId, 8701 AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), null); 8702 invalidateRectOnScreen(oldBounds); 8703 } else { 8704 // The node was refreshed, invalidate bounds if necessary. 8705 final Rect newBounds = mAccessibilityFocusedVirtualView.getBoundsInScreen(); 8706 if (!oldBounds.equals(newBounds)) { 8707 oldBounds.union(newBounds); 8708 invalidateRectOnScreen(oldBounds); 8709 } 8710 } 8711 } 8712 8713 @Override notifySubtreeAccessibilityStateChanged(View child, View source, int changeType)8714 public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) { 8715 postSendWindowContentChangedCallback(Preconditions.checkNotNull(source), changeType); 8716 } 8717 8718 @Override canResolveLayoutDirection()8719 public boolean canResolveLayoutDirection() { 8720 return true; 8721 } 8722 8723 @Override isLayoutDirectionResolved()8724 public boolean isLayoutDirectionResolved() { 8725 return true; 8726 } 8727 8728 @Override getLayoutDirection()8729 public int getLayoutDirection() { 8730 return View.LAYOUT_DIRECTION_RESOLVED_DEFAULT; 8731 } 8732 8733 @Override canResolveTextDirection()8734 public boolean canResolveTextDirection() { 8735 return true; 8736 } 8737 8738 @Override isTextDirectionResolved()8739 public boolean isTextDirectionResolved() { 8740 return true; 8741 } 8742 8743 @Override getTextDirection()8744 public int getTextDirection() { 8745 return View.TEXT_DIRECTION_RESOLVED_DEFAULT; 8746 } 8747 8748 @Override canResolveTextAlignment()8749 public boolean canResolveTextAlignment() { 8750 return true; 8751 } 8752 8753 @Override isTextAlignmentResolved()8754 public boolean isTextAlignmentResolved() { 8755 return true; 8756 } 8757 8758 @Override getTextAlignment()8759 public int getTextAlignment() { 8760 return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT; 8761 } 8762 getCommonPredecessor(View first, View second)8763 private View getCommonPredecessor(View first, View second) { 8764 if (mTempHashSet == null) { 8765 mTempHashSet = new HashSet<View>(); 8766 } 8767 HashSet<View> seen = mTempHashSet; 8768 seen.clear(); 8769 View firstCurrent = first; 8770 while (firstCurrent != null) { 8771 seen.add(firstCurrent); 8772 ViewParent firstCurrentParent = firstCurrent.mParent; 8773 if (firstCurrentParent instanceof View) { 8774 firstCurrent = (View) firstCurrentParent; 8775 } else { 8776 firstCurrent = null; 8777 } 8778 } 8779 View secondCurrent = second; 8780 while (secondCurrent != null) { 8781 if (seen.contains(secondCurrent)) { 8782 seen.clear(); 8783 return secondCurrent; 8784 } 8785 ViewParent secondCurrentParent = secondCurrent.mParent; 8786 if (secondCurrentParent instanceof View) { 8787 secondCurrent = (View) secondCurrentParent; 8788 } else { 8789 secondCurrent = null; 8790 } 8791 } 8792 seen.clear(); 8793 return null; 8794 } 8795 checkThread()8796 void checkThread() { 8797 if (mThread != Thread.currentThread()) { 8798 throw new CalledFromWrongThreadException( 8799 "Only the original thread that created a view hierarchy can touch its views."); 8800 } 8801 } 8802 8803 @Override requestDisallowInterceptTouchEvent(boolean disallowIntercept)8804 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 8805 // ViewAncestor never intercepts touch event, so this can be a no-op 8806 } 8807 8808 @Override requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)8809 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 8810 if (rectangle == null) { 8811 return scrollToRectOrFocus(null, immediate); 8812 } 8813 rectangle.offset(child.getLeft() - child.getScrollX(), 8814 child.getTop() - child.getScrollY()); 8815 final boolean scrolled = scrollToRectOrFocus(rectangle, immediate); 8816 mTempRect.set(rectangle); 8817 mTempRect.offset(0, -mCurScrollY); 8818 mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop); 8819 try { 8820 mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect); 8821 } catch (RemoteException re) { 8822 /* ignore */ 8823 } 8824 return scrolled; 8825 } 8826 8827 @Override childHasTransientStateChanged(View child, boolean hasTransientState)8828 public void childHasTransientStateChanged(View child, boolean hasTransientState) { 8829 // Do nothing. 8830 } 8831 8832 @Override onStartNestedScroll(View child, View target, int nestedScrollAxes)8833 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 8834 return false; 8835 } 8836 8837 @Override onStopNestedScroll(View target)8838 public void onStopNestedScroll(View target) { 8839 } 8840 8841 @Override onNestedScrollAccepted(View child, View target, int nestedScrollAxes)8842 public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { 8843 } 8844 8845 @Override onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)8846 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, 8847 int dxUnconsumed, int dyUnconsumed) { 8848 } 8849 8850 @Override onNestedPreScroll(View target, int dx, int dy, int[] consumed)8851 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 8852 } 8853 8854 @Override onNestedFling(View target, float velocityX, float velocityY, boolean consumed)8855 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 8856 return false; 8857 } 8858 8859 @Override onNestedPreFling(View target, float velocityX, float velocityY)8860 public boolean onNestedPreFling(View target, float velocityX, float velocityY) { 8861 return false; 8862 } 8863 8864 @Override onNestedPrePerformAccessibilityAction(View target, int action, Bundle args)8865 public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { 8866 return false; 8867 } 8868 8869 /** 8870 * Adds a scroll capture callback to this window. 8871 * 8872 * @param callback the callback to add 8873 */ addScrollCaptureCallback(ScrollCaptureCallback callback)8874 public void addScrollCaptureCallback(ScrollCaptureCallback callback) { 8875 if (mRootScrollCaptureCallbacks == null) { 8876 mRootScrollCaptureCallbacks = new HashSet<>(); 8877 } 8878 mRootScrollCaptureCallbacks.add(callback); 8879 } 8880 8881 /** 8882 * Removes a scroll capture callback from this window. 8883 * 8884 * @param callback the callback to remove 8885 */ removeScrollCaptureCallback(ScrollCaptureCallback callback)8886 public void removeScrollCaptureCallback(ScrollCaptureCallback callback) { 8887 if (mRootScrollCaptureCallbacks != null) { 8888 mRootScrollCaptureCallbacks.remove(callback); 8889 if (mRootScrollCaptureCallbacks.isEmpty()) { 8890 mRootScrollCaptureCallbacks = null; 8891 } 8892 } 8893 } 8894 8895 /** 8896 * Dispatches a scroll capture request to the view hierarchy on the ui thread. 8897 * 8898 * @param controller the controller to receive replies 8899 */ dispatchScrollCaptureRequest(@onNull IScrollCaptureController controller)8900 public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureController controller) { 8901 mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, controller).sendToTarget(); 8902 } 8903 8904 /** 8905 * Collect and include any ScrollCaptureCallback instances registered with the window. 8906 * 8907 * @see #addScrollCaptureCallback(ScrollCaptureCallback) 8908 * @param targets the search queue for targets 8909 */ collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets)8910 private void collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets) { 8911 for (ScrollCaptureCallback cb : mRootScrollCaptureCallbacks) { 8912 // Add to the list for consideration 8913 Point offset = new Point(mView.getLeft(), mView.getTop()); 8914 Rect rect = new Rect(0, 0, mView.getWidth(), mView.getHeight()); 8915 targets.add(new ScrollCaptureTarget(mView, rect, offset, cb)); 8916 } 8917 } 8918 8919 /** 8920 * Handles an inbound request for scroll capture from the system. If a client is not already 8921 * active, a search will be dispatched through the view tree to locate scrolling content. 8922 * <p> 8923 * Either {@link IScrollCaptureController#onClientConnected(IScrollCaptureClient, Rect, 8924 * Point)} or {@link IScrollCaptureController#onClientUnavailable()} will be returned 8925 * depending on the results of the search. 8926 * 8927 * @param controller the interface to the system controller 8928 * @see ScrollCaptureTargetResolver 8929 */ handleScrollCaptureRequest(@onNull IScrollCaptureController controller)8930 private void handleScrollCaptureRequest(@NonNull IScrollCaptureController controller) { 8931 LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); 8932 8933 // Window (root) level callbacks 8934 collectRootScrollCaptureTargets(targetList); 8935 8936 // Search through View-tree 8937 View rootView = getView(); 8938 Point point = new Point(); 8939 Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight()); 8940 getChildVisibleRect(rootView, rect, point); 8941 rootView.dispatchScrollCaptureSearch(rect, point, targetList); 8942 8943 // No-op path. Scroll capture not offered for this window. 8944 if (targetList.isEmpty()) { 8945 dispatchScrollCaptureSearchResult(controller, null); 8946 return; 8947 } 8948 8949 // Request scrollBounds from each of the targets. 8950 // Continues with the consumer once all responses are consumed, or the timeout expires. 8951 ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetList); 8952 resolver.start(mHandler, 1000, 8953 (selected) -> dispatchScrollCaptureSearchResult(controller, selected)); 8954 } 8955 8956 /** Called by {@link #handleScrollCaptureRequest} when a result is returned */ dispatchScrollCaptureSearchResult( @onNull IScrollCaptureController controller, @Nullable ScrollCaptureTarget selectedTarget)8957 private void dispatchScrollCaptureSearchResult( 8958 @NonNull IScrollCaptureController controller, 8959 @Nullable ScrollCaptureTarget selectedTarget) { 8960 8961 // If timeout or no eligible targets found. 8962 if (selectedTarget == null) { 8963 try { 8964 if (DEBUG_SCROLL_CAPTURE) { 8965 Log.d(TAG, "scrollCaptureSearch returned no targets available."); 8966 } 8967 controller.onClientUnavailable(); 8968 } catch (RemoteException e) { 8969 if (DEBUG_SCROLL_CAPTURE) { 8970 Log.w(TAG, "Failed to notify controller of scroll capture search result.", e); 8971 } 8972 } 8973 return; 8974 } 8975 8976 // Create a client instance and return it to the caller 8977 mScrollCaptureClient = new ScrollCaptureClient(selectedTarget, controller); 8978 try { 8979 if (DEBUG_SCROLL_CAPTURE) { 8980 Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureClient()); 8981 } 8982 controller.onClientConnected( 8983 mScrollCaptureClient, 8984 selectedTarget.getScrollBounds(), 8985 selectedTarget.getPositionInWindow()); 8986 } catch (RemoteException e) { 8987 if (DEBUG_SCROLL_CAPTURE) { 8988 Log.w(TAG, "Failed to notify controller of scroll capture search result.", e); 8989 } 8990 mScrollCaptureClient.disconnect(); 8991 mScrollCaptureClient = null; 8992 } 8993 } 8994 reportNextDraw()8995 private void reportNextDraw() { 8996 if (mReportNextDraw == false) { 8997 drawPending(); 8998 } 8999 mReportNextDraw = true; 9000 } 9001 9002 /** 9003 * Force the window to report its next draw. 9004 * <p> 9005 * This method is only supposed to be used to speed up the interaction from SystemUI and window 9006 * manager when waiting for the first frame to be drawn when turning on the screen. DO NOT USE 9007 * unless you fully understand this interaction. 9008 * @hide 9009 */ setReportNextDraw()9010 public void setReportNextDraw() { 9011 reportNextDraw(); 9012 invalidate(); 9013 } 9014 changeCanvasOpacity(boolean opaque)9015 void changeCanvasOpacity(boolean opaque) { 9016 Log.d(mTag, "changeCanvasOpacity: opaque=" + opaque); 9017 opaque = opaque & ((mView.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0); 9018 if (mAttachInfo.mThreadedRenderer != null) { 9019 mAttachInfo.mThreadedRenderer.setOpaque(opaque); 9020 } 9021 } 9022 9023 /** 9024 * Dispatches a KeyEvent to all registered key fallback handlers. 9025 * 9026 * @param event 9027 * @return {@code true} if the event was handled, {@code false} otherwise. 9028 */ dispatchUnhandledKeyEvent(KeyEvent event)9029 public boolean dispatchUnhandledKeyEvent(KeyEvent event) { 9030 return mUnhandledKeyManager.dispatch(mView, event); 9031 } 9032 9033 class TakenSurfaceHolder extends BaseSurfaceHolder { 9034 @Override onAllowLockCanvas()9035 public boolean onAllowLockCanvas() { 9036 return mDrawingAllowed; 9037 } 9038 9039 @Override onRelayoutContainer()9040 public void onRelayoutContainer() { 9041 // Not currently interesting -- from changing between fixed and layout size. 9042 } 9043 9044 @Override setFormat(int format)9045 public void setFormat(int format) { 9046 ((RootViewSurfaceTaker)mView).setSurfaceFormat(format); 9047 } 9048 9049 @Override setType(int type)9050 public void setType(int type) { 9051 ((RootViewSurfaceTaker)mView).setSurfaceType(type); 9052 } 9053 9054 @Override onUpdateSurface()9055 public void onUpdateSurface() { 9056 // We take care of format and type changes on our own. 9057 throw new IllegalStateException("Shouldn't be here"); 9058 } 9059 9060 @Override isCreating()9061 public boolean isCreating() { 9062 return mIsCreating; 9063 } 9064 9065 @Override setFixedSize(int width, int height)9066 public void setFixedSize(int width, int height) { 9067 throw new UnsupportedOperationException( 9068 "Currently only support sizing from layout"); 9069 } 9070 9071 @Override setKeepScreenOn(boolean screenOn)9072 public void setKeepScreenOn(boolean screenOn) { 9073 ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn); 9074 } 9075 } 9076 9077 static class W extends IWindow.Stub { 9078 private final WeakReference<ViewRootImpl> mViewAncestor; 9079 private final IWindowSession mWindowSession; 9080 W(ViewRootImpl viewAncestor)9081 W(ViewRootImpl viewAncestor) { 9082 mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); 9083 mWindowSession = viewAncestor.mWindowSession; 9084 } 9085 9086 @Override resized(Rect frame, Rect contentInsets, Rect visibleInsets, Rect stableInsets, boolean reportDraw, MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, DisplayCutout.ParcelableWrapper displayCutout)9087 public void resized(Rect frame, Rect contentInsets, 9088 Rect visibleInsets, Rect stableInsets, boolean reportDraw, 9089 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, 9090 boolean alwaysConsumeSystemBars, int displayId, 9091 DisplayCutout.ParcelableWrapper displayCutout) { 9092 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9093 if (viewAncestor != null) { 9094 viewAncestor.dispatchResized(frame, contentInsets, 9095 visibleInsets, stableInsets, reportDraw, mergedConfiguration, 9096 backDropFrame, forceLayout, alwaysConsumeSystemBars, displayId, 9097 displayCutout); 9098 } 9099 } 9100 9101 @Override locationInParentDisplayChanged(Point offset)9102 public void locationInParentDisplayChanged(Point offset) { 9103 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9104 if (viewAncestor != null) { 9105 viewAncestor.dispatchLocationInParentDisplayChanged(offset); 9106 } 9107 } 9108 9109 @Override insetsChanged(InsetsState insetsState)9110 public void insetsChanged(InsetsState insetsState) { 9111 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9112 if (viewAncestor != null) { 9113 viewAncestor.dispatchInsetsChanged(insetsState); 9114 } 9115 } 9116 9117 @Override insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)9118 public void insetsControlChanged(InsetsState insetsState, 9119 InsetsSourceControl[] activeControls) { 9120 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9121 if (viewAncestor != null) { 9122 viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls); 9123 } 9124 } 9125 9126 @Override showInsets(@nsetsType int types, boolean fromIme)9127 public void showInsets(@InsetsType int types, boolean fromIme) { 9128 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9129 if (viewAncestor != null) { 9130 viewAncestor.showInsets(types, fromIme); 9131 } 9132 } 9133 9134 @Override hideInsets(@nsetsType int types, boolean fromIme)9135 public void hideInsets(@InsetsType int types, boolean fromIme) { 9136 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9137 if (viewAncestor != null) { 9138 viewAncestor.hideInsets(types, fromIme); 9139 } 9140 } 9141 9142 @Override moved(int newX, int newY)9143 public void moved(int newX, int newY) { 9144 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9145 if (viewAncestor != null) { 9146 viewAncestor.dispatchMoved(newX, newY); 9147 } 9148 } 9149 9150 @Override dispatchAppVisibility(boolean visible)9151 public void dispatchAppVisibility(boolean visible) { 9152 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9153 if (viewAncestor != null) { 9154 viewAncestor.dispatchAppVisibility(visible); 9155 } 9156 } 9157 9158 @Override dispatchGetNewSurface()9159 public void dispatchGetNewSurface() { 9160 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9161 if (viewAncestor != null) { 9162 viewAncestor.dispatchGetNewSurface(); 9163 } 9164 } 9165 9166 @Override windowFocusChanged(boolean hasFocus, boolean inTouchMode)9167 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 9168 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9169 if (viewAncestor != null) { 9170 viewAncestor.windowFocusChanged(hasFocus, inTouchMode); 9171 } 9172 } 9173 checkCallingPermission(String permission)9174 private static int checkCallingPermission(String permission) { 9175 try { 9176 return ActivityManager.getService().checkPermission( 9177 permission, Binder.getCallingPid(), Binder.getCallingUid()); 9178 } catch (RemoteException e) { 9179 return PackageManager.PERMISSION_DENIED; 9180 } 9181 } 9182 9183 @Override executeCommand(String command, String parameters, ParcelFileDescriptor out)9184 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 9185 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9186 if (viewAncestor != null) { 9187 final View view = viewAncestor.mView; 9188 if (view != null) { 9189 if (checkCallingPermission(Manifest.permission.DUMP) != 9190 PackageManager.PERMISSION_GRANTED) { 9191 throw new SecurityException("Insufficient permissions to invoke" 9192 + " executeCommand() from pid=" + Binder.getCallingPid() 9193 + ", uid=" + Binder.getCallingUid()); 9194 } 9195 9196 OutputStream clientStream = null; 9197 try { 9198 clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out); 9199 ViewDebug.dispatchCommand(view, command, parameters, clientStream); 9200 } catch (IOException e) { 9201 e.printStackTrace(); 9202 } finally { 9203 if (clientStream != null) { 9204 try { 9205 clientStream.close(); 9206 } catch (IOException e) { 9207 e.printStackTrace(); 9208 } 9209 } 9210 } 9211 } 9212 } 9213 } 9214 9215 @Override closeSystemDialogs(String reason)9216 public void closeSystemDialogs(String reason) { 9217 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9218 if (viewAncestor != null) { 9219 viewAncestor.dispatchCloseSystemDialogs(reason); 9220 } 9221 } 9222 9223 @Override dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom, boolean sync)9224 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 9225 float zoom, boolean sync) { 9226 if (sync) { 9227 try { 9228 mWindowSession.wallpaperOffsetsComplete(asBinder()); 9229 } catch (RemoteException e) { 9230 } 9231 } 9232 } 9233 9234 @Override dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync)9235 public void dispatchWallpaperCommand(String action, int x, int y, 9236 int z, Bundle extras, boolean sync) { 9237 if (sync) { 9238 try { 9239 mWindowSession.wallpaperCommandComplete(asBinder(), null); 9240 } catch (RemoteException e) { 9241 } 9242 } 9243 } 9244 9245 /* Drag/drop */ 9246 @Override dispatchDragEvent(DragEvent event)9247 public void dispatchDragEvent(DragEvent event) { 9248 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9249 if (viewAncestor != null) { 9250 viewAncestor.dispatchDragEvent(event); 9251 } 9252 } 9253 9254 @Override updatePointerIcon(float x, float y)9255 public void updatePointerIcon(float x, float y) { 9256 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9257 if (viewAncestor != null) { 9258 viewAncestor.updatePointerIcon(x, y); 9259 } 9260 } 9261 9262 @Override dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue, int localChanges)9263 public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, 9264 int localValue, int localChanges) { 9265 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9266 if (viewAncestor != null) { 9267 viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility, 9268 localValue, localChanges); 9269 } 9270 } 9271 9272 @Override dispatchWindowShown()9273 public void dispatchWindowShown() { 9274 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9275 if (viewAncestor != null) { 9276 viewAncestor.dispatchWindowShown(); 9277 } 9278 } 9279 9280 @Override requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId)9281 public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) { 9282 ViewRootImpl viewAncestor = mViewAncestor.get(); 9283 if (viewAncestor != null) { 9284 viewAncestor.dispatchRequestKeyboardShortcuts(receiver, deviceId); 9285 } 9286 } 9287 9288 @Override dispatchPointerCaptureChanged(boolean hasCapture)9289 public void dispatchPointerCaptureChanged(boolean hasCapture) { 9290 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9291 if (viewAncestor != null) { 9292 viewAncestor.dispatchPointerCaptureChanged(hasCapture); 9293 } 9294 } 9295 9296 @Override requestScrollCapture(IScrollCaptureController controller)9297 public void requestScrollCapture(IScrollCaptureController controller) { 9298 final ViewRootImpl viewAncestor = mViewAncestor.get(); 9299 if (viewAncestor != null) { 9300 viewAncestor.dispatchScrollCaptureRequest(controller); 9301 } 9302 } 9303 } 9304 9305 public static final class CalledFromWrongThreadException extends AndroidRuntimeException { 9306 @UnsupportedAppUsage CalledFromWrongThreadException(String msg)9307 public CalledFromWrongThreadException(String msg) { 9308 super(msg); 9309 } 9310 } 9311 getRunQueue()9312 static HandlerActionQueue getRunQueue() { 9313 HandlerActionQueue rq = sRunQueues.get(); 9314 if (rq != null) { 9315 return rq; 9316 } 9317 rq = new HandlerActionQueue(); 9318 sRunQueues.set(rq); 9319 return rq; 9320 } 9321 9322 /** 9323 * Start a drag resizing which will inform all listeners that a window resize is taking place. 9324 */ startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode)9325 private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets, 9326 Rect stableInsets, int resizeMode) { 9327 if (!mDragResizing) { 9328 mDragResizing = true; 9329 if (mUseMTRenderer) { 9330 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 9331 mWindowCallbacks.get(i).onWindowDragResizeStart( 9332 initialBounds, fullscreen, systemInsets, stableInsets, resizeMode); 9333 } 9334 } 9335 mFullRedrawNeeded = true; 9336 } 9337 } 9338 9339 /** 9340 * End a drag resize which will inform all listeners that a window resize has ended. 9341 */ endDragResizing()9342 private void endDragResizing() { 9343 if (mDragResizing) { 9344 mDragResizing = false; 9345 if (mUseMTRenderer) { 9346 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 9347 mWindowCallbacks.get(i).onWindowDragResizeEnd(); 9348 } 9349 } 9350 mFullRedrawNeeded = true; 9351 } 9352 } 9353 updateContentDrawBounds()9354 private boolean updateContentDrawBounds() { 9355 boolean updated = false; 9356 if (mUseMTRenderer) { 9357 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 9358 updated |= 9359 mWindowCallbacks.get(i).onContentDrawn(mWindowAttributes.surfaceInsets.left, 9360 mWindowAttributes.surfaceInsets.top, mWidth, mHeight); 9361 } 9362 } 9363 return updated | (mDragResizing && mReportNextDraw); 9364 } 9365 requestDrawWindow()9366 private void requestDrawWindow() { 9367 if (!mUseMTRenderer) { 9368 return; 9369 } 9370 mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size()); 9371 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 9372 mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw); 9373 } 9374 } 9375 9376 /** 9377 * Tells this instance that its corresponding activity has just relaunched. In this case, we 9378 * need to force a relayout of the window to make sure we get the correct bounds from window 9379 * manager. 9380 */ reportActivityRelaunched()9381 public void reportActivityRelaunched() { 9382 mActivityRelaunched = true; 9383 } 9384 getSurfaceControl()9385 public SurfaceControl getSurfaceControl() { 9386 return mSurfaceControl; 9387 } 9388 9389 /** 9390 * @return Returns a token used to identify the windows input channel. 9391 */ getInputToken()9392 public IBinder getInputToken() { 9393 if (mInputEventReceiver == null) { 9394 return null; 9395 } 9396 return mInputEventReceiver.getToken(); 9397 } 9398 9399 @NonNull getWindowToken()9400 public IBinder getWindowToken() { 9401 return mAttachInfo.mWindowToken; 9402 } 9403 9404 /** 9405 * Class for managing the accessibility interaction connection 9406 * based on the global accessibility state. 9407 */ 9408 final class AccessibilityInteractionConnectionManager 9409 implements AccessibilityStateChangeListener { 9410 @Override onAccessibilityStateChanged(boolean enabled)9411 public void onAccessibilityStateChanged(boolean enabled) { 9412 if (enabled) { 9413 ensureConnection(); 9414 if (mAttachInfo.mHasWindowFocus && (mView != null)) { 9415 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 9416 View focusedView = mView.findFocus(); 9417 if (focusedView != null && focusedView != mView) { 9418 focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 9419 } 9420 } 9421 if (mAttachInfo.mLeashedParentToken != null) { 9422 mAccessibilityManager.associateEmbeddedHierarchy( 9423 mAttachInfo.mLeashedParentToken, mLeashToken); 9424 } 9425 } else { 9426 ensureNoConnection(); 9427 mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget(); 9428 } 9429 } 9430 ensureConnection()9431 public void ensureConnection() { 9432 final boolean registered = mAttachInfo.mAccessibilityWindowId 9433 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 9434 if (!registered) { 9435 mAttachInfo.mAccessibilityWindowId = 9436 mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, 9437 mLeashToken, 9438 mContext.getPackageName(), 9439 new AccessibilityInteractionConnection(ViewRootImpl.this)); 9440 } 9441 } 9442 ensureNoConnection()9443 public void ensureNoConnection() { 9444 final boolean registered = mAttachInfo.mAccessibilityWindowId 9445 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 9446 if (registered) { 9447 mAttachInfo.mAccessibilityWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 9448 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); 9449 } 9450 } 9451 } 9452 9453 final class HighContrastTextManager implements HighTextContrastChangeListener { HighContrastTextManager()9454 HighContrastTextManager() { 9455 ThreadedRenderer.setHighContrastText(mAccessibilityManager.isHighTextContrastEnabled()); 9456 } 9457 @Override onHighTextContrastStateChanged(boolean enabled)9458 public void onHighTextContrastStateChanged(boolean enabled) { 9459 ThreadedRenderer.setHighContrastText(enabled); 9460 9461 // Destroy Displaylists so they can be recreated with high contrast recordings 9462 destroyHardwareResources(); 9463 9464 // Schedule redraw, which will rerecord + redraw all text 9465 invalidate(); 9466 } 9467 } 9468 9469 /** 9470 * This class is an interface this ViewAncestor provides to the 9471 * AccessibilityManagerService to the latter can interact with 9472 * the view hierarchy in this ViewAncestor. 9473 */ 9474 static final class AccessibilityInteractionConnection 9475 extends IAccessibilityInteractionConnection.Stub { 9476 private final WeakReference<ViewRootImpl> mViewRootImpl; 9477 AccessibilityInteractionConnection(ViewRootImpl viewRootImpl)9478 AccessibilityInteractionConnection(ViewRootImpl viewRootImpl) { 9479 mViewRootImpl = new WeakReference<ViewRootImpl>(viewRootImpl); 9480 } 9481 9482 @Override findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args)9483 public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, 9484 Region interactiveRegion, int interactionId, 9485 IAccessibilityInteractionConnectionCallback callback, int flags, 9486 int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) { 9487 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9488 if (viewRootImpl != null && viewRootImpl.mView != null) { 9489 viewRootImpl.getAccessibilityInteractionController() 9490 .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, 9491 interactiveRegion, interactionId, callback, flags, interrogatingPid, 9492 interrogatingTid, spec, args); 9493 } else { 9494 // We cannot make the call and notify the caller so it does not wait. 9495 try { 9496 callback.setFindAccessibilityNodeInfosResult(null, interactionId); 9497 } catch (RemoteException re) { 9498 /* best effort - ignore */ 9499 } 9500 } 9501 } 9502 9503 @Override performAccessibilityAction(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid)9504 public void performAccessibilityAction(long accessibilityNodeId, int action, 9505 Bundle arguments, int interactionId, 9506 IAccessibilityInteractionConnectionCallback callback, int flags, 9507 int interrogatingPid, long interrogatingTid) { 9508 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9509 if (viewRootImpl != null && viewRootImpl.mView != null) { 9510 viewRootImpl.getAccessibilityInteractionController() 9511 .performAccessibilityActionClientThread(accessibilityNodeId, action, arguments, 9512 interactionId, callback, flags, interrogatingPid, interrogatingTid); 9513 } else { 9514 // We cannot make the call and notify the caller so it does not wait. 9515 try { 9516 callback.setPerformAccessibilityActionResult(false, interactionId); 9517 } catch (RemoteException re) { 9518 /* best effort - ignore */ 9519 } 9520 } 9521 } 9522 9523 @Override findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)9524 public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, 9525 String viewId, Region interactiveRegion, int interactionId, 9526 IAccessibilityInteractionConnectionCallback callback, int flags, 9527 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 9528 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9529 if (viewRootImpl != null && viewRootImpl.mView != null) { 9530 viewRootImpl.getAccessibilityInteractionController() 9531 .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId, 9532 viewId, interactiveRegion, interactionId, callback, flags, 9533 interrogatingPid, interrogatingTid, spec); 9534 } else { 9535 // We cannot make the call and notify the caller so it does not wait. 9536 try { 9537 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 9538 } catch (RemoteException re) { 9539 /* best effort - ignore */ 9540 } 9541 } 9542 } 9543 9544 @Override findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)9545 public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, 9546 Region interactiveRegion, int interactionId, 9547 IAccessibilityInteractionConnectionCallback callback, int flags, 9548 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 9549 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9550 if (viewRootImpl != null && viewRootImpl.mView != null) { 9551 viewRootImpl.getAccessibilityInteractionController() 9552 .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, 9553 interactiveRegion, interactionId, callback, flags, interrogatingPid, 9554 interrogatingTid, spec); 9555 } else { 9556 // We cannot make the call and notify the caller so it does not wait. 9557 try { 9558 callback.setFindAccessibilityNodeInfosResult(null, interactionId); 9559 } catch (RemoteException re) { 9560 /* best effort - ignore */ 9561 } 9562 } 9563 } 9564 9565 @Override findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)9566 public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, 9567 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, 9568 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 9569 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9570 if (viewRootImpl != null && viewRootImpl.mView != null) { 9571 viewRootImpl.getAccessibilityInteractionController() 9572 .findFocusClientThread(accessibilityNodeId, focusType, interactiveRegion, 9573 interactionId, callback, flags, interrogatingPid, interrogatingTid, 9574 spec); 9575 } else { 9576 // We cannot make the call and notify the caller so it does not wait. 9577 try { 9578 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 9579 } catch (RemoteException re) { 9580 /* best effort - ignore */ 9581 } 9582 } 9583 } 9584 9585 @Override focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)9586 public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, 9587 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, 9588 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 9589 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9590 if (viewRootImpl != null && viewRootImpl.mView != null) { 9591 viewRootImpl.getAccessibilityInteractionController() 9592 .focusSearchClientThread(accessibilityNodeId, direction, interactiveRegion, 9593 interactionId, callback, flags, interrogatingPid, interrogatingTid, 9594 spec); 9595 } else { 9596 // We cannot make the call and notify the caller so it does not wait. 9597 try { 9598 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 9599 } catch (RemoteException re) { 9600 /* best effort - ignore */ 9601 } 9602 } 9603 } 9604 9605 @Override clearAccessibilityFocus()9606 public void clearAccessibilityFocus() { 9607 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9608 if (viewRootImpl != null && viewRootImpl.mView != null) { 9609 viewRootImpl.getAccessibilityInteractionController() 9610 .clearAccessibilityFocusClientThread(); 9611 } 9612 } 9613 9614 @Override notifyOutsideTouch()9615 public void notifyOutsideTouch() { 9616 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 9617 if (viewRootImpl != null && viewRootImpl.mView != null) { 9618 viewRootImpl.getAccessibilityInteractionController() 9619 .notifyOutsideTouchClientThread(); 9620 } 9621 } 9622 } 9623 9624 /** 9625 * Gets an accessibility embedded connection interface for this ViewRootImpl. 9626 * @hide 9627 */ getAccessibilityEmbeddedConnection()9628 public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() { 9629 if (mAccessibilityEmbeddedConnection == null) { 9630 mAccessibilityEmbeddedConnection = new AccessibilityEmbeddedConnection( 9631 ViewRootImpl.this); 9632 } 9633 return mAccessibilityEmbeddedConnection; 9634 } 9635 9636 private class SendWindowContentChangedAccessibilityEvent implements Runnable { 9637 private int mChangeTypes = 0; 9638 9639 public View mSource; 9640 public long mLastEventTimeMillis; 9641 /** 9642 * Override for {@link AccessibilityEvent#originStackTrace} to provide the stack trace 9643 * of the original {@link #runOrPost} call instead of one for sending the delayed event 9644 * from a looper. 9645 */ 9646 public StackTraceElement[] mOrigin; 9647 9648 @Override run()9649 public void run() { 9650 // Protect against re-entrant code and attempt to do the right thing in the case that 9651 // we're multithreaded. 9652 View source = mSource; 9653 mSource = null; 9654 if (source == null) { 9655 Log.e(TAG, "Accessibility content change has no source"); 9656 return; 9657 } 9658 // The accessibility may be turned off while we were waiting so check again. 9659 if (AccessibilityManager.getInstance(mContext).isEnabled()) { 9660 mLastEventTimeMillis = SystemClock.uptimeMillis(); 9661 AccessibilityEvent event = AccessibilityEvent.obtain(); 9662 event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 9663 event.setContentChangeTypes(mChangeTypes); 9664 if (AccessibilityEvent.DEBUG_ORIGIN) event.originStackTrace = mOrigin; 9665 source.sendAccessibilityEventUnchecked(event); 9666 } else { 9667 mLastEventTimeMillis = 0; 9668 } 9669 // In any case reset to initial state. 9670 source.resetSubtreeAccessibilityStateChanged(); 9671 mChangeTypes = 0; 9672 if (AccessibilityEvent.DEBUG_ORIGIN) mOrigin = null; 9673 } 9674 runOrPost(View source, int changeType)9675 public void runOrPost(View source, int changeType) { 9676 if (mHandler.getLooper() != Looper.myLooper()) { 9677 CalledFromWrongThreadException e = new CalledFromWrongThreadException("Only the " 9678 + "original thread that created a view hierarchy can touch its views."); 9679 // TODO: Throw the exception 9680 Log.e(TAG, "Accessibility content change on non-UI thread. Future Android " 9681 + "versions will throw an exception.", e); 9682 // Attempt to recover. This code does not eliminate the thread safety issue, but 9683 // it should force any issues to happen near the above log. 9684 mHandler.removeCallbacks(this); 9685 if (mSource != null) { 9686 // Dispatch whatever was pending. It's still possible that the runnable started 9687 // just before we removed the callbacks, and bad things will happen, but at 9688 // least they should happen very close to the logged error. 9689 run(); 9690 } 9691 } 9692 if (mSource != null) { 9693 // If there is no common predecessor, then mSource points to 9694 // a removed view, hence in this case always prefer the source. 9695 View predecessor = getCommonPredecessor(mSource, source); 9696 if (predecessor != null) { 9697 predecessor = predecessor.getSelfOrParentImportantForA11y(); 9698 } 9699 mSource = (predecessor != null) ? predecessor : source; 9700 mChangeTypes |= changeType; 9701 return; 9702 } 9703 mSource = source; 9704 mChangeTypes = changeType; 9705 if (AccessibilityEvent.DEBUG_ORIGIN) { 9706 mOrigin = Thread.currentThread().getStackTrace(); 9707 } 9708 final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis; 9709 final long minEventIntevalMillis = 9710 ViewConfiguration.getSendRecurringAccessibilityEventsInterval(); 9711 if (timeSinceLastMillis >= minEventIntevalMillis) { 9712 removeCallbacksAndRun(); 9713 } else { 9714 mHandler.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis); 9715 } 9716 } 9717 removeCallbacksAndRun()9718 public void removeCallbacksAndRun() { 9719 mHandler.removeCallbacks(this); 9720 run(); 9721 } 9722 } 9723 9724 private static class UnhandledKeyManager { 9725 // This is used to ensure that unhandled events are only dispatched once. We attempt 9726 // to dispatch more than once in order to achieve a certain order. Specifically, if we 9727 // are in an Activity or Dialog (and have a Window.Callback), the unhandled events should 9728 // be dispatched after the view hierarchy, but before the Callback. However, if we aren't 9729 // in an activity, we still want unhandled keys to be dispatched. 9730 private boolean mDispatched = true; 9731 9732 // Keeps track of which Views have unhandled key focus for which keys. This doesn't 9733 // include modifiers. 9734 private final SparseArray<WeakReference<View>> mCapturedKeys = new SparseArray<>(); 9735 9736 // The current receiver. This value is transient and used between the pre-dispatch and 9737 // pre-view phase to ensure that other input-stages don't interfere with tracking. 9738 private WeakReference<View> mCurrentReceiver = null; 9739 dispatch(View root, KeyEvent event)9740 boolean dispatch(View root, KeyEvent event) { 9741 if (mDispatched) { 9742 return false; 9743 } 9744 View consumer; 9745 try { 9746 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "UnhandledKeyEvent dispatch"); 9747 mDispatched = true; 9748 9749 consumer = root.dispatchUnhandledKeyEvent(event); 9750 9751 // If an unhandled listener handles one, then keep track of it so that the 9752 // consuming view is first to receive its repeats and release as well. 9753 if (event.getAction() == KeyEvent.ACTION_DOWN) { 9754 int keycode = event.getKeyCode(); 9755 if (consumer != null && !KeyEvent.isModifierKey(keycode)) { 9756 mCapturedKeys.put(keycode, new WeakReference<>(consumer)); 9757 } 9758 } 9759 } finally { 9760 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 9761 } 9762 return consumer != null; 9763 } 9764 9765 /** 9766 * Called before the event gets dispatched to anything 9767 */ preDispatch(KeyEvent event)9768 void preDispatch(KeyEvent event) { 9769 // Always clean-up 'up' events since it's possible for earlier dispatch stages to 9770 // consume them without consuming the corresponding 'down' event. 9771 mCurrentReceiver = null; 9772 if (event.getAction() == KeyEvent.ACTION_UP) { 9773 int idx = mCapturedKeys.indexOfKey(event.getKeyCode()); 9774 if (idx >= 0) { 9775 mCurrentReceiver = mCapturedKeys.valueAt(idx); 9776 mCapturedKeys.removeAt(idx); 9777 } 9778 } 9779 } 9780 9781 /** 9782 * Called before the event gets dispatched to the view hierarchy 9783 * @return {@code true} if an unhandled handler has focus and consumed the event 9784 */ preViewDispatch(KeyEvent event)9785 boolean preViewDispatch(KeyEvent event) { 9786 mDispatched = false; 9787 if (mCurrentReceiver == null) { 9788 mCurrentReceiver = mCapturedKeys.get(event.getKeyCode()); 9789 } 9790 if (mCurrentReceiver != null) { 9791 View target = mCurrentReceiver.get(); 9792 if (event.getAction() == KeyEvent.ACTION_UP) { 9793 mCurrentReceiver = null; 9794 } 9795 if (target != null && target.isAttachedToWindow()) { 9796 target.onUnhandledKeyEvent(event); 9797 } 9798 // consume anyways so that we don't feed uncaptured key events to other views 9799 return true; 9800 } 9801 return false; 9802 } 9803 } 9804 setUseBLASTSyncTransaction()9805 void setUseBLASTSyncTransaction() { 9806 mNextDrawUseBLASTSyncTransaction = true; 9807 } 9808 finishBLASTSync(boolean apply)9809 private void finishBLASTSync(boolean apply) { 9810 mSendNextFrameToWm = false; 9811 if (mNextReportConsumeBLAST) { 9812 mNextReportConsumeBLAST = false; 9813 9814 if (apply) { 9815 mRtBLASTSyncTransaction.apply(); 9816 } else { 9817 mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction); 9818 } 9819 } 9820 } 9821 getBLASTSyncTransaction()9822 SurfaceControl.Transaction getBLASTSyncTransaction() { 9823 return mRtBLASTSyncTransaction; 9824 } 9825 9826 /** 9827 * @hide 9828 */ getRenderSurfaceControl()9829 public SurfaceControl getRenderSurfaceControl() { 9830 if (useBLAST()) { 9831 return mBlastSurfaceControl; 9832 } else { 9833 return mSurfaceControl; 9834 } 9835 } 9836 9837 @Override onDescendantUnbufferedRequested()9838 public void onDescendantUnbufferedRequested() { 9839 mUnbufferedInputSource = mView.mUnbufferedInputSource; 9840 } 9841 9842 /** 9843 * Force disabling use of the BLAST adapter regardless of the system 9844 * flag. Needs to be called before addView. 9845 */ forceDisableBLAST()9846 void forceDisableBLAST() { 9847 mForceDisableBLAST = true; 9848 } 9849 useBLAST()9850 boolean useBLAST() { 9851 return mUseBLASTAdapter && !mForceDisableBLAST; 9852 } 9853 9854 /** 9855 * Returns true if we are about to or currently processing a draw directed 9856 * in to a BLAST transaction. 9857 */ isDrawingToBLASTTransaction()9858 boolean isDrawingToBLASTTransaction() { 9859 return mNextReportConsumeBLAST; 9860 } 9861 } 9862