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