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