ViewRootImpl.java revision 5b02c629bd8da951331e5e464fe78e03b3393a72
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 17package android.view; 18 19import static android.view.Display.INVALID_DISPLAY; 20import static android.view.View.PFLAG_DRAW_ANIMATION; 21import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; 22import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; 23import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 24import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; 25import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 26import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; 27import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; 28 29import android.Manifest; 30import android.animation.LayoutTransition; 31import android.annotation.NonNull; 32import android.annotation.Nullable; 33import android.app.ActivityManager; 34import android.app.ActivityThread; 35import android.app.ResourcesManager; 36import android.content.ClipData; 37import android.content.ClipDescription; 38import android.content.Context; 39import android.content.pm.ActivityInfo; 40import android.content.pm.PackageManager; 41import android.content.res.CompatibilityInfo; 42import android.content.res.Configuration; 43import android.content.res.Resources; 44import android.graphics.Canvas; 45import android.graphics.Color; 46import android.graphics.Matrix; 47import android.graphics.PixelFormat; 48import android.graphics.Point; 49import android.graphics.PointF; 50import android.graphics.PorterDuff; 51import android.graphics.Rect; 52import android.graphics.Region; 53import android.graphics.drawable.AnimatedVectorDrawable; 54import android.graphics.drawable.Drawable; 55import android.hardware.display.DisplayManager; 56import android.hardware.display.DisplayManager.DisplayListener; 57import android.hardware.input.InputManager; 58import android.media.AudioManager; 59import android.os.Binder; 60import android.os.Build; 61import android.os.Bundle; 62import android.os.Debug; 63import android.os.Handler; 64import android.os.Looper; 65import android.os.Message; 66import android.os.ParcelFileDescriptor; 67import android.os.Process; 68import android.os.RemoteException; 69import android.os.SystemClock; 70import android.os.SystemProperties; 71import android.os.Trace; 72import android.util.AndroidRuntimeException; 73import android.util.DisplayMetrics; 74import android.util.Log; 75import android.util.LongArray; 76import android.util.MergedConfiguration; 77import android.util.Slog; 78import android.util.SparseArray; 79import android.util.TimeUtils; 80import android.util.TypedValue; 81import android.view.Surface.OutOfResourcesException; 82import android.view.ThreadedRenderer.FrameDrawingCallback; 83import android.view.View.AttachInfo; 84import android.view.View.FocusDirection; 85import android.view.View.MeasureSpec; 86import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 87import android.view.accessibility.AccessibilityEvent; 88import android.view.accessibility.AccessibilityManager; 89import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; 90import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener; 91import android.view.accessibility.AccessibilityNodeInfo; 92import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 93import android.view.accessibility.AccessibilityNodeProvider; 94import android.view.accessibility.AccessibilityWindowInfo; 95import android.view.accessibility.IAccessibilityInteractionConnection; 96import android.view.accessibility.IAccessibilityInteractionConnectionCallback; 97import android.view.animation.AccelerateDecelerateInterpolator; 98import android.view.animation.Interpolator; 99import android.view.autofill.AutofillManager; 100import android.view.inputmethod.InputMethodManager; 101import android.widget.Scroller; 102 103import com.android.internal.R; 104import com.android.internal.annotations.GuardedBy; 105import com.android.internal.os.IResultReceiver; 106import com.android.internal.os.SomeArgs; 107import com.android.internal.policy.PhoneFallbackEventHandler; 108import com.android.internal.util.Preconditions; 109import com.android.internal.view.BaseSurfaceHolder; 110import com.android.internal.view.RootViewSurfaceTaker; 111import com.android.internal.view.SurfaceCallbackHelper; 112 113import java.io.FileDescriptor; 114import java.io.IOException; 115import java.io.OutputStream; 116import java.io.PrintWriter; 117import java.lang.ref.WeakReference; 118import java.util.ArrayList; 119import java.util.HashSet; 120import java.util.LinkedList; 121import java.util.Queue; 122import 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"}) 132public 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. */ 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 */ 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 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 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 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 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 1671 private int dipToPx(int dip) { 1672 final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); 1673 return (int) (displayMetrics.density * dip + 0.5f); 1674 } 1675 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 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 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 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 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 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 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 */ 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 */ 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 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 */ 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 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 3065 void drawPending() { 3066 mDrawsNeededToReport++; 3067 } 3068 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 3079 private void postDrawFinished() { 3080 mHandler.sendEmptyMessage(MSG_DRAW_FINISHED); 3081 } 3082 3083 private void reportDrawFinished() { 3084 try { 3085 mDrawsNeededToReport = 0; 3086 mWindowSession.finishDrawing(mWindow); 3087 } catch (RemoteException e) { 3088 // Have fun! 3089 } 3090 } 3091 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 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 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, mNextRtFrameCallback); 3309 mNextRtFrameCallback = null; 3310 } else { 3311 // If we get here with a disabled & requested hardware renderer, something went 3312 // wrong (an invalidate posted right before we destroyed the hardware surface 3313 // for instance) so we should just bail out. Locking the surface with software 3314 // rendering at this point would lock it forever and prevent hardware renderer 3315 // from doing its job when it comes back. 3316 // Before we request a new frame we must however attempt to reinitiliaze the 3317 // hardware renderer if it's in requested state. This would happen after an 3318 // eglTerminate() for instance. 3319 if (mAttachInfo.mThreadedRenderer != null && 3320 !mAttachInfo.mThreadedRenderer.isEnabled() && 3321 mAttachInfo.mThreadedRenderer.isRequested() && 3322 mSurface.isValid()) { 3323 3324 try { 3325 mAttachInfo.mThreadedRenderer.initializeIfNeeded( 3326 mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); 3327 } catch (OutOfResourcesException e) { 3328 handleOutOfResourcesException(e); 3329 return false; 3330 } 3331 3332 mFullRedrawNeeded = true; 3333 scheduleTraversals(); 3334 return false; 3335 } 3336 3337 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, 3338 scalingRequired, dirty, surfaceInsets)) { 3339 return false; 3340 } 3341 } 3342 } 3343 3344 if (animating) { 3345 mFullRedrawNeeded = true; 3346 scheduleTraversals(); 3347 } 3348 return useAsyncReport; 3349 } 3350 3351 /** 3352 * @return true if drawing was successful, false if an error occurred 3353 */ 3354 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, 3355 boolean scalingRequired, Rect dirty, Rect surfaceInsets) { 3356 3357 // Draw with software renderer. 3358 final Canvas canvas; 3359 3360 // We already have the offset of surfaceInsets in xoff, yoff and dirty region, 3361 // therefore we need to add it back when moving the dirty region. 3362 int dirtyXOffset = xoff; 3363 int dirtyYOffset = yoff; 3364 if (surfaceInsets != null) { 3365 dirtyXOffset += surfaceInsets.left; 3366 dirtyYOffset += surfaceInsets.top; 3367 } 3368 3369 try { 3370 dirty.offset(-dirtyXOffset, -dirtyYOffset); 3371 final int left = dirty.left; 3372 final int top = dirty.top; 3373 final int right = dirty.right; 3374 final int bottom = dirty.bottom; 3375 3376 canvas = mSurface.lockCanvas(dirty); 3377 3378 // The dirty rectangle can be modified by Surface.lockCanvas() 3379 //noinspection ConstantConditions 3380 if (left != dirty.left || top != dirty.top || right != dirty.right 3381 || bottom != dirty.bottom) { 3382 attachInfo.mIgnoreDirtyState = true; 3383 } 3384 3385 // TODO: Do this in native 3386 canvas.setDensity(mDensity); 3387 } catch (Surface.OutOfResourcesException e) { 3388 handleOutOfResourcesException(e); 3389 return false; 3390 } catch (IllegalArgumentException e) { 3391 Log.e(mTag, "Could not lock surface", e); 3392 // Don't assume this is due to out of memory, it could be 3393 // something else, and if it is something else then we could 3394 // kill stuff (or ourself) for no reason. 3395 mLayoutRequested = true; // ask wm for a new surface next time. 3396 return false; 3397 } finally { 3398 dirty.offset(dirtyXOffset, dirtyYOffset); // Reset to the original value. 3399 } 3400 3401 try { 3402 if (DEBUG_ORIENTATION || DEBUG_DRAW) { 3403 Log.v(mTag, "Surface " + surface + " drawing to bitmap w=" 3404 + canvas.getWidth() + ", h=" + canvas.getHeight()); 3405 //canvas.drawARGB(255, 255, 0, 0); 3406 } 3407 3408 // If this bitmap's format includes an alpha channel, we 3409 // need to clear it before drawing so that the child will 3410 // properly re-composite its drawing on a transparent 3411 // background. This automatically respects the clip/dirty region 3412 // or 3413 // If we are applying an offset, we need to clear the area 3414 // where the offset doesn't appear to avoid having garbage 3415 // left in the blank areas. 3416 if (!canvas.isOpaque() || yoff != 0 || xoff != 0) { 3417 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 3418 } 3419 3420 dirty.setEmpty(); 3421 mIsAnimating = false; 3422 mView.mPrivateFlags |= View.PFLAG_DRAWN; 3423 3424 if (DEBUG_DRAW) { 3425 Context cxt = mView.getContext(); 3426 Log.i(mTag, "Drawing: package:" + cxt.getPackageName() + 3427 ", metrics=" + cxt.getResources().getDisplayMetrics() + 3428 ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo()); 3429 } 3430 try { 3431 canvas.translate(-xoff, -yoff); 3432 if (mTranslator != null) { 3433 mTranslator.translateCanvas(canvas); 3434 } 3435 canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0); 3436 attachInfo.mSetIgnoreDirtyState = false; 3437 3438 mView.draw(canvas); 3439 3440 drawAccessibilityFocusedDrawableIfNeeded(canvas); 3441 } finally { 3442 if (!attachInfo.mSetIgnoreDirtyState) { 3443 // Only clear the flag if it was not set during the mView.draw() call 3444 attachInfo.mIgnoreDirtyState = false; 3445 } 3446 } 3447 } finally { 3448 try { 3449 surface.unlockCanvasAndPost(canvas); 3450 } catch (IllegalArgumentException e) { 3451 Log.e(mTag, "Could not unlock surface", e); 3452 mLayoutRequested = true; // ask wm for a new surface next time. 3453 //noinspection ReturnInsideFinallyBlock 3454 return false; 3455 } 3456 3457 if (LOCAL_LOGV) { 3458 Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost"); 3459 } 3460 } 3461 return true; 3462 } 3463 3464 /** 3465 * We want to draw a highlight around the current accessibility focused. 3466 * Since adding a style for all possible view is not a viable option we 3467 * have this specialized drawing method. 3468 * 3469 * Note: We are doing this here to be able to draw the highlight for 3470 * virtual views in addition to real ones. 3471 * 3472 * @param canvas The canvas on which to draw. 3473 */ 3474 private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) { 3475 final Rect bounds = mAttachInfo.mTmpInvalRect; 3476 if (getAccessibilityFocusedRect(bounds)) { 3477 final Drawable drawable = getAccessibilityFocusedDrawable(); 3478 if (drawable != null) { 3479 drawable.setBounds(bounds); 3480 drawable.draw(canvas); 3481 } 3482 } else if (mAttachInfo.mAccessibilityFocusDrawable != null) { 3483 mAttachInfo.mAccessibilityFocusDrawable.setBounds(0, 0, 0, 0); 3484 } 3485 } 3486 3487 private boolean getAccessibilityFocusedRect(Rect bounds) { 3488 final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext); 3489 if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) { 3490 return false; 3491 } 3492 3493 final View host = mAccessibilityFocusedHost; 3494 if (host == null || host.mAttachInfo == null) { 3495 return false; 3496 } 3497 3498 final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); 3499 if (provider == null) { 3500 host.getBoundsOnScreen(bounds, true); 3501 } else if (mAccessibilityFocusedVirtualView != null) { 3502 mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds); 3503 } else { 3504 return false; 3505 } 3506 3507 // Transform the rect into window-relative coordinates. 3508 final AttachInfo attachInfo = mAttachInfo; 3509 bounds.offset(0, attachInfo.mViewRootImpl.mScrollY); 3510 bounds.offset(-attachInfo.mWindowLeft, -attachInfo.mWindowTop); 3511 if (!bounds.intersect(0, 0, attachInfo.mViewRootImpl.mWidth, 3512 attachInfo.mViewRootImpl.mHeight)) { 3513 // If no intersection, set bounds to empty. 3514 bounds.setEmpty(); 3515 } 3516 return !bounds.isEmpty(); 3517 } 3518 3519 private Drawable getAccessibilityFocusedDrawable() { 3520 // Lazily load the accessibility focus drawable. 3521 if (mAttachInfo.mAccessibilityFocusDrawable == null) { 3522 final TypedValue value = new TypedValue(); 3523 final boolean resolved = mView.mContext.getTheme().resolveAttribute( 3524 R.attr.accessibilityFocusedDrawable, value, true); 3525 if (resolved) { 3526 mAttachInfo.mAccessibilityFocusDrawable = 3527 mView.mContext.getDrawable(value.resourceId); 3528 } 3529 } 3530 return mAttachInfo.mAccessibilityFocusDrawable; 3531 } 3532 3533 /** 3534 * Requests that the root render node is invalidated next time we perform a draw, such that 3535 * {@link WindowCallbacks#onPostDraw} gets called. 3536 */ 3537 public void requestInvalidateRootRenderNode() { 3538 mInvalidateRootRequested = true; 3539 } 3540 3541 boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) { 3542 final Rect ci = mAttachInfo.mContentInsets; 3543 final Rect vi = mAttachInfo.mVisibleInsets; 3544 int scrollY = 0; 3545 boolean handled = false; 3546 3547 if (vi.left > ci.left || vi.top > ci.top 3548 || vi.right > ci.right || vi.bottom > ci.bottom) { 3549 // We'll assume that we aren't going to change the scroll 3550 // offset, since we want to avoid that unless it is actually 3551 // going to make the focus visible... otherwise we scroll 3552 // all over the place. 3553 scrollY = mScrollY; 3554 // We can be called for two different situations: during a draw, 3555 // to update the scroll position if the focus has changed (in which 3556 // case 'rectangle' is null), or in response to a 3557 // requestChildRectangleOnScreen() call (in which case 'rectangle' 3558 // is non-null and we just want to scroll to whatever that 3559 // rectangle is). 3560 final View focus = mView.findFocus(); 3561 if (focus == null) { 3562 return false; 3563 } 3564 View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null; 3565 if (focus != lastScrolledFocus) { 3566 // If the focus has changed, then ignore any requests to scroll 3567 // to a rectangle; first we want to make sure the entire focus 3568 // view is visible. 3569 rectangle = null; 3570 } 3571 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Eval scroll: focus=" + focus 3572 + " rectangle=" + rectangle + " ci=" + ci 3573 + " vi=" + vi); 3574 if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) { 3575 // Optimization: if the focus hasn't changed since last 3576 // time, and no layout has happened, then just leave things 3577 // as they are. 3578 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Keeping scroll y=" 3579 + mScrollY + " vi=" + vi.toShortString()); 3580 } else { 3581 // We need to determine if the currently focused view is 3582 // within the visible part of the window and, if not, apply 3583 // a pan so it can be seen. 3584 mLastScrolledFocus = new WeakReference<View>(focus); 3585 mScrollMayChange = false; 3586 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Need to scroll?"); 3587 // Try to find the rectangle from the focus view. 3588 if (focus.getGlobalVisibleRect(mVisRect, null)) { 3589 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Root w=" 3590 + mView.getWidth() + " h=" + mView.getHeight() 3591 + " ci=" + ci.toShortString() 3592 + " vi=" + vi.toShortString()); 3593 if (rectangle == null) { 3594 focus.getFocusedRect(mTempRect); 3595 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Focus " + focus 3596 + ": focusRect=" + mTempRect.toShortString()); 3597 if (mView instanceof ViewGroup) { 3598 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 3599 focus, mTempRect); 3600 } 3601 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 3602 "Focus in window: focusRect=" 3603 + mTempRect.toShortString() 3604 + " visRect=" + mVisRect.toShortString()); 3605 } else { 3606 mTempRect.set(rectangle); 3607 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 3608 "Request scroll to rect: " 3609 + mTempRect.toShortString() 3610 + " visRect=" + mVisRect.toShortString()); 3611 } 3612 if (mTempRect.intersect(mVisRect)) { 3613 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 3614 "Focus window visible rect: " 3615 + mTempRect.toShortString()); 3616 if (mTempRect.height() > 3617 (mView.getHeight()-vi.top-vi.bottom)) { 3618 // If the focus simply is not going to fit, then 3619 // best is probably just to leave things as-is. 3620 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 3621 "Too tall; leaving scrollY=" + scrollY); 3622 } 3623 // Next, check whether top or bottom is covered based on the non-scrolled 3624 // position, and calculate new scrollY (or set it to 0). 3625 // We can't keep using mScrollY here. For example mScrollY is non-zero 3626 // due to IME, then IME goes away. The current value of mScrollY leaves top 3627 // and bottom both visible, but we still need to scroll it back to 0. 3628 else if (mTempRect.top < vi.top) { 3629 scrollY = mTempRect.top - vi.top; 3630 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 3631 "Top covered; scrollY=" + scrollY); 3632 } else if (mTempRect.bottom > (mView.getHeight()-vi.bottom)) { 3633 scrollY = mTempRect.bottom - (mView.getHeight()-vi.bottom); 3634 if (DEBUG_INPUT_RESIZE) Log.v(mTag, 3635 "Bottom covered; scrollY=" + scrollY); 3636 } else { 3637 scrollY = 0; 3638 } 3639 handled = true; 3640 } 3641 } 3642 } 3643 } 3644 3645 if (scrollY != mScrollY) { 3646 if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Pan scroll changed: old=" 3647 + mScrollY + " , new=" + scrollY); 3648 if (!immediate) { 3649 if (mScroller == null) { 3650 mScroller = new Scroller(mView.getContext()); 3651 } 3652 mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY); 3653 } else if (mScroller != null) { 3654 mScroller.abortAnimation(); 3655 } 3656 mScrollY = scrollY; 3657 } 3658 3659 return handled; 3660 } 3661 3662 /** 3663 * @hide 3664 */ 3665 public View getAccessibilityFocusedHost() { 3666 return mAccessibilityFocusedHost; 3667 } 3668 3669 /** 3670 * @hide 3671 */ 3672 public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() { 3673 return mAccessibilityFocusedVirtualView; 3674 } 3675 3676 void setAccessibilityFocus(View view, AccessibilityNodeInfo node) { 3677 // If we have a virtual view with accessibility focus we need 3678 // to clear the focus and invalidate the virtual view bounds. 3679 if (mAccessibilityFocusedVirtualView != null) { 3680 3681 AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView; 3682 View focusHost = mAccessibilityFocusedHost; 3683 3684 // Wipe the state of the current accessibility focus since 3685 // the call into the provider to clear accessibility focus 3686 // will fire an accessibility event which will end up calling 3687 // this method and we want to have clean state when this 3688 // invocation happens. 3689 mAccessibilityFocusedHost = null; 3690 mAccessibilityFocusedVirtualView = null; 3691 3692 // Clear accessibility focus on the host after clearing state since 3693 // this method may be reentrant. 3694 focusHost.clearAccessibilityFocusNoCallbacks( 3695 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 3696 3697 AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider(); 3698 if (provider != null) { 3699 // Invalidate the area of the cleared accessibility focus. 3700 focusNode.getBoundsInParent(mTempRect); 3701 focusHost.invalidate(mTempRect); 3702 // Clear accessibility focus in the virtual node. 3703 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( 3704 focusNode.getSourceNodeId()); 3705 provider.performAction(virtualNodeId, 3706 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); 3707 } 3708 focusNode.recycle(); 3709 } 3710 if ((mAccessibilityFocusedHost != null) && (mAccessibilityFocusedHost != view)) { 3711 // Clear accessibility focus in the view. 3712 mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks( 3713 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 3714 } 3715 3716 // Set the new focus host and node. 3717 mAccessibilityFocusedHost = view; 3718 mAccessibilityFocusedVirtualView = node; 3719 3720 if (mAttachInfo.mThreadedRenderer != null) { 3721 mAttachInfo.mThreadedRenderer.invalidateRoot(); 3722 } 3723 } 3724 3725 boolean hasPointerCapture() { 3726 return mPointerCapture; 3727 } 3728 3729 void requestPointerCapture(boolean enabled) { 3730 if (mPointerCapture == enabled) { 3731 return; 3732 } 3733 InputManager.getInstance().requestPointerCapture(mAttachInfo.mWindowToken, enabled); 3734 } 3735 3736 private void handlePointerCaptureChanged(boolean hasCapture) { 3737 if (mPointerCapture == hasCapture) { 3738 return; 3739 } 3740 mPointerCapture = hasCapture; 3741 if (mView != null) { 3742 mView.dispatchPointerCaptureChanged(hasCapture); 3743 } 3744 } 3745 3746 @Override 3747 public void requestChildFocus(View child, View focused) { 3748 if (DEBUG_INPUT_RESIZE) { 3749 Log.v(mTag, "Request child focus: focus now " + focused); 3750 } 3751 checkThread(); 3752 scheduleTraversals(); 3753 } 3754 3755 @Override 3756 public void clearChildFocus(View child) { 3757 if (DEBUG_INPUT_RESIZE) { 3758 Log.v(mTag, "Clearing child focus"); 3759 } 3760 checkThread(); 3761 scheduleTraversals(); 3762 } 3763 3764 @Override 3765 public ViewParent getParentForAccessibility() { 3766 return null; 3767 } 3768 3769 @Override 3770 public void focusableViewAvailable(View v) { 3771 checkThread(); 3772 if (mView != null) { 3773 if (!mView.hasFocus()) { 3774 if (sAlwaysAssignFocus || !mAttachInfo.mInTouchMode) { 3775 v.requestFocus(); 3776 } 3777 } else { 3778 // the one case where will transfer focus away from the current one 3779 // is if the current view is a view group that prefers to give focus 3780 // to its children first AND the view is a descendant of it. 3781 View focused = mView.findFocus(); 3782 if (focused instanceof ViewGroup) { 3783 ViewGroup group = (ViewGroup) focused; 3784 if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS 3785 && isViewDescendantOf(v, focused)) { 3786 v.requestFocus(); 3787 } 3788 } 3789 } 3790 } 3791 } 3792 3793 @Override 3794 public void recomputeViewAttributes(View child) { 3795 checkThread(); 3796 if (mView == child) { 3797 mAttachInfo.mRecomputeGlobalAttributes = true; 3798 if (!mWillDrawSoon) { 3799 scheduleTraversals(); 3800 } 3801 } 3802 } 3803 3804 void dispatchDetachedFromWindow() { 3805 mFirstInputStage.onDetachedFromWindow(); 3806 if (mView != null && mView.mAttachInfo != null) { 3807 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); 3808 mView.dispatchDetachedFromWindow(); 3809 } 3810 3811 mAccessibilityInteractionConnectionManager.ensureNoConnection(); 3812 mAccessibilityManager.removeAccessibilityStateChangeListener( 3813 mAccessibilityInteractionConnectionManager); 3814 mAccessibilityManager.removeHighTextContrastStateChangeListener( 3815 mHighContrastTextManager); 3816 removeSendWindowContentChangedCallback(); 3817 3818 destroyHardwareRenderer(); 3819 3820 setAccessibilityFocus(null, null); 3821 3822 mView.assignParent(null); 3823 mView = null; 3824 mAttachInfo.mRootView = null; 3825 3826 mSurface.release(); 3827 3828 if (mInputQueueCallback != null && mInputQueue != null) { 3829 mInputQueueCallback.onInputQueueDestroyed(mInputQueue); 3830 mInputQueue.dispose(); 3831 mInputQueueCallback = null; 3832 mInputQueue = null; 3833 } 3834 if (mInputEventReceiver != null) { 3835 mInputEventReceiver.dispose(); 3836 mInputEventReceiver = null; 3837 } 3838 try { 3839 mWindowSession.remove(mWindow); 3840 } catch (RemoteException e) { 3841 } 3842 3843 // Dispose the input channel after removing the window so the Window Manager 3844 // doesn't interpret the input channel being closed as an abnormal termination. 3845 if (mInputChannel != null) { 3846 mInputChannel.dispose(); 3847 mInputChannel = null; 3848 } 3849 3850 mDisplayManager.unregisterDisplayListener(mDisplayListener); 3851 3852 unscheduleTraversals(); 3853 } 3854 3855 /** 3856 * Notifies all callbacks that configuration and/or display has changed and updates internal 3857 * state. 3858 * @param mergedConfiguration New global and override config in {@link MergedConfiguration} 3859 * container. 3860 * @param force Flag indicating if we should force apply the config. 3861 * @param newDisplayId Id of new display if moved, {@link Display#INVALID_DISPLAY} if not 3862 * changed. 3863 */ 3864 private void performConfigurationChange(MergedConfiguration mergedConfiguration, boolean force, 3865 int newDisplayId) { 3866 if (mergedConfiguration == null) { 3867 throw new IllegalArgumentException("No merged config provided."); 3868 } 3869 3870 Configuration globalConfig = mergedConfiguration.getGlobalConfiguration(); 3871 final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration(); 3872 if (DEBUG_CONFIGURATION) Log.v(mTag, 3873 "Applying new config to window " + mWindowAttributes.getTitle() 3874 + ", globalConfig: " + globalConfig 3875 + ", overrideConfig: " + overrideConfig); 3876 3877 final CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo(); 3878 if (!ci.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) { 3879 globalConfig = new Configuration(globalConfig); 3880 ci.applyToConfiguration(mNoncompatDensity, globalConfig); 3881 } 3882 3883 synchronized (sConfigCallbacks) { 3884 for (int i=sConfigCallbacks.size()-1; i>=0; i--) { 3885 sConfigCallbacks.get(i).onConfigurationChanged(globalConfig); 3886 } 3887 } 3888 3889 mLastReportedMergedConfiguration.setConfiguration(globalConfig, overrideConfig); 3890 3891 mForceNextConfigUpdate = force; 3892 if (mActivityConfigCallback != null) { 3893 // An activity callback is set - notify it about override configuration update. 3894 // This basically initiates a round trip to ActivityThread and back, which will ensure 3895 // that corresponding activity and resources are updated before updating inner state of 3896 // ViewRootImpl. Eventually it will call #updateConfiguration(). 3897 mActivityConfigCallback.onConfigurationChanged(overrideConfig, newDisplayId); 3898 } else { 3899 // There is no activity callback - update the configuration right away. 3900 updateConfiguration(newDisplayId); 3901 } 3902 mForceNextConfigUpdate = false; 3903 } 3904 3905 /** 3906 * Update display and views if last applied merged configuration changed. 3907 * @param newDisplayId Id of new display if moved, {@link Display#INVALID_DISPLAY} otherwise. 3908 */ 3909 public void updateConfiguration(int newDisplayId) { 3910 if (mView == null) { 3911 return; 3912 } 3913 3914 // At this point the resources have been updated to 3915 // have the most recent config, whatever that is. Use 3916 // the one in them which may be newer. 3917 final Resources localResources = mView.getResources(); 3918 final Configuration config = localResources.getConfiguration(); 3919 3920 // Handle move to display. 3921 if (newDisplayId != INVALID_DISPLAY) { 3922 onMovedToDisplay(newDisplayId, config); 3923 } 3924 3925 // Handle configuration change. 3926 if (mForceNextConfigUpdate || mLastConfigurationFromResources.diff(config) != 0) { 3927 // Update the display with new DisplayAdjustments. 3928 mDisplay = ResourcesManager.getInstance().getAdjustedDisplay( 3929 mDisplay.getDisplayId(), localResources); 3930 3931 final int lastLayoutDirection = mLastConfigurationFromResources.getLayoutDirection(); 3932 final int currentLayoutDirection = config.getLayoutDirection(); 3933 mLastConfigurationFromResources.setTo(config); 3934 if (lastLayoutDirection != currentLayoutDirection 3935 && mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { 3936 mView.setLayoutDirection(currentLayoutDirection); 3937 } 3938 mView.dispatchConfigurationChanged(config); 3939 3940 // We could have gotten this {@link Configuration} update after we called 3941 // {@link #performTraversals} with an older {@link Configuration}. As a result, our 3942 // window frame may be stale. We must ensure the next pass of {@link #performTraversals} 3943 // catches this. 3944 mForceNextWindowRelayout = true; 3945 requestLayout(); 3946 } 3947 } 3948 3949 /** 3950 * Return true if child is an ancestor of parent, (or equal to the parent). 3951 */ 3952 public static boolean isViewDescendantOf(View child, View parent) { 3953 if (child == parent) { 3954 return true; 3955 } 3956 3957 final ViewParent theParent = child.getParent(); 3958 return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); 3959 } 3960 3961 private static void forceLayout(View view) { 3962 view.forceLayout(); 3963 if (view instanceof ViewGroup) { 3964 ViewGroup group = (ViewGroup) view; 3965 final int count = group.getChildCount(); 3966 for (int i = 0; i < count; i++) { 3967 forceLayout(group.getChildAt(i)); 3968 } 3969 } 3970 } 3971 3972 private final static int MSG_INVALIDATE = 1; 3973 private final static int MSG_INVALIDATE_RECT = 2; 3974 private final static int MSG_DIE = 3; 3975 private final static int MSG_RESIZED = 4; 3976 private final static int MSG_RESIZED_REPORT = 5; 3977 private final static int MSG_WINDOW_FOCUS_CHANGED = 6; 3978 private final static int MSG_DISPATCH_INPUT_EVENT = 7; 3979 private final static int MSG_DISPATCH_APP_VISIBILITY = 8; 3980 private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9; 3981 private final static int MSG_DISPATCH_KEY_FROM_IME = 11; 3982 private final static int MSG_DISPATCH_KEY_FROM_AUTOFILL = 12; 3983 private final static int MSG_CHECK_FOCUS = 13; 3984 private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14; 3985 private final static int MSG_DISPATCH_DRAG_EVENT = 15; 3986 private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16; 3987 private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17; 3988 private final static int MSG_UPDATE_CONFIGURATION = 18; 3989 private final static int MSG_PROCESS_INPUT_EVENTS = 19; 3990 private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; 3991 private final static int MSG_INVALIDATE_WORLD = 22; 3992 private final static int MSG_WINDOW_MOVED = 23; 3993 private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24; 3994 private final static int MSG_DISPATCH_WINDOW_SHOWN = 25; 3995 private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; 3996 private final static int MSG_UPDATE_POINTER_ICON = 27; 3997 private final static int MSG_POINTER_CAPTURE_CHANGED = 28; 3998 private final static int MSG_DRAW_FINISHED = 29; 3999 4000 final class ViewRootHandler extends Handler { 4001 @Override 4002 public String getMessageName(Message message) { 4003 switch (message.what) { 4004 case MSG_INVALIDATE: 4005 return "MSG_INVALIDATE"; 4006 case MSG_INVALIDATE_RECT: 4007 return "MSG_INVALIDATE_RECT"; 4008 case MSG_DIE: 4009 return "MSG_DIE"; 4010 case MSG_RESIZED: 4011 return "MSG_RESIZED"; 4012 case MSG_RESIZED_REPORT: 4013 return "MSG_RESIZED_REPORT"; 4014 case MSG_WINDOW_FOCUS_CHANGED: 4015 return "MSG_WINDOW_FOCUS_CHANGED"; 4016 case MSG_DISPATCH_INPUT_EVENT: 4017 return "MSG_DISPATCH_INPUT_EVENT"; 4018 case MSG_DISPATCH_APP_VISIBILITY: 4019 return "MSG_DISPATCH_APP_VISIBILITY"; 4020 case MSG_DISPATCH_GET_NEW_SURFACE: 4021 return "MSG_DISPATCH_GET_NEW_SURFACE"; 4022 case MSG_DISPATCH_KEY_FROM_IME: 4023 return "MSG_DISPATCH_KEY_FROM_IME"; 4024 case MSG_DISPATCH_KEY_FROM_AUTOFILL: 4025 return "MSG_DISPATCH_KEY_FROM_AUTOFILL"; 4026 case MSG_CHECK_FOCUS: 4027 return "MSG_CHECK_FOCUS"; 4028 case MSG_CLOSE_SYSTEM_DIALOGS: 4029 return "MSG_CLOSE_SYSTEM_DIALOGS"; 4030 case MSG_DISPATCH_DRAG_EVENT: 4031 return "MSG_DISPATCH_DRAG_EVENT"; 4032 case MSG_DISPATCH_DRAG_LOCATION_EVENT: 4033 return "MSG_DISPATCH_DRAG_LOCATION_EVENT"; 4034 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: 4035 return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY"; 4036 case MSG_UPDATE_CONFIGURATION: 4037 return "MSG_UPDATE_CONFIGURATION"; 4038 case MSG_PROCESS_INPUT_EVENTS: 4039 return "MSG_PROCESS_INPUT_EVENTS"; 4040 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: 4041 return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; 4042 case MSG_WINDOW_MOVED: 4043 return "MSG_WINDOW_MOVED"; 4044 case MSG_SYNTHESIZE_INPUT_EVENT: 4045 return "MSG_SYNTHESIZE_INPUT_EVENT"; 4046 case MSG_DISPATCH_WINDOW_SHOWN: 4047 return "MSG_DISPATCH_WINDOW_SHOWN"; 4048 case MSG_UPDATE_POINTER_ICON: 4049 return "MSG_UPDATE_POINTER_ICON"; 4050 case MSG_POINTER_CAPTURE_CHANGED: 4051 return "MSG_POINTER_CAPTURE_CHANGED"; 4052 case MSG_DRAW_FINISHED: 4053 return "MSG_DRAW_FINISHED"; 4054 } 4055 return super.getMessageName(message); 4056 } 4057 4058 @Override 4059 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 4060 if (msg.what == MSG_REQUEST_KEYBOARD_SHORTCUTS && msg.obj == null) { 4061 // Debugging for b/27963013 4062 throw new NullPointerException( 4063 "Attempted to call MSG_REQUEST_KEYBOARD_SHORTCUTS with null receiver:"); 4064 } 4065 return super.sendMessageAtTime(msg, uptimeMillis); 4066 } 4067 4068 @Override 4069 public void handleMessage(Message msg) { 4070 switch (msg.what) { 4071 case MSG_INVALIDATE: 4072 ((View) msg.obj).invalidate(); 4073 break; 4074 case MSG_INVALIDATE_RECT: 4075 final View.AttachInfo.InvalidateInfo info = 4076 (View.AttachInfo.InvalidateInfo) msg.obj; 4077 info.target.invalidate(info.left, info.top, info.right, info.bottom); 4078 info.recycle(); 4079 break; 4080 case MSG_PROCESS_INPUT_EVENTS: 4081 mProcessInputEventsScheduled = false; 4082 doProcessInputEvents(); 4083 break; 4084 case MSG_DISPATCH_APP_VISIBILITY: 4085 handleAppVisibility(msg.arg1 != 0); 4086 break; 4087 case MSG_DISPATCH_GET_NEW_SURFACE: 4088 handleGetNewSurface(); 4089 break; 4090 case MSG_RESIZED: { 4091 // Recycled in the fall through... 4092 SomeArgs args = (SomeArgs) msg.obj; 4093 if (mWinFrame.equals(args.arg1) 4094 && mPendingOverscanInsets.equals(args.arg5) 4095 && mPendingContentInsets.equals(args.arg2) 4096 && mPendingStableInsets.equals(args.arg6) 4097 && mPendingDisplayCutout.get().equals(args.arg9) 4098 && mPendingVisibleInsets.equals(args.arg3) 4099 && mPendingOutsets.equals(args.arg7) 4100 && mPendingBackDropFrame.equals(args.arg8) 4101 && args.arg4 == null 4102 && args.argi1 == 0 4103 && mDisplay.getDisplayId() == args.argi3) { 4104 break; 4105 } 4106 } // fall through... 4107 case MSG_RESIZED_REPORT: 4108 if (mAdded) { 4109 SomeArgs args = (SomeArgs) msg.obj; 4110 4111 final int displayId = args.argi3; 4112 MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4; 4113 final boolean displayChanged = mDisplay.getDisplayId() != displayId; 4114 4115 if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) { 4116 // If configuration changed - notify about that and, maybe, 4117 // about move to display. 4118 performConfigurationChange(mergedConfiguration, false /* force */, 4119 displayChanged 4120 ? displayId : INVALID_DISPLAY /* same display */); 4121 } else if (displayChanged) { 4122 // Moved to display without config change - report last applied one. 4123 onMovedToDisplay(displayId, mLastConfigurationFromResources); 4124 } 4125 4126 final boolean framesChanged = !mWinFrame.equals(args.arg1) 4127 || !mPendingOverscanInsets.equals(args.arg5) 4128 || !mPendingContentInsets.equals(args.arg2) 4129 || !mPendingStableInsets.equals(args.arg6) 4130 || !mPendingDisplayCutout.get().equals(args.arg9) 4131 || !mPendingVisibleInsets.equals(args.arg3) 4132 || !mPendingOutsets.equals(args.arg7); 4133 4134 mWinFrame.set((Rect) args.arg1); 4135 mPendingOverscanInsets.set((Rect) args.arg5); 4136 mPendingContentInsets.set((Rect) args.arg2); 4137 mPendingStableInsets.set((Rect) args.arg6); 4138 mPendingDisplayCutout.set((DisplayCutout) args.arg9); 4139 mPendingVisibleInsets.set((Rect) args.arg3); 4140 mPendingOutsets.set((Rect) args.arg7); 4141 mPendingBackDropFrame.set((Rect) args.arg8); 4142 mForceNextWindowRelayout = args.argi1 != 0; 4143 mPendingAlwaysConsumeNavBar = args.argi2 != 0; 4144 4145 args.recycle(); 4146 4147 if (msg.what == MSG_RESIZED_REPORT) { 4148 reportNextDraw(); 4149 } 4150 4151 if (mView != null && framesChanged) { 4152 forceLayout(mView); 4153 } 4154 requestLayout(); 4155 } 4156 break; 4157 case MSG_WINDOW_MOVED: 4158 if (mAdded) { 4159 final int w = mWinFrame.width(); 4160 final int h = mWinFrame.height(); 4161 final int l = msg.arg1; 4162 final int t = msg.arg2; 4163 mWinFrame.left = l; 4164 mWinFrame.right = l + w; 4165 mWinFrame.top = t; 4166 mWinFrame.bottom = t + h; 4167 4168 mPendingBackDropFrame.set(mWinFrame); 4169 maybeHandleWindowMove(mWinFrame); 4170 } 4171 break; 4172 case MSG_WINDOW_FOCUS_CHANGED: { 4173 handleWindowFocusChanged(); 4174 } break; 4175 case MSG_DIE: 4176 doDie(); 4177 break; 4178 case MSG_DISPATCH_INPUT_EVENT: { 4179 SomeArgs args = (SomeArgs) msg.obj; 4180 InputEvent event = (InputEvent) args.arg1; 4181 InputEventReceiver receiver = (InputEventReceiver) args.arg2; 4182 enqueueInputEvent(event, receiver, 0, true); 4183 args.recycle(); 4184 } break; 4185 case MSG_SYNTHESIZE_INPUT_EVENT: { 4186 InputEvent event = (InputEvent) msg.obj; 4187 enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true); 4188 } break; 4189 case MSG_DISPATCH_KEY_FROM_IME: { 4190 if (LOCAL_LOGV) { 4191 Log.v(TAG, "Dispatching key " + msg.obj + " from IME to " + mView); 4192 } 4193 KeyEvent event = (KeyEvent) msg.obj; 4194 if ((event.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) != 0) { 4195 // The IME is trying to say this event is from the 4196 // system! Bad bad bad! 4197 //noinspection UnusedAssignment 4198 event = KeyEvent.changeFlags(event, 4199 event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); 4200 } 4201 enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true); 4202 } break; 4203 case MSG_DISPATCH_KEY_FROM_AUTOFILL: { 4204 if (LOCAL_LOGV) { 4205 Log.v(TAG, "Dispatching key " + msg.obj + " from Autofill to " + mView); 4206 } 4207 KeyEvent event = (KeyEvent) msg.obj; 4208 enqueueInputEvent(event, null, 0, true); 4209 } break; 4210 case MSG_CHECK_FOCUS: { 4211 InputMethodManager imm = InputMethodManager.peekInstance(); 4212 if (imm != null) { 4213 imm.checkFocus(); 4214 } 4215 } break; 4216 case MSG_CLOSE_SYSTEM_DIALOGS: { 4217 if (mView != null) { 4218 mView.onCloseSystemDialogs((String) msg.obj); 4219 } 4220 } break; 4221 case MSG_DISPATCH_DRAG_EVENT: { 4222 } // fall through 4223 case MSG_DISPATCH_DRAG_LOCATION_EVENT: { 4224 DragEvent event = (DragEvent) msg.obj; 4225 // only present when this app called startDrag() 4226 event.mLocalState = mLocalDragState; 4227 handleDragEvent(event); 4228 } break; 4229 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: { 4230 handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj); 4231 } break; 4232 case MSG_UPDATE_CONFIGURATION: { 4233 Configuration config = (Configuration) msg.obj; 4234 if (config.isOtherSeqNewer( 4235 mLastReportedMergedConfiguration.getMergedConfiguration())) { 4236 // If we already have a newer merged config applied - use its global part. 4237 config = mLastReportedMergedConfiguration.getGlobalConfiguration(); 4238 } 4239 4240 // Use the newer global config and last reported override config. 4241 mPendingMergedConfiguration.setConfiguration(config, 4242 mLastReportedMergedConfiguration.getOverrideConfiguration()); 4243 4244 performConfigurationChange(mPendingMergedConfiguration, false /* force */, 4245 INVALID_DISPLAY /* same display */); 4246 } break; 4247 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { 4248 setAccessibilityFocus(null, null); 4249 } break; 4250 case MSG_INVALIDATE_WORLD: { 4251 if (mView != null) { 4252 invalidateWorld(mView); 4253 } 4254 } break; 4255 case MSG_DISPATCH_WINDOW_SHOWN: { 4256 handleDispatchWindowShown(); 4257 } break; 4258 case MSG_REQUEST_KEYBOARD_SHORTCUTS: { 4259 final IResultReceiver receiver = (IResultReceiver) msg.obj; 4260 final int deviceId = msg.arg1; 4261 handleRequestKeyboardShortcuts(receiver, deviceId); 4262 } break; 4263 case MSG_UPDATE_POINTER_ICON: { 4264 MotionEvent event = (MotionEvent) msg.obj; 4265 resetPointerIcon(event); 4266 } break; 4267 case MSG_POINTER_CAPTURE_CHANGED: { 4268 final boolean hasCapture = msg.arg1 != 0; 4269 handlePointerCaptureChanged(hasCapture); 4270 } break; 4271 case MSG_DRAW_FINISHED: { 4272 pendingDrawFinished(); 4273 } break; 4274 } 4275 } 4276 } 4277 4278 final ViewRootHandler mHandler = new ViewRootHandler(); 4279 4280 /** 4281 * Something in the current window tells us we need to change the touch mode. For 4282 * example, we are not in touch mode, and the user touches the screen. 4283 * 4284 * If the touch mode has changed, tell the window manager, and handle it locally. 4285 * 4286 * @param inTouchMode Whether we want to be in touch mode. 4287 * @return True if the touch mode changed and focus changed was changed as a result 4288 */ 4289 boolean ensureTouchMode(boolean inTouchMode) { 4290 if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current " 4291 + "touch mode is " + mAttachInfo.mInTouchMode); 4292 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 4293 4294 // tell the window manager 4295 try { 4296 mWindowSession.setInTouchMode(inTouchMode); 4297 } catch (RemoteException e) { 4298 throw new RuntimeException(e); 4299 } 4300 4301 // handle the change 4302 return ensureTouchModeLocally(inTouchMode); 4303 } 4304 4305 /** 4306 * Ensure that the touch mode for this window is set, and if it is changing, 4307 * take the appropriate action. 4308 * @param inTouchMode Whether we want to be in touch mode. 4309 * @return True if the touch mode changed and focus changed was changed as a result 4310 */ 4311 private boolean ensureTouchModeLocally(boolean inTouchMode) { 4312 if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current " 4313 + "touch mode is " + mAttachInfo.mInTouchMode); 4314 4315 if (mAttachInfo.mInTouchMode == inTouchMode) return false; 4316 4317 mAttachInfo.mInTouchMode = inTouchMode; 4318 mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode); 4319 4320 return (inTouchMode) ? enterTouchMode() : leaveTouchMode(); 4321 } 4322 4323 private boolean enterTouchMode() { 4324 if (mView != null && mView.hasFocus()) { 4325 // note: not relying on mFocusedView here because this could 4326 // be when the window is first being added, and mFocused isn't 4327 // set yet. 4328 final View focused = mView.findFocus(); 4329 if (focused != null && !focused.isFocusableInTouchMode()) { 4330 final ViewGroup ancestorToTakeFocus = findAncestorToTakeFocusInTouchMode(focused); 4331 if (ancestorToTakeFocus != null) { 4332 // there is an ancestor that wants focus after its 4333 // descendants that is focusable in touch mode.. give it 4334 // focus 4335 return ancestorToTakeFocus.requestFocus(); 4336 } else { 4337 // There's nothing to focus. Clear and propagate through the 4338 // hierarchy, but don't attempt to place new focus. 4339 focused.clearFocusInternal(null, true, false); 4340 return true; 4341 } 4342 } 4343 } 4344 return false; 4345 } 4346 4347 /** 4348 * Find an ancestor of focused that wants focus after its descendants and is 4349 * focusable in touch mode. 4350 * @param focused The currently focused view. 4351 * @return An appropriate view, or null if no such view exists. 4352 */ 4353 private static ViewGroup findAncestorToTakeFocusInTouchMode(View focused) { 4354 ViewParent parent = focused.getParent(); 4355 while (parent instanceof ViewGroup) { 4356 final ViewGroup vgParent = (ViewGroup) parent; 4357 if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS 4358 && vgParent.isFocusableInTouchMode()) { 4359 return vgParent; 4360 } 4361 if (vgParent.isRootNamespace()) { 4362 return null; 4363 } else { 4364 parent = vgParent.getParent(); 4365 } 4366 } 4367 return null; 4368 } 4369 4370 private boolean leaveTouchMode() { 4371 if (mView != null) { 4372 if (mView.hasFocus()) { 4373 View focusedView = mView.findFocus(); 4374 if (!(focusedView instanceof ViewGroup)) { 4375 // some view has focus, let it keep it 4376 return false; 4377 } else if (((ViewGroup) focusedView).getDescendantFocusability() != 4378 ViewGroup.FOCUS_AFTER_DESCENDANTS) { 4379 // some view group has focus, and doesn't prefer its children 4380 // over itself for focus, so let them keep it. 4381 return false; 4382 } 4383 } 4384 4385 // find the best view to give focus to in this brave new non-touch-mode 4386 // world 4387 return mView.restoreDefaultFocus(); 4388 } 4389 return false; 4390 } 4391 4392 /** 4393 * Base class for implementing a stage in the chain of responsibility 4394 * for processing input events. 4395 * <p> 4396 * Events are delivered to the stage by the {@link #deliver} method. The stage 4397 * then has the choice of finishing the event or forwarding it to the next stage. 4398 * </p> 4399 */ 4400 abstract class InputStage { 4401 private final InputStage mNext; 4402 4403 protected static final int FORWARD = 0; 4404 protected static final int FINISH_HANDLED = 1; 4405 protected static final int FINISH_NOT_HANDLED = 2; 4406 4407 /** 4408 * Creates an input stage. 4409 * @param next The next stage to which events should be forwarded. 4410 */ 4411 public InputStage(InputStage next) { 4412 mNext = next; 4413 } 4414 4415 /** 4416 * Delivers an event to be processed. 4417 */ 4418 public final void deliver(QueuedInputEvent q) { 4419 if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { 4420 forward(q); 4421 } else if (shouldDropInputEvent(q)) { 4422 finish(q, false); 4423 } else { 4424 apply(q, onProcess(q)); 4425 } 4426 } 4427 4428 /** 4429 * Marks the the input event as finished then forwards it to the next stage. 4430 */ 4431 protected void finish(QueuedInputEvent q, boolean handled) { 4432 q.mFlags |= QueuedInputEvent.FLAG_FINISHED; 4433 if (handled) { 4434 q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED; 4435 } 4436 forward(q); 4437 } 4438 4439 /** 4440 * Forwards the event to the next stage. 4441 */ 4442 protected void forward(QueuedInputEvent q) { 4443 onDeliverToNext(q); 4444 } 4445 4446 /** 4447 * Applies a result code from {@link #onProcess} to the specified event. 4448 */ 4449 protected void apply(QueuedInputEvent q, int result) { 4450 if (result == FORWARD) { 4451 forward(q); 4452 } else if (result == FINISH_HANDLED) { 4453 finish(q, true); 4454 } else if (result == FINISH_NOT_HANDLED) { 4455 finish(q, false); 4456 } else { 4457 throw new IllegalArgumentException("Invalid result: " + result); 4458 } 4459 } 4460 4461 /** 4462 * Called when an event is ready to be processed. 4463 * @return A result code indicating how the event was handled. 4464 */ 4465 protected int onProcess(QueuedInputEvent q) { 4466 return FORWARD; 4467 } 4468 4469 /** 4470 * Called when an event is being delivered to the next stage. 4471 */ 4472 protected void onDeliverToNext(QueuedInputEvent q) { 4473 if (DEBUG_INPUT_STAGES) { 4474 Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q); 4475 } 4476 if (mNext != null) { 4477 mNext.deliver(q); 4478 } else { 4479 finishInputEvent(q); 4480 } 4481 } 4482 4483 protected void onWindowFocusChanged(boolean hasWindowFocus) { 4484 if (mNext != null) { 4485 mNext.onWindowFocusChanged(hasWindowFocus); 4486 } 4487 } 4488 4489 protected void onDetachedFromWindow() { 4490 if (mNext != null) { 4491 mNext.onDetachedFromWindow(); 4492 } 4493 } 4494 4495 protected boolean shouldDropInputEvent(QueuedInputEvent q) { 4496 if (mView == null || !mAdded) { 4497 Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent); 4498 return true; 4499 } else if ((!mAttachInfo.mHasWindowFocus 4500 && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) 4501 && !isAutofillUiShowing()) || mStopped 4502 || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) 4503 || (mPausedForTransition && !isBack(q.mEvent))) { 4504 // This is a focus event and the window doesn't currently have input focus or 4505 // has stopped. This could be an event that came back from the previous stage 4506 // but the window has lost focus or stopped in the meantime. 4507 if (isTerminalInputEvent(q.mEvent)) { 4508 // Don't drop terminal input events, however mark them as canceled. 4509 q.mEvent.cancel(); 4510 Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent); 4511 return false; 4512 } 4513 4514 // Drop non-terminal input events. 4515 Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent); 4516 return true; 4517 } 4518 return false; 4519 } 4520 4521 void dump(String prefix, PrintWriter writer) { 4522 if (mNext != null) { 4523 mNext.dump(prefix, writer); 4524 } 4525 } 4526 4527 private boolean isBack(InputEvent event) { 4528 if (event instanceof KeyEvent) { 4529 return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK; 4530 } else { 4531 return false; 4532 } 4533 } 4534 } 4535 4536 /** 4537 * Base class for implementing an input pipeline stage that supports 4538 * asynchronous and out-of-order processing of input events. 4539 * <p> 4540 * In addition to what a normal input stage can do, an asynchronous 4541 * input stage may also defer an input event that has been delivered to it 4542 * and finish or forward it later. 4543 * </p> 4544 */ 4545 abstract class AsyncInputStage extends InputStage { 4546 private final String mTraceCounter; 4547 4548 private QueuedInputEvent mQueueHead; 4549 private QueuedInputEvent mQueueTail; 4550 private int mQueueLength; 4551 4552 protected static final int DEFER = 3; 4553 4554 /** 4555 * Creates an asynchronous input stage. 4556 * @param next The next stage to which events should be forwarded. 4557 * @param traceCounter The name of a counter to record the size of 4558 * the queue of pending events. 4559 */ 4560 public AsyncInputStage(InputStage next, String traceCounter) { 4561 super(next); 4562 mTraceCounter = traceCounter; 4563 } 4564 4565 /** 4566 * Marks the event as deferred, which is to say that it will be handled 4567 * asynchronously. The caller is responsible for calling {@link #forward} 4568 * or {@link #finish} later when it is done handling the event. 4569 */ 4570 protected void defer(QueuedInputEvent q) { 4571 q.mFlags |= QueuedInputEvent.FLAG_DEFERRED; 4572 enqueue(q); 4573 } 4574 4575 @Override 4576 protected void forward(QueuedInputEvent q) { 4577 // Clear the deferred flag. 4578 q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED; 4579 4580 // Fast path if the queue is empty. 4581 QueuedInputEvent curr = mQueueHead; 4582 if (curr == null) { 4583 super.forward(q); 4584 return; 4585 } 4586 4587 // Determine whether the event must be serialized behind any others 4588 // before it can be delivered to the next stage. This is done because 4589 // deferred events might be handled out of order by the stage. 4590 final int deviceId = q.mEvent.getDeviceId(); 4591 QueuedInputEvent prev = null; 4592 boolean blocked = false; 4593 while (curr != null && curr != q) { 4594 if (!blocked && deviceId == curr.mEvent.getDeviceId()) { 4595 blocked = true; 4596 } 4597 prev = curr; 4598 curr = curr.mNext; 4599 } 4600 4601 // If the event is blocked, then leave it in the queue to be delivered later. 4602 // Note that the event might not yet be in the queue if it was not previously 4603 // deferred so we will enqueue it if needed. 4604 if (blocked) { 4605 if (curr == null) { 4606 enqueue(q); 4607 } 4608 return; 4609 } 4610 4611 // The event is not blocked. Deliver it immediately. 4612 if (curr != null) { 4613 curr = curr.mNext; 4614 dequeue(q, prev); 4615 } 4616 super.forward(q); 4617 4618 // Dequeuing this event may have unblocked successors. Deliver them. 4619 while (curr != null) { 4620 if (deviceId == curr.mEvent.getDeviceId()) { 4621 if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) { 4622 break; 4623 } 4624 QueuedInputEvent next = curr.mNext; 4625 dequeue(curr, prev); 4626 super.forward(curr); 4627 curr = next; 4628 } else { 4629 prev = curr; 4630 curr = curr.mNext; 4631 } 4632 } 4633 } 4634 4635 @Override 4636 protected void apply(QueuedInputEvent q, int result) { 4637 if (result == DEFER) { 4638 defer(q); 4639 } else { 4640 super.apply(q, result); 4641 } 4642 } 4643 4644 private void enqueue(QueuedInputEvent q) { 4645 if (mQueueTail == null) { 4646 mQueueHead = q; 4647 mQueueTail = q; 4648 } else { 4649 mQueueTail.mNext = q; 4650 mQueueTail = q; 4651 } 4652 4653 mQueueLength += 1; 4654 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength); 4655 } 4656 4657 private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) { 4658 if (prev == null) { 4659 mQueueHead = q.mNext; 4660 } else { 4661 prev.mNext = q.mNext; 4662 } 4663 if (mQueueTail == q) { 4664 mQueueTail = prev; 4665 } 4666 q.mNext = null; 4667 4668 mQueueLength -= 1; 4669 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength); 4670 } 4671 4672 @Override 4673 void dump(String prefix, PrintWriter writer) { 4674 writer.print(prefix); 4675 writer.print(getClass().getName()); 4676 writer.print(": mQueueLength="); 4677 writer.println(mQueueLength); 4678 4679 super.dump(prefix, writer); 4680 } 4681 } 4682 4683 /** 4684 * Delivers pre-ime input events to a native activity. 4685 * Does not support pointer events. 4686 */ 4687 final class NativePreImeInputStage extends AsyncInputStage 4688 implements InputQueue.FinishedInputEventCallback { 4689 public NativePreImeInputStage(InputStage next, String traceCounter) { 4690 super(next, traceCounter); 4691 } 4692 4693 @Override 4694 protected int onProcess(QueuedInputEvent q) { 4695 if (mInputQueue != null && q.mEvent instanceof KeyEvent) { 4696 mInputQueue.sendInputEvent(q.mEvent, q, true, this); 4697 return DEFER; 4698 } 4699 return FORWARD; 4700 } 4701 4702 @Override 4703 public void onFinishedInputEvent(Object token, boolean handled) { 4704 QueuedInputEvent q = (QueuedInputEvent)token; 4705 if (handled) { 4706 finish(q, true); 4707 return; 4708 } 4709 forward(q); 4710 } 4711 } 4712 4713 /** 4714 * Delivers pre-ime input events to the view hierarchy. 4715 * Does not support pointer events. 4716 */ 4717 final class ViewPreImeInputStage extends InputStage { 4718 public ViewPreImeInputStage(InputStage next) { 4719 super(next); 4720 } 4721 4722 @Override 4723 protected int onProcess(QueuedInputEvent q) { 4724 if (q.mEvent instanceof KeyEvent) { 4725 return processKeyEvent(q); 4726 } 4727 return FORWARD; 4728 } 4729 4730 private int processKeyEvent(QueuedInputEvent q) { 4731 final KeyEvent event = (KeyEvent)q.mEvent; 4732 if (mView.dispatchKeyEventPreIme(event)) { 4733 return FINISH_HANDLED; 4734 } 4735 return FORWARD; 4736 } 4737 } 4738 4739 /** 4740 * Delivers input events to the ime. 4741 * Does not support pointer events. 4742 */ 4743 final class ImeInputStage extends AsyncInputStage 4744 implements InputMethodManager.FinishedInputEventCallback { 4745 public ImeInputStage(InputStage next, String traceCounter) { 4746 super(next, traceCounter); 4747 } 4748 4749 @Override 4750 protected int onProcess(QueuedInputEvent q) { 4751 if (mLastWasImTarget && !isInLocalFocusMode()) { 4752 InputMethodManager imm = InputMethodManager.peekInstance(); 4753 if (imm != null) { 4754 final InputEvent event = q.mEvent; 4755 if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event); 4756 int result = imm.dispatchInputEvent(event, q, this, mHandler); 4757 if (result == InputMethodManager.DISPATCH_HANDLED) { 4758 return FINISH_HANDLED; 4759 } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) { 4760 // The IME could not handle it, so skip along to the next InputStage 4761 return FORWARD; 4762 } else { 4763 return DEFER; // callback will be invoked later 4764 } 4765 } 4766 } 4767 return FORWARD; 4768 } 4769 4770 @Override 4771 public void onFinishedInputEvent(Object token, boolean handled) { 4772 QueuedInputEvent q = (QueuedInputEvent)token; 4773 if (handled) { 4774 finish(q, true); 4775 return; 4776 } 4777 forward(q); 4778 } 4779 } 4780 4781 /** 4782 * Performs early processing of post-ime input events. 4783 */ 4784 final class EarlyPostImeInputStage extends InputStage { 4785 public EarlyPostImeInputStage(InputStage next) { 4786 super(next); 4787 } 4788 4789 @Override 4790 protected int onProcess(QueuedInputEvent q) { 4791 if (q.mEvent instanceof KeyEvent) { 4792 return processKeyEvent(q); 4793 } else { 4794 final int source = q.mEvent.getSource(); 4795 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { 4796 return processPointerEvent(q); 4797 } 4798 } 4799 return FORWARD; 4800 } 4801 4802 private int processKeyEvent(QueuedInputEvent q) { 4803 final KeyEvent event = (KeyEvent)q.mEvent; 4804 4805 if (mAttachInfo.mTooltipHost != null) { 4806 mAttachInfo.mTooltipHost.handleTooltipKey(event); 4807 } 4808 4809 // If the key's purpose is to exit touch mode then we consume it 4810 // and consider it handled. 4811 if (checkForLeavingTouchModeAndConsume(event)) { 4812 return FINISH_HANDLED; 4813 } 4814 4815 // Make sure the fallback event policy sees all keys that will be 4816 // delivered to the view hierarchy. 4817 mFallbackEventHandler.preDispatchKeyEvent(event); 4818 return FORWARD; 4819 } 4820 4821 private int processPointerEvent(QueuedInputEvent q) { 4822 final MotionEvent event = (MotionEvent)q.mEvent; 4823 4824 // Translate the pointer event for compatibility, if needed. 4825 if (mTranslator != null) { 4826 mTranslator.translateEventInScreenToAppWindow(event); 4827 } 4828 4829 // Enter touch mode on down or scroll, if it is coming from a touch screen device, 4830 // exit otherwise. 4831 final int action = event.getAction(); 4832 if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { 4833 ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); 4834 } 4835 4836 if (action == MotionEvent.ACTION_DOWN) { 4837 // Upon motion event within app window, close autofill ui. 4838 AutofillManager afm = getAutofillManager(); 4839 if (afm != null) { 4840 afm.requestHideFillUi(); 4841 } 4842 } 4843 4844 if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) { 4845 mAttachInfo.mTooltipHost.hideTooltip(); 4846 } 4847 4848 // Offset the scroll position. 4849 if (mCurScrollY != 0) { 4850 event.offsetLocation(0, mCurScrollY); 4851 } 4852 4853 // Remember the touch position for possible drag-initiation. 4854 if (event.isTouchEvent()) { 4855 mLastTouchPoint.x = event.getRawX(); 4856 mLastTouchPoint.y = event.getRawY(); 4857 mLastTouchSource = event.getSource(); 4858 } 4859 return FORWARD; 4860 } 4861 } 4862 4863 /** 4864 * Delivers post-ime input events to a native activity. 4865 */ 4866 final class NativePostImeInputStage extends AsyncInputStage 4867 implements InputQueue.FinishedInputEventCallback { 4868 public NativePostImeInputStage(InputStage next, String traceCounter) { 4869 super(next, traceCounter); 4870 } 4871 4872 @Override 4873 protected int onProcess(QueuedInputEvent q) { 4874 if (mInputQueue != null) { 4875 mInputQueue.sendInputEvent(q.mEvent, q, false, this); 4876 return DEFER; 4877 } 4878 return FORWARD; 4879 } 4880 4881 @Override 4882 public void onFinishedInputEvent(Object token, boolean handled) { 4883 QueuedInputEvent q = (QueuedInputEvent)token; 4884 if (handled) { 4885 finish(q, true); 4886 return; 4887 } 4888 forward(q); 4889 } 4890 } 4891 4892 /** 4893 * Delivers post-ime input events to the view hierarchy. 4894 */ 4895 final class ViewPostImeInputStage extends InputStage { 4896 public ViewPostImeInputStage(InputStage next) { 4897 super(next); 4898 } 4899 4900 @Override 4901 protected int onProcess(QueuedInputEvent q) { 4902 if (q.mEvent instanceof KeyEvent) { 4903 return processKeyEvent(q); 4904 } else { 4905 final int source = q.mEvent.getSource(); 4906 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { 4907 return processPointerEvent(q); 4908 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 4909 return processTrackballEvent(q); 4910 } else { 4911 return processGenericMotionEvent(q); 4912 } 4913 } 4914 } 4915 4916 @Override 4917 protected void onDeliverToNext(QueuedInputEvent q) { 4918 if (mUnbufferedInputDispatch 4919 && q.mEvent instanceof MotionEvent 4920 && ((MotionEvent)q.mEvent).isTouchEvent() 4921 && isTerminalInputEvent(q.mEvent)) { 4922 mUnbufferedInputDispatch = false; 4923 scheduleConsumeBatchedInput(); 4924 } 4925 super.onDeliverToNext(q); 4926 } 4927 4928 private boolean performFocusNavigation(KeyEvent event) { 4929 int direction = 0; 4930 switch (event.getKeyCode()) { 4931 case KeyEvent.KEYCODE_DPAD_LEFT: 4932 if (event.hasNoModifiers()) { 4933 direction = View.FOCUS_LEFT; 4934 } 4935 break; 4936 case KeyEvent.KEYCODE_DPAD_RIGHT: 4937 if (event.hasNoModifiers()) { 4938 direction = View.FOCUS_RIGHT; 4939 } 4940 break; 4941 case KeyEvent.KEYCODE_DPAD_UP: 4942 if (event.hasNoModifiers()) { 4943 direction = View.FOCUS_UP; 4944 } 4945 break; 4946 case KeyEvent.KEYCODE_DPAD_DOWN: 4947 if (event.hasNoModifiers()) { 4948 direction = View.FOCUS_DOWN; 4949 } 4950 break; 4951 case KeyEvent.KEYCODE_TAB: 4952 if (event.hasNoModifiers()) { 4953 direction = View.FOCUS_FORWARD; 4954 } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { 4955 direction = View.FOCUS_BACKWARD; 4956 } 4957 break; 4958 } 4959 if (direction != 0) { 4960 View focused = mView.findFocus(); 4961 if (focused != null) { 4962 View v = focused.focusSearch(direction); 4963 if (v != null && v != focused) { 4964 // do the math the get the interesting rect 4965 // of previous focused into the coord system of 4966 // newly focused view 4967 focused.getFocusedRect(mTempRect); 4968 if (mView instanceof ViewGroup) { 4969 ((ViewGroup) mView).offsetDescendantRectToMyCoords( 4970 focused, mTempRect); 4971 ((ViewGroup) mView).offsetRectIntoDescendantCoords( 4972 v, mTempRect); 4973 } 4974 if (v.requestFocus(direction, mTempRect)) { 4975 playSoundEffect(SoundEffectConstants 4976 .getContantForFocusDirection(direction)); 4977 return true; 4978 } 4979 } 4980 4981 // Give the focused view a last chance to handle the dpad key. 4982 if (mView.dispatchUnhandledMove(focused, direction)) { 4983 return true; 4984 } 4985 } else { 4986 if (mView.restoreDefaultFocus()) { 4987 return true; 4988 } 4989 } 4990 } 4991 return false; 4992 } 4993 4994 private boolean performKeyboardGroupNavigation(int direction) { 4995 final View focused = mView.findFocus(); 4996 if (focused == null && mView.restoreDefaultFocus()) { 4997 return true; 4998 } 4999 View cluster = focused == null ? keyboardNavigationClusterSearch(null, direction) 5000 : focused.keyboardNavigationClusterSearch(null, direction); 5001 5002 // Since requestFocus only takes "real" focus directions (and therefore also 5003 // restoreFocusInCluster), convert forward/backward focus into FOCUS_DOWN. 5004 int realDirection = direction; 5005 if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) { 5006 realDirection = View.FOCUS_DOWN; 5007 } 5008 5009 if (cluster != null && cluster.isRootNamespace()) { 5010 // the default cluster. Try to find a non-clustered view to focus. 5011 if (cluster.restoreFocusNotInCluster()) { 5012 playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); 5013 return true; 5014 } 5015 // otherwise skip to next actual cluster 5016 cluster = keyboardNavigationClusterSearch(null, direction); 5017 } 5018 5019 if (cluster != null && cluster.restoreFocusInCluster(realDirection)) { 5020 playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); 5021 return true; 5022 } 5023 5024 return false; 5025 } 5026 5027 private int processKeyEvent(QueuedInputEvent q) { 5028 final KeyEvent event = (KeyEvent)q.mEvent; 5029 5030 if (mUnhandledKeyManager.preViewDispatch(event)) { 5031 return FINISH_HANDLED; 5032 } 5033 5034 // Deliver the key to the view hierarchy. 5035 if (mView.dispatchKeyEvent(event)) { 5036 return FINISH_HANDLED; 5037 } 5038 5039 if (shouldDropInputEvent(q)) { 5040 return FINISH_NOT_HANDLED; 5041 } 5042 5043 // This dispatch is for windows that don't have a Window.Callback. Otherwise, 5044 // the Window.Callback usually will have already called this (see 5045 // DecorView.superDispatchKeyEvent) leaving this call a no-op. 5046 if (mUnhandledKeyManager.dispatch(mView, event)) { 5047 return FINISH_HANDLED; 5048 } 5049 5050 int groupNavigationDirection = 0; 5051 5052 if (event.getAction() == KeyEvent.ACTION_DOWN 5053 && event.getKeyCode() == KeyEvent.KEYCODE_TAB) { 5054 if (KeyEvent.metaStateHasModifiers(event.getMetaState(), KeyEvent.META_META_ON)) { 5055 groupNavigationDirection = View.FOCUS_FORWARD; 5056 } else if (KeyEvent.metaStateHasModifiers(event.getMetaState(), 5057 KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON)) { 5058 groupNavigationDirection = View.FOCUS_BACKWARD; 5059 } 5060 } 5061 5062 // If a modifier is held, try to interpret the key as a shortcut. 5063 if (event.getAction() == KeyEvent.ACTION_DOWN 5064 && !KeyEvent.metaStateHasNoModifiers(event.getMetaState()) 5065 && event.getRepeatCount() == 0 5066 && !KeyEvent.isModifierKey(event.getKeyCode()) 5067 && groupNavigationDirection == 0) { 5068 if (mView.dispatchKeyShortcutEvent(event)) { 5069 return FINISH_HANDLED; 5070 } 5071 if (shouldDropInputEvent(q)) { 5072 return FINISH_NOT_HANDLED; 5073 } 5074 } 5075 5076 // Apply the fallback event policy. 5077 if (mFallbackEventHandler.dispatchKeyEvent(event)) { 5078 return FINISH_HANDLED; 5079 } 5080 if (shouldDropInputEvent(q)) { 5081 return FINISH_NOT_HANDLED; 5082 } 5083 5084 // Handle automatic focus changes. 5085 if (event.getAction() == KeyEvent.ACTION_DOWN) { 5086 if (groupNavigationDirection != 0) { 5087 if (performKeyboardGroupNavigation(groupNavigationDirection)) { 5088 return FINISH_HANDLED; 5089 } 5090 } else { 5091 if (performFocusNavigation(event)) { 5092 return FINISH_HANDLED; 5093 } 5094 } 5095 } 5096 return FORWARD; 5097 } 5098 5099 private int processPointerEvent(QueuedInputEvent q) { 5100 final MotionEvent event = (MotionEvent)q.mEvent; 5101 5102 mAttachInfo.mUnbufferedDispatchRequested = false; 5103 mAttachInfo.mHandlingPointerEvent = true; 5104 boolean handled = mView.dispatchPointerEvent(event); 5105 maybeUpdatePointerIcon(event); 5106 maybeUpdateTooltip(event); 5107 mAttachInfo.mHandlingPointerEvent = false; 5108 if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { 5109 mUnbufferedInputDispatch = true; 5110 if (mConsumeBatchedInputScheduled) { 5111 scheduleConsumeBatchedInputImmediately(); 5112 } 5113 } 5114 return handled ? FINISH_HANDLED : FORWARD; 5115 } 5116 5117 private void maybeUpdatePointerIcon(MotionEvent event) { 5118 if (event.getPointerCount() == 1 && event.isFromSource(InputDevice.SOURCE_MOUSE)) { 5119 if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER 5120 || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { 5121 // Other apps or the window manager may change the icon type outside of 5122 // this app, therefore the icon type has to be reset on enter/exit event. 5123 mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; 5124 } 5125 5126 if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { 5127 if (!updatePointerIcon(event) && 5128 event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) { 5129 mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; 5130 } 5131 } 5132 } 5133 } 5134 5135 private int processTrackballEvent(QueuedInputEvent q) { 5136 final MotionEvent event = (MotionEvent)q.mEvent; 5137 5138 if (event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) { 5139 if (!hasPointerCapture() || mView.dispatchCapturedPointerEvent(event)) { 5140 return FINISH_HANDLED; 5141 } 5142 } 5143 5144 if (mView.dispatchTrackballEvent(event)) { 5145 return FINISH_HANDLED; 5146 } 5147 return FORWARD; 5148 } 5149 5150 private int processGenericMotionEvent(QueuedInputEvent q) { 5151 final MotionEvent event = (MotionEvent)q.mEvent; 5152 5153 // Deliver the event to the view. 5154 if (mView.dispatchGenericMotionEvent(event)) { 5155 return FINISH_HANDLED; 5156 } 5157 return FORWARD; 5158 } 5159 } 5160 5161 private void resetPointerIcon(MotionEvent event) { 5162 mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; 5163 updatePointerIcon(event); 5164 } 5165 5166 private boolean updatePointerIcon(MotionEvent event) { 5167 final int pointerIndex = 0; 5168 final float x = event.getX(pointerIndex); 5169 final float y = event.getY(pointerIndex); 5170 if (mView == null) { 5171 // E.g. click outside a popup to dismiss it 5172 Slog.d(mTag, "updatePointerIcon called after view was removed"); 5173 return false; 5174 } 5175 if (x < 0 || x >= mView.getWidth() || y < 0 || y >= mView.getHeight()) { 5176 // E.g. when moving window divider with mouse 5177 Slog.d(mTag, "updatePointerIcon called with position out of bounds"); 5178 return false; 5179 } 5180 final PointerIcon pointerIcon = mView.onResolvePointerIcon(event, pointerIndex); 5181 final int pointerType = (pointerIcon != null) ? 5182 pointerIcon.getType() : PointerIcon.TYPE_DEFAULT; 5183 5184 if (mPointerIconType != pointerType) { 5185 mPointerIconType = pointerType; 5186 mCustomPointerIcon = null; 5187 if (mPointerIconType != PointerIcon.TYPE_CUSTOM) { 5188 InputManager.getInstance().setPointerIconType(pointerType); 5189 return true; 5190 } 5191 } 5192 if (mPointerIconType == PointerIcon.TYPE_CUSTOM && 5193 !pointerIcon.equals(mCustomPointerIcon)) { 5194 mCustomPointerIcon = pointerIcon; 5195 InputManager.getInstance().setCustomPointerIcon(mCustomPointerIcon); 5196 } 5197 return true; 5198 } 5199 5200 private void maybeUpdateTooltip(MotionEvent event) { 5201 if (event.getPointerCount() != 1) { 5202 return; 5203 } 5204 final int action = event.getActionMasked(); 5205 if (action != MotionEvent.ACTION_HOVER_ENTER 5206 && action != MotionEvent.ACTION_HOVER_MOVE 5207 && action != MotionEvent.ACTION_HOVER_EXIT) { 5208 return; 5209 } 5210 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 5211 if (manager.isEnabled() && manager.isTouchExplorationEnabled()) { 5212 return; 5213 } 5214 if (mView == null) { 5215 Slog.d(mTag, "maybeUpdateTooltip called after view was removed"); 5216 return; 5217 } 5218 mView.dispatchTooltipHoverEvent(event); 5219 } 5220 5221 /** 5222 * Performs synthesis of new input events from unhandled input events. 5223 */ 5224 final class SyntheticInputStage extends InputStage { 5225 private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler(); 5226 private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler(); 5227 private final SyntheticTouchNavigationHandler mTouchNavigation = 5228 new SyntheticTouchNavigationHandler(); 5229 private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler(); 5230 5231 public SyntheticInputStage() { 5232 super(null); 5233 } 5234 5235 @Override 5236 protected int onProcess(QueuedInputEvent q) { 5237 q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED; 5238 if (q.mEvent instanceof MotionEvent) { 5239 final MotionEvent event = (MotionEvent)q.mEvent; 5240 final int source = event.getSource(); 5241 if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 5242 mTrackball.process(event); 5243 return FINISH_HANDLED; 5244 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 5245 mJoystick.process(event); 5246 return FINISH_HANDLED; 5247 } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) 5248 == InputDevice.SOURCE_TOUCH_NAVIGATION) { 5249 mTouchNavigation.process(event); 5250 return FINISH_HANDLED; 5251 } 5252 } else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) { 5253 mKeyboard.process((KeyEvent)q.mEvent); 5254 return FINISH_HANDLED; 5255 } 5256 5257 return FORWARD; 5258 } 5259 5260 @Override 5261 protected void onDeliverToNext(QueuedInputEvent q) { 5262 if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) { 5263 // Cancel related synthetic events if any prior stage has handled the event. 5264 if (q.mEvent instanceof MotionEvent) { 5265 final MotionEvent event = (MotionEvent)q.mEvent; 5266 final int source = event.getSource(); 5267 if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 5268 mTrackball.cancel(); 5269 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 5270 mJoystick.cancel(); 5271 } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) 5272 == InputDevice.SOURCE_TOUCH_NAVIGATION) { 5273 mTouchNavigation.cancel(event); 5274 } 5275 } 5276 } 5277 super.onDeliverToNext(q); 5278 } 5279 5280 @Override 5281 protected void onWindowFocusChanged(boolean hasWindowFocus) { 5282 if (!hasWindowFocus) { 5283 mJoystick.cancel(); 5284 } 5285 } 5286 5287 @Override 5288 protected void onDetachedFromWindow() { 5289 mJoystick.cancel(); 5290 } 5291 } 5292 5293 /** 5294 * Creates dpad events from unhandled trackball movements. 5295 */ 5296 final class SyntheticTrackballHandler { 5297 private final TrackballAxis mX = new TrackballAxis(); 5298 private final TrackballAxis mY = new TrackballAxis(); 5299 private long mLastTime; 5300 5301 public void process(MotionEvent event) { 5302 // Translate the trackball event into DPAD keys and try to deliver those. 5303 long curTime = SystemClock.uptimeMillis(); 5304 if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) { 5305 // It has been too long since the last movement, 5306 // so restart at the beginning. 5307 mX.reset(0); 5308 mY.reset(0); 5309 mLastTime = curTime; 5310 } 5311 5312 final int action = event.getAction(); 5313 final int metaState = event.getMetaState(); 5314 switch (action) { 5315 case MotionEvent.ACTION_DOWN: 5316 mX.reset(2); 5317 mY.reset(2); 5318 enqueueInputEvent(new KeyEvent(curTime, curTime, 5319 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 5320 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 5321 InputDevice.SOURCE_KEYBOARD)); 5322 break; 5323 case MotionEvent.ACTION_UP: 5324 mX.reset(2); 5325 mY.reset(2); 5326 enqueueInputEvent(new KeyEvent(curTime, curTime, 5327 KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState, 5328 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 5329 InputDevice.SOURCE_KEYBOARD)); 5330 break; 5331 } 5332 5333 if (DEBUG_TRACKBALL) Log.v(mTag, "TB X=" + mX.position + " step=" 5334 + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration 5335 + " move=" + event.getX() 5336 + " / Y=" + mY.position + " step=" 5337 + mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration 5338 + " move=" + event.getY()); 5339 final float xOff = mX.collect(event.getX(), event.getEventTime(), "X"); 5340 final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y"); 5341 5342 // Generate DPAD events based on the trackball movement. 5343 // We pick the axis that has moved the most as the direction of 5344 // the DPAD. When we generate DPAD events for one axis, then the 5345 // other axis is reset -- we don't want to perform DPAD jumps due 5346 // to slight movements in the trackball when making major movements 5347 // along the other axis. 5348 int keycode = 0; 5349 int movement = 0; 5350 float accel = 1; 5351 if (xOff > yOff) { 5352 movement = mX.generate(); 5353 if (movement != 0) { 5354 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT 5355 : KeyEvent.KEYCODE_DPAD_LEFT; 5356 accel = mX.acceleration; 5357 mY.reset(2); 5358 } 5359 } else if (yOff > 0) { 5360 movement = mY.generate(); 5361 if (movement != 0) { 5362 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN 5363 : KeyEvent.KEYCODE_DPAD_UP; 5364 accel = mY.acceleration; 5365 mX.reset(2); 5366 } 5367 } 5368 5369 if (keycode != 0) { 5370 if (movement < 0) movement = -movement; 5371 int accelMovement = (int)(movement * accel); 5372 if (DEBUG_TRACKBALL) Log.v(mTag, "Move: movement=" + movement 5373 + " accelMovement=" + accelMovement 5374 + " accel=" + accel); 5375 if (accelMovement > movement) { 5376 if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: " 5377 + keycode); 5378 movement--; 5379 int repeatCount = accelMovement - movement; 5380 enqueueInputEvent(new KeyEvent(curTime, curTime, 5381 KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState, 5382 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 5383 InputDevice.SOURCE_KEYBOARD)); 5384 } 5385 while (movement > 0) { 5386 if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: " 5387 + keycode); 5388 movement--; 5389 curTime = SystemClock.uptimeMillis(); 5390 enqueueInputEvent(new KeyEvent(curTime, curTime, 5391 KeyEvent.ACTION_DOWN, keycode, 0, metaState, 5392 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 5393 InputDevice.SOURCE_KEYBOARD)); 5394 enqueueInputEvent(new KeyEvent(curTime, curTime, 5395 KeyEvent.ACTION_UP, keycode, 0, metaState, 5396 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK, 5397 InputDevice.SOURCE_KEYBOARD)); 5398 } 5399 mLastTime = curTime; 5400 } 5401 } 5402 5403 public void cancel() { 5404 mLastTime = Integer.MIN_VALUE; 5405 5406 // If we reach this, we consumed a trackball event. 5407 // Because we will not translate the trackball event into a key event, 5408 // touch mode will not exit, so we exit touch mode here. 5409 if (mView != null && mAdded) { 5410 ensureTouchMode(false); 5411 } 5412 } 5413 } 5414 5415 /** 5416 * Maintains state information for a single trackball axis, generating 5417 * discrete (DPAD) movements based on raw trackball motion. 5418 */ 5419 static final class TrackballAxis { 5420 /** 5421 * The maximum amount of acceleration we will apply. 5422 */ 5423 static final float MAX_ACCELERATION = 20; 5424 5425 /** 5426 * The maximum amount of time (in milliseconds) between events in order 5427 * for us to consider the user to be doing fast trackball movements, 5428 * and thus apply an acceleration. 5429 */ 5430 static final long FAST_MOVE_TIME = 150; 5431 5432 /** 5433 * Scaling factor to the time (in milliseconds) between events to how 5434 * much to multiple/divide the current acceleration. When movement 5435 * is < FAST_MOVE_TIME this multiplies the acceleration; when > 5436 * FAST_MOVE_TIME it divides it. 5437 */ 5438 static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); 5439 5440 static final float FIRST_MOVEMENT_THRESHOLD = 0.5f; 5441 static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f; 5442 static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f; 5443 5444 float position; 5445 float acceleration = 1; 5446 long lastMoveTime = 0; 5447 int step; 5448 int dir; 5449 int nonAccelMovement; 5450 5451 void reset(int _step) { 5452 position = 0; 5453 acceleration = 1; 5454 lastMoveTime = 0; 5455 step = _step; 5456 dir = 0; 5457 } 5458 5459 /** 5460 * Add trackball movement into the state. If the direction of movement 5461 * has been reversed, the state is reset before adding the 5462 * movement (so that you don't have to compensate for any previously 5463 * collected movement before see the result of the movement in the 5464 * new direction). 5465 * 5466 * @return Returns the absolute value of the amount of movement 5467 * collected so far. 5468 */ 5469 float collect(float off, long time, String axis) { 5470 long normTime; 5471 if (off > 0) { 5472 normTime = (long)(off * FAST_MOVE_TIME); 5473 if (dir < 0) { 5474 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!"); 5475 position = 0; 5476 step = 0; 5477 acceleration = 1; 5478 lastMoveTime = 0; 5479 } 5480 dir = 1; 5481 } else if (off < 0) { 5482 normTime = (long)((-off) * FAST_MOVE_TIME); 5483 if (dir > 0) { 5484 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!"); 5485 position = 0; 5486 step = 0; 5487 acceleration = 1; 5488 lastMoveTime = 0; 5489 } 5490 dir = -1; 5491 } else { 5492 normTime = 0; 5493 } 5494 5495 // The number of milliseconds between each movement that is 5496 // considered "normal" and will not result in any acceleration 5497 // or deceleration, scaled by the offset we have here. 5498 if (normTime > 0) { 5499 long delta = time - lastMoveTime; 5500 lastMoveTime = time; 5501 float acc = acceleration; 5502 if (delta < normTime) { 5503 // The user is scrolling rapidly, so increase acceleration. 5504 float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR; 5505 if (scale > 1) acc *= scale; 5506 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off=" 5507 + off + " normTime=" + normTime + " delta=" + delta 5508 + " scale=" + scale + " acc=" + acc); 5509 acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION; 5510 } else { 5511 // The user is scrolling slowly, so decrease acceleration. 5512 float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR; 5513 if (scale > 1) acc /= scale; 5514 if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off=" 5515 + off + " normTime=" + normTime + " delta=" + delta 5516 + " scale=" + scale + " acc=" + acc); 5517 acceleration = acc > 1 ? acc : 1; 5518 } 5519 } 5520 position += off; 5521 return Math.abs(position); 5522 } 5523 5524 /** 5525 * Generate the number of discrete movement events appropriate for 5526 * the currently collected trackball movement. 5527 * 5528 * @return Returns the number of discrete movements, either positive 5529 * or negative, or 0 if there is not enough trackball movement yet 5530 * for a discrete movement. 5531 */ 5532 int generate() { 5533 int movement = 0; 5534 nonAccelMovement = 0; 5535 do { 5536 final int dir = position >= 0 ? 1 : -1; 5537 switch (step) { 5538 // If we are going to execute the first step, then we want 5539 // to do this as soon as possible instead of waiting for 5540 // a full movement, in order to make things look responsive. 5541 case 0: 5542 if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) { 5543 return movement; 5544 } 5545 movement += dir; 5546 nonAccelMovement += dir; 5547 step = 1; 5548 break; 5549 // If we have generated the first movement, then we need 5550 // to wait for the second complete trackball motion before 5551 // generating the second discrete movement. 5552 case 1: 5553 if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) { 5554 return movement; 5555 } 5556 movement += dir; 5557 nonAccelMovement += dir; 5558 position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir; 5559 step = 2; 5560 break; 5561 // After the first two, we generate discrete movements 5562 // consistently with the trackball, applying an acceleration 5563 // if the trackball is moving quickly. This is a simple 5564 // acceleration on top of what we already compute based 5565 // on how quickly the wheel is being turned, to apply 5566 // a longer increasing acceleration to continuous movement 5567 // in one direction. 5568 default: 5569 if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) { 5570 return movement; 5571 } 5572 movement += dir; 5573 position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD; 5574 float acc = acceleration; 5575 acc *= 1.1f; 5576 acceleration = acc < MAX_ACCELERATION ? acc : acceleration; 5577 break; 5578 } 5579 } while (true); 5580 } 5581 } 5582 5583 /** 5584 * Creates dpad events from unhandled joystick movements. 5585 */ 5586 final class SyntheticJoystickHandler extends Handler { 5587 private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1; 5588 private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2; 5589 5590 private final JoystickAxesState mJoystickAxesState = new JoystickAxesState(); 5591 private final SparseArray<KeyEvent> mDeviceKeyEvents = new SparseArray<>(); 5592 5593 public SyntheticJoystickHandler() { 5594 super(true); 5595 } 5596 5597 @Override 5598 public void handleMessage(Message msg) { 5599 switch (msg.what) { 5600 case MSG_ENQUEUE_X_AXIS_KEY_REPEAT: 5601 case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: { 5602 if (mAttachInfo.mHasWindowFocus) { 5603 KeyEvent oldEvent = (KeyEvent) msg.obj; 5604 KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, 5605 SystemClock.uptimeMillis(), oldEvent.getRepeatCount() + 1); 5606 enqueueInputEvent(e); 5607 Message m = obtainMessage(msg.what, e); 5608 m.setAsynchronous(true); 5609 sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay()); 5610 } 5611 } break; 5612 } 5613 } 5614 5615 public void process(MotionEvent event) { 5616 switch(event.getActionMasked()) { 5617 case MotionEvent.ACTION_CANCEL: 5618 cancel(); 5619 break; 5620 case MotionEvent.ACTION_MOVE: 5621 update(event); 5622 break; 5623 default: 5624 Log.w(mTag, "Unexpected action: " + event.getActionMasked()); 5625 } 5626 } 5627 5628 private void cancel() { 5629 removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT); 5630 removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT); 5631 for (int i = 0; i < mDeviceKeyEvents.size(); i++) { 5632 final KeyEvent keyEvent = mDeviceKeyEvents.valueAt(i); 5633 if (keyEvent != null) { 5634 enqueueInputEvent(KeyEvent.changeTimeRepeat(keyEvent, 5635 SystemClock.uptimeMillis(), 0)); 5636 } 5637 } 5638 mDeviceKeyEvents.clear(); 5639 mJoystickAxesState.resetState(); 5640 } 5641 5642 private void update(MotionEvent event) { 5643 final int historySize = event.getHistorySize(); 5644 for (int h = 0; h < historySize; h++) { 5645 final long time = event.getHistoricalEventTime(h); 5646 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X, 5647 event.getHistoricalAxisValue(MotionEvent.AXIS_X, 0, h)); 5648 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y, 5649 event.getHistoricalAxisValue(MotionEvent.AXIS_Y, 0, h)); 5650 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X, 5651 event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_X, 0, h)); 5652 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y, 5653 event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_Y, 0, h)); 5654 } 5655 final long time = event.getEventTime(); 5656 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X, 5657 event.getAxisValue(MotionEvent.AXIS_X)); 5658 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y, 5659 event.getAxisValue(MotionEvent.AXIS_Y)); 5660 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X, 5661 event.getAxisValue(MotionEvent.AXIS_HAT_X)); 5662 mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y, 5663 event.getAxisValue(MotionEvent.AXIS_HAT_Y)); 5664 } 5665 5666 final class JoystickAxesState { 5667 // State machine: from neutral state (no button press) can go into 5668 // button STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state, emitting an ACTION_DOWN event. 5669 // From STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state can go into neutral state, 5670 // emitting an ACTION_UP event. 5671 private static final int STATE_UP_OR_LEFT = -1; 5672 private static final int STATE_NEUTRAL = 0; 5673 private static final int STATE_DOWN_OR_RIGHT = 1; 5674 5675 final int[] mAxisStatesHat = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_HAT_X, AXIS_HAT_Y} 5676 final int[] mAxisStatesStick = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_X, AXIS_Y} 5677 5678 void resetState() { 5679 mAxisStatesHat[0] = STATE_NEUTRAL; 5680 mAxisStatesHat[1] = STATE_NEUTRAL; 5681 mAxisStatesStick[0] = STATE_NEUTRAL; 5682 mAxisStatesStick[1] = STATE_NEUTRAL; 5683 } 5684 5685 void updateStateForAxis(MotionEvent event, long time, int axis, float value) { 5686 // Emit KeyEvent if necessary 5687 // axis can be AXIS_X, AXIS_Y, AXIS_HAT_X, AXIS_HAT_Y 5688 final int axisStateIndex; 5689 final int repeatMessage; 5690 if (isXAxis(axis)) { 5691 axisStateIndex = 0; 5692 repeatMessage = MSG_ENQUEUE_X_AXIS_KEY_REPEAT; 5693 } else if (isYAxis(axis)) { 5694 axisStateIndex = 1; 5695 repeatMessage = MSG_ENQUEUE_Y_AXIS_KEY_REPEAT; 5696 } else { 5697 Log.e(mTag, "Unexpected axis " + axis + " in updateStateForAxis!"); 5698 return; 5699 } 5700 final int newState = joystickAxisValueToState(value); 5701 5702 final int currentState; 5703 if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) { 5704 currentState = mAxisStatesStick[axisStateIndex]; 5705 } else { 5706 currentState = mAxisStatesHat[axisStateIndex]; 5707 } 5708 5709 if (currentState == newState) { 5710 return; 5711 } 5712 5713 final int metaState = event.getMetaState(); 5714 final int deviceId = event.getDeviceId(); 5715 final int source = event.getSource(); 5716 5717 if (currentState == STATE_DOWN_OR_RIGHT || currentState == STATE_UP_OR_LEFT) { 5718 // send a button release event 5719 final int keyCode = joystickAxisAndStateToKeycode(axis, currentState); 5720 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 5721 enqueueInputEvent(new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode, 5722 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source)); 5723 // remove the corresponding pending UP event if focus lost/view detached 5724 mDeviceKeyEvents.put(deviceId, null); 5725 } 5726 removeMessages(repeatMessage); 5727 } 5728 5729 if (newState == STATE_DOWN_OR_RIGHT || newState == STATE_UP_OR_LEFT) { 5730 // send a button down event 5731 final int keyCode = joystickAxisAndStateToKeycode(axis, newState); 5732 if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 5733 KeyEvent keyEvent = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode, 5734 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source); 5735 enqueueInputEvent(keyEvent); 5736 Message m = obtainMessage(repeatMessage, keyEvent); 5737 m.setAsynchronous(true); 5738 sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout()); 5739 // store the corresponding ACTION_UP event so that it can be sent 5740 // if focus is lost or root view is removed 5741 mDeviceKeyEvents.put(deviceId, 5742 new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode, 5743 0, metaState, deviceId, 0, 5744 KeyEvent.FLAG_FALLBACK | KeyEvent.FLAG_CANCELED, 5745 source)); 5746 } 5747 } 5748 if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) { 5749 mAxisStatesStick[axisStateIndex] = newState; 5750 } else { 5751 mAxisStatesHat[axisStateIndex] = newState; 5752 } 5753 } 5754 5755 private boolean isXAxis(int axis) { 5756 return axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_HAT_X; 5757 } 5758 private boolean isYAxis(int axis) { 5759 return axis == MotionEvent.AXIS_Y || axis == MotionEvent.AXIS_HAT_Y; 5760 } 5761 5762 private int joystickAxisAndStateToKeycode(int axis, int state) { 5763 if (isXAxis(axis) && state == STATE_UP_OR_LEFT) { 5764 return KeyEvent.KEYCODE_DPAD_LEFT; 5765 } 5766 if (isXAxis(axis) && state == STATE_DOWN_OR_RIGHT) { 5767 return KeyEvent.KEYCODE_DPAD_RIGHT; 5768 } 5769 if (isYAxis(axis) && state == STATE_UP_OR_LEFT) { 5770 return KeyEvent.KEYCODE_DPAD_UP; 5771 } 5772 if (isYAxis(axis) && state == STATE_DOWN_OR_RIGHT) { 5773 return KeyEvent.KEYCODE_DPAD_DOWN; 5774 } 5775 Log.e(mTag, "Unknown axis " + axis + " or direction " + state); 5776 return KeyEvent.KEYCODE_UNKNOWN; // should never happen 5777 } 5778 5779 private int joystickAxisValueToState(float value) { 5780 if (value >= 0.5f) { 5781 return STATE_DOWN_OR_RIGHT; 5782 } else if (value <= -0.5f) { 5783 return STATE_UP_OR_LEFT; 5784 } else { 5785 return STATE_NEUTRAL; 5786 } 5787 } 5788 } 5789 } 5790 5791 /** 5792 * Creates dpad events from unhandled touch navigation movements. 5793 */ 5794 final class SyntheticTouchNavigationHandler extends Handler { 5795 private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler"; 5796 private static final boolean LOCAL_DEBUG = false; 5797 5798 // Assumed nominal width and height in millimeters of a touch navigation pad, 5799 // if no resolution information is available from the input system. 5800 private static final float DEFAULT_WIDTH_MILLIMETERS = 48; 5801 private static final float DEFAULT_HEIGHT_MILLIMETERS = 48; 5802 5803 /* TODO: These constants should eventually be moved to ViewConfiguration. */ 5804 5805 // The nominal distance traveled to move by one unit. 5806 private static final int TICK_DISTANCE_MILLIMETERS = 12; 5807 5808 // Minimum and maximum fling velocity in ticks per second. 5809 // The minimum velocity should be set such that we perform enough ticks per 5810 // second that the fling appears to be fluid. For example, if we set the minimum 5811 // to 2 ticks per second, then there may be up to half a second delay between the next 5812 // to last and last ticks which is noticeably discrete and jerky. This value should 5813 // probably not be set to anything less than about 4. 5814 // If fling accuracy is a problem then consider tuning the tick distance instead. 5815 private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f; 5816 private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f; 5817 5818 // Fling velocity decay factor applied after each new key is emitted. 5819 // This parameter controls the deceleration and overall duration of the fling. 5820 // The fling stops automatically when its velocity drops below the minimum 5821 // fling velocity defined above. 5822 private static final float FLING_TICK_DECAY = 0.8f; 5823 5824 /* The input device that we are tracking. */ 5825 5826 private int mCurrentDeviceId = -1; 5827 private int mCurrentSource; 5828 private boolean mCurrentDeviceSupported; 5829 5830 /* Configuration for the current input device. */ 5831 5832 // The scaled tick distance. A movement of this amount should generally translate 5833 // into a single dpad event in a given direction. 5834 private float mConfigTickDistance; 5835 5836 // The minimum and maximum scaled fling velocity. 5837 private float mConfigMinFlingVelocity; 5838 private float mConfigMaxFlingVelocity; 5839 5840 /* Tracking state. */ 5841 5842 // The velocity tracker for detecting flings. 5843 private VelocityTracker mVelocityTracker; 5844 5845 // The active pointer id, or -1 if none. 5846 private int mActivePointerId = -1; 5847 5848 // Location where tracking started. 5849 private float mStartX; 5850 private float mStartY; 5851 5852 // Most recently observed position. 5853 private float mLastX; 5854 private float mLastY; 5855 5856 // Accumulated movement delta since the last direction key was sent. 5857 private float mAccumulatedX; 5858 private float mAccumulatedY; 5859 5860 // Set to true if any movement was delivered to the app. 5861 // Implies that tap slop was exceeded. 5862 private boolean mConsumedMovement; 5863 5864 // The most recently sent key down event. 5865 // The keycode remains set until the direction changes or a fling ends 5866 // so that repeated key events may be generated as required. 5867 private long mPendingKeyDownTime; 5868 private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; 5869 private int mPendingKeyRepeatCount; 5870 private int mPendingKeyMetaState; 5871 5872 // The current fling velocity while a fling is in progress. 5873 private boolean mFlinging; 5874 private float mFlingVelocity; 5875 5876 public SyntheticTouchNavigationHandler() { 5877 super(true); 5878 } 5879 5880 public void process(MotionEvent event) { 5881 // Update the current device information. 5882 final long time = event.getEventTime(); 5883 final int deviceId = event.getDeviceId(); 5884 final int source = event.getSource(); 5885 if (mCurrentDeviceId != deviceId || mCurrentSource != source) { 5886 finishKeys(time); 5887 finishTracking(time); 5888 mCurrentDeviceId = deviceId; 5889 mCurrentSource = source; 5890 mCurrentDeviceSupported = false; 5891 InputDevice device = event.getDevice(); 5892 if (device != null) { 5893 // In order to support an input device, we must know certain 5894 // characteristics about it, such as its size and resolution. 5895 InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X); 5896 InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y); 5897 if (xRange != null && yRange != null) { 5898 mCurrentDeviceSupported = true; 5899 5900 // Infer the resolution if it not actually known. 5901 float xRes = xRange.getResolution(); 5902 if (xRes <= 0) { 5903 xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS; 5904 } 5905 float yRes = yRange.getResolution(); 5906 if (yRes <= 0) { 5907 yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS; 5908 } 5909 float nominalRes = (xRes + yRes) * 0.5f; 5910 5911 // Precompute all of the configuration thresholds we will need. 5912 mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes; 5913 mConfigMinFlingVelocity = 5914 MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; 5915 mConfigMaxFlingVelocity = 5916 MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; 5917 5918 if (LOCAL_DEBUG) { 5919 Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId 5920 + " (" + Integer.toHexString(mCurrentSource) + "): " 5921 + ", mConfigTickDistance=" + mConfigTickDistance 5922 + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity 5923 + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity); 5924 } 5925 } 5926 } 5927 } 5928 if (!mCurrentDeviceSupported) { 5929 return; 5930 } 5931 5932 // Handle the event. 5933 final int action = event.getActionMasked(); 5934 switch (action) { 5935 case MotionEvent.ACTION_DOWN: { 5936 boolean caughtFling = mFlinging; 5937 finishKeys(time); 5938 finishTracking(time); 5939 mActivePointerId = event.getPointerId(0); 5940 mVelocityTracker = VelocityTracker.obtain(); 5941 mVelocityTracker.addMovement(event); 5942 mStartX = event.getX(); 5943 mStartY = event.getY(); 5944 mLastX = mStartX; 5945 mLastY = mStartY; 5946 mAccumulatedX = 0; 5947 mAccumulatedY = 0; 5948 5949 // If we caught a fling, then pretend that the tap slop has already 5950 // been exceeded to suppress taps whose only purpose is to stop the fling. 5951 mConsumedMovement = caughtFling; 5952 break; 5953 } 5954 5955 case MotionEvent.ACTION_MOVE: 5956 case MotionEvent.ACTION_UP: { 5957 if (mActivePointerId < 0) { 5958 break; 5959 } 5960 final int index = event.findPointerIndex(mActivePointerId); 5961 if (index < 0) { 5962 finishKeys(time); 5963 finishTracking(time); 5964 break; 5965 } 5966 5967 mVelocityTracker.addMovement(event); 5968 final float x = event.getX(index); 5969 final float y = event.getY(index); 5970 mAccumulatedX += x - mLastX; 5971 mAccumulatedY += y - mLastY; 5972 mLastX = x; 5973 mLastY = y; 5974 5975 // Consume any accumulated movement so far. 5976 final int metaState = event.getMetaState(); 5977 consumeAccumulatedMovement(time, metaState); 5978 5979 // Detect taps and flings. 5980 if (action == MotionEvent.ACTION_UP) { 5981 if (mConsumedMovement && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { 5982 // It might be a fling. 5983 mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity); 5984 final float vx = mVelocityTracker.getXVelocity(mActivePointerId); 5985 final float vy = mVelocityTracker.getYVelocity(mActivePointerId); 5986 if (!startFling(time, vx, vy)) { 5987 finishKeys(time); 5988 } 5989 } 5990 finishTracking(time); 5991 } 5992 break; 5993 } 5994 5995 case MotionEvent.ACTION_CANCEL: { 5996 finishKeys(time); 5997 finishTracking(time); 5998 break; 5999 } 6000 } 6001 } 6002 6003 public void cancel(MotionEvent event) { 6004 if (mCurrentDeviceId == event.getDeviceId() 6005 && mCurrentSource == event.getSource()) { 6006 final long time = event.getEventTime(); 6007 finishKeys(time); 6008 finishTracking(time); 6009 } 6010 } 6011 6012 private void finishKeys(long time) { 6013 cancelFling(); 6014 sendKeyUp(time); 6015 } 6016 6017 private void finishTracking(long time) { 6018 if (mActivePointerId >= 0) { 6019 mActivePointerId = -1; 6020 mVelocityTracker.recycle(); 6021 mVelocityTracker = null; 6022 } 6023 } 6024 6025 private void consumeAccumulatedMovement(long time, int metaState) { 6026 final float absX = Math.abs(mAccumulatedX); 6027 final float absY = Math.abs(mAccumulatedY); 6028 if (absX >= absY) { 6029 if (absX >= mConfigTickDistance) { 6030 mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX, 6031 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT); 6032 mAccumulatedY = 0; 6033 mConsumedMovement = true; 6034 } 6035 } else { 6036 if (absY >= mConfigTickDistance) { 6037 mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY, 6038 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN); 6039 mAccumulatedX = 0; 6040 mConsumedMovement = true; 6041 } 6042 } 6043 } 6044 6045 private float consumeAccumulatedMovement(long time, int metaState, 6046 float accumulator, int negativeKeyCode, int positiveKeyCode) { 6047 while (accumulator <= -mConfigTickDistance) { 6048 sendKeyDownOrRepeat(time, negativeKeyCode, metaState); 6049 accumulator += mConfigTickDistance; 6050 } 6051 while (accumulator >= mConfigTickDistance) { 6052 sendKeyDownOrRepeat(time, positiveKeyCode, metaState); 6053 accumulator -= mConfigTickDistance; 6054 } 6055 return accumulator; 6056 } 6057 6058 private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) { 6059 if (mPendingKeyCode != keyCode) { 6060 sendKeyUp(time); 6061 mPendingKeyDownTime = time; 6062 mPendingKeyCode = keyCode; 6063 mPendingKeyRepeatCount = 0; 6064 } else { 6065 mPendingKeyRepeatCount += 1; 6066 } 6067 mPendingKeyMetaState = metaState; 6068 6069 // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1 6070 // but it doesn't quite make sense when simulating the events in this way. 6071 if (LOCAL_DEBUG) { 6072 Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode 6073 + ", repeatCount=" + mPendingKeyRepeatCount 6074 + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); 6075 } 6076 enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, 6077 KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount, 6078 mPendingKeyMetaState, mCurrentDeviceId, 6079 KeyEvent.FLAG_FALLBACK, mCurrentSource)); 6080 } 6081 6082 private void sendKeyUp(long time) { 6083 if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { 6084 if (LOCAL_DEBUG) { 6085 Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode 6086 + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); 6087 } 6088 enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, 6089 KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState, 6090 mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK, 6091 mCurrentSource)); 6092 mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; 6093 } 6094 } 6095 6096 private boolean startFling(long time, float vx, float vy) { 6097 if (LOCAL_DEBUG) { 6098 Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy 6099 + ", min=" + mConfigMinFlingVelocity); 6100 } 6101 6102 // Flings must be oriented in the same direction as the preceding movements. 6103 switch (mPendingKeyCode) { 6104 case KeyEvent.KEYCODE_DPAD_LEFT: 6105 if (-vx >= mConfigMinFlingVelocity 6106 && Math.abs(vy) < mConfigMinFlingVelocity) { 6107 mFlingVelocity = -vx; 6108 break; 6109 } 6110 return false; 6111 6112 case KeyEvent.KEYCODE_DPAD_RIGHT: 6113 if (vx >= mConfigMinFlingVelocity 6114 && Math.abs(vy) < mConfigMinFlingVelocity) { 6115 mFlingVelocity = vx; 6116 break; 6117 } 6118 return false; 6119 6120 case KeyEvent.KEYCODE_DPAD_UP: 6121 if (-vy >= mConfigMinFlingVelocity 6122 && Math.abs(vx) < mConfigMinFlingVelocity) { 6123 mFlingVelocity = -vy; 6124 break; 6125 } 6126 return false; 6127 6128 case KeyEvent.KEYCODE_DPAD_DOWN: 6129 if (vy >= mConfigMinFlingVelocity 6130 && Math.abs(vx) < mConfigMinFlingVelocity) { 6131 mFlingVelocity = vy; 6132 break; 6133 } 6134 return false; 6135 } 6136 6137 // Post the first fling event. 6138 mFlinging = postFling(time); 6139 return mFlinging; 6140 } 6141 6142 private boolean postFling(long time) { 6143 // The idea here is to estimate the time when the pointer would have 6144 // traveled one tick distance unit given the current fling velocity. 6145 // This effect creates continuity of motion. 6146 if (mFlingVelocity >= mConfigMinFlingVelocity) { 6147 long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000); 6148 postAtTime(mFlingRunnable, time + delay); 6149 if (LOCAL_DEBUG) { 6150 Log.d(LOCAL_TAG, "Posted fling: velocity=" 6151 + mFlingVelocity + ", delay=" + delay 6152 + ", keyCode=" + mPendingKeyCode); 6153 } 6154 return true; 6155 } 6156 return false; 6157 } 6158 6159 private void cancelFling() { 6160 if (mFlinging) { 6161 removeCallbacks(mFlingRunnable); 6162 mFlinging = false; 6163 } 6164 } 6165 6166 private final Runnable mFlingRunnable = new Runnable() { 6167 @Override 6168 public void run() { 6169 final long time = SystemClock.uptimeMillis(); 6170 sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState); 6171 mFlingVelocity *= FLING_TICK_DECAY; 6172 if (!postFling(time)) { 6173 mFlinging = false; 6174 finishKeys(time); 6175 } 6176 } 6177 }; 6178 } 6179 6180 final class SyntheticKeyboardHandler { 6181 public void process(KeyEvent event) { 6182 if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) { 6183 return; 6184 } 6185 6186 final KeyCharacterMap kcm = event.getKeyCharacterMap(); 6187 final int keyCode = event.getKeyCode(); 6188 final int metaState = event.getMetaState(); 6189 6190 // Check for fallback actions specified by the key character map. 6191 KeyCharacterMap.FallbackAction fallbackAction = 6192 kcm.getFallbackAction(keyCode, metaState); 6193 if (fallbackAction != null) { 6194 final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK; 6195 KeyEvent fallbackEvent = KeyEvent.obtain( 6196 event.getDownTime(), event.getEventTime(), 6197 event.getAction(), fallbackAction.keyCode, 6198 event.getRepeatCount(), fallbackAction.metaState, 6199 event.getDeviceId(), event.getScanCode(), 6200 flags, event.getSource(), null); 6201 fallbackAction.recycle(); 6202 enqueueInputEvent(fallbackEvent); 6203 } 6204 } 6205 } 6206 6207 /** 6208 * Returns true if the key is used for keyboard navigation. 6209 * @param keyEvent The key event. 6210 * @return True if the key is used for keyboard navigation. 6211 */ 6212 private static boolean isNavigationKey(KeyEvent keyEvent) { 6213 switch (keyEvent.getKeyCode()) { 6214 case KeyEvent.KEYCODE_DPAD_LEFT: 6215 case KeyEvent.KEYCODE_DPAD_RIGHT: 6216 case KeyEvent.KEYCODE_DPAD_UP: 6217 case KeyEvent.KEYCODE_DPAD_DOWN: 6218 case KeyEvent.KEYCODE_DPAD_CENTER: 6219 case KeyEvent.KEYCODE_PAGE_UP: 6220 case KeyEvent.KEYCODE_PAGE_DOWN: 6221 case KeyEvent.KEYCODE_MOVE_HOME: 6222 case KeyEvent.KEYCODE_MOVE_END: 6223 case KeyEvent.KEYCODE_TAB: 6224 case KeyEvent.KEYCODE_SPACE: 6225 case KeyEvent.KEYCODE_ENTER: 6226 return true; 6227 } 6228 return false; 6229 } 6230 6231 /** 6232 * Returns true if the key is used for typing. 6233 * @param keyEvent The key event. 6234 * @return True if the key is used for typing. 6235 */ 6236 private static boolean isTypingKey(KeyEvent keyEvent) { 6237 return keyEvent.getUnicodeChar() > 0; 6238 } 6239 6240 /** 6241 * See if the key event means we should leave touch mode (and leave touch mode if so). 6242 * @param event The key event. 6243 * @return Whether this key event should be consumed (meaning the act of 6244 * leaving touch mode alone is considered the event). 6245 */ 6246 private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) { 6247 // Only relevant in touch mode. 6248 if (!mAttachInfo.mInTouchMode) { 6249 return false; 6250 } 6251 6252 // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP. 6253 final int action = event.getAction(); 6254 if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) { 6255 return false; 6256 } 6257 6258 // Don't leave touch mode if the IME told us not to. 6259 if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { 6260 return false; 6261 } 6262 6263 // If the key can be used for keyboard navigation then leave touch mode 6264 // and select a focused view if needed (in ensureTouchMode). 6265 // When a new focused view is selected, we consume the navigation key because 6266 // navigation doesn't make much sense unless a view already has focus so 6267 // the key's purpose is to set focus. 6268 if (isNavigationKey(event)) { 6269 return ensureTouchMode(false); 6270 } 6271 6272 // If the key can be used for typing then leave touch mode 6273 // and select a focused view if needed (in ensureTouchMode). 6274 // Always allow the view to process the typing key. 6275 if (isTypingKey(event)) { 6276 ensureTouchMode(false); 6277 return false; 6278 } 6279 6280 return false; 6281 } 6282 6283 /* drag/drop */ 6284 void setLocalDragState(Object obj) { 6285 mLocalDragState = obj; 6286 } 6287 6288 private void handleDragEvent(DragEvent event) { 6289 // From the root, only drag start/end/location are dispatched. entered/exited 6290 // are determined and dispatched by the viewgroup hierarchy, who then report 6291 // that back here for ultimate reporting back to the framework. 6292 if (mView != null && mAdded) { 6293 final int what = event.mAction; 6294 6295 // Cache the drag description when the operation starts, then fill it in 6296 // on subsequent calls as a convenience 6297 if (what == DragEvent.ACTION_DRAG_STARTED) { 6298 mCurrentDragView = null; // Start the current-recipient tracking 6299 mDragDescription = event.mClipDescription; 6300 } else { 6301 if (what == DragEvent.ACTION_DRAG_ENDED) { 6302 mDragDescription = null; 6303 } 6304 event.mClipDescription = mDragDescription; 6305 } 6306 6307 if (what == DragEvent.ACTION_DRAG_EXITED) { 6308 // A direct EXITED event means that the window manager knows we've just crossed 6309 // a window boundary, so the current drag target within this one must have 6310 // just been exited. Send the EXITED notification to the current drag view, if any. 6311 if (View.sCascadedDragDrop) { 6312 mView.dispatchDragEnterExitInPreN(event); 6313 } 6314 setDragFocus(null, event); 6315 } else { 6316 // For events with a [screen] location, translate into window coordinates 6317 if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { 6318 mDragPoint.set(event.mX, event.mY); 6319 if (mTranslator != null) { 6320 mTranslator.translatePointInScreenToAppWindow(mDragPoint); 6321 } 6322 6323 if (mCurScrollY != 0) { 6324 mDragPoint.offset(0, mCurScrollY); 6325 } 6326 6327 event.mX = mDragPoint.x; 6328 event.mY = mDragPoint.y; 6329 } 6330 6331 // Remember who the current drag target is pre-dispatch 6332 final View prevDragView = mCurrentDragView; 6333 6334 if (what == DragEvent.ACTION_DROP && event.mClipData != null) { 6335 event.mClipData.prepareToEnterProcess(); 6336 } 6337 6338 // Now dispatch the drag/drop event 6339 boolean result = mView.dispatchDragEvent(event); 6340 6341 if (what == DragEvent.ACTION_DRAG_LOCATION && !event.mEventHandlerWasCalled) { 6342 // If the LOCATION event wasn't delivered to any handler, no view now has a drag 6343 // focus. 6344 setDragFocus(null, event); 6345 } 6346 6347 // If we changed apparent drag target, tell the OS about it 6348 if (prevDragView != mCurrentDragView) { 6349 try { 6350 if (prevDragView != null) { 6351 mWindowSession.dragRecipientExited(mWindow); 6352 } 6353 if (mCurrentDragView != null) { 6354 mWindowSession.dragRecipientEntered(mWindow); 6355 } 6356 } catch (RemoteException e) { 6357 Slog.e(mTag, "Unable to note drag target change"); 6358 } 6359 } 6360 6361 // Report the drop result when we're done 6362 if (what == DragEvent.ACTION_DROP) { 6363 try { 6364 Log.i(mTag, "Reporting drop result: " + result); 6365 mWindowSession.reportDropResult(mWindow, result); 6366 } catch (RemoteException e) { 6367 Log.e(mTag, "Unable to report drop result"); 6368 } 6369 } 6370 6371 // When the drag operation ends, reset drag-related state 6372 if (what == DragEvent.ACTION_DRAG_ENDED) { 6373 mCurrentDragView = null; 6374 setLocalDragState(null); 6375 mAttachInfo.mDragToken = null; 6376 if (mAttachInfo.mDragSurface != null) { 6377 mAttachInfo.mDragSurface.release(); 6378 mAttachInfo.mDragSurface = null; 6379 } 6380 } 6381 } 6382 } 6383 event.recycle(); 6384 } 6385 6386 public void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) { 6387 if (mSeq != args.seq) { 6388 // The sequence has changed, so we need to update our value and make 6389 // sure to do a traversal afterward so the window manager is given our 6390 // most recent data. 6391 mSeq = args.seq; 6392 mAttachInfo.mForceReportNewAttributes = true; 6393 scheduleTraversals(); 6394 } 6395 if (mView == null) return; 6396 if (args.localChanges != 0) { 6397 mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges); 6398 } 6399 6400 int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS; 6401 if (visibility != mAttachInfo.mGlobalSystemUiVisibility) { 6402 mAttachInfo.mGlobalSystemUiVisibility = visibility; 6403 mView.dispatchSystemUiVisibilityChanged(visibility); 6404 } 6405 } 6406 6407 /** 6408 * Notify that the window title changed 6409 */ 6410 public void onWindowTitleChanged() { 6411 mAttachInfo.mForceReportNewAttributes = true; 6412 } 6413 6414 public void handleDispatchWindowShown() { 6415 mAttachInfo.mTreeObserver.dispatchOnWindowShown(); 6416 } 6417 6418 public void handleRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) { 6419 Bundle data = new Bundle(); 6420 ArrayList<KeyboardShortcutGroup> list = new ArrayList<>(); 6421 if (mView != null) { 6422 mView.requestKeyboardShortcuts(list, deviceId); 6423 } 6424 data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list); 6425 try { 6426 receiver.send(0, data); 6427 } catch (RemoteException e) { 6428 } 6429 } 6430 6431 public void getLastTouchPoint(Point outLocation) { 6432 outLocation.x = (int) mLastTouchPoint.x; 6433 outLocation.y = (int) mLastTouchPoint.y; 6434 } 6435 6436 public int getLastTouchSource() { 6437 return mLastTouchSource; 6438 } 6439 6440 public void setDragFocus(View newDragTarget, DragEvent event) { 6441 if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) { 6442 // Send EXITED and ENTERED notifications to the old and new drag focus views. 6443 6444 final float tx = event.mX; 6445 final float ty = event.mY; 6446 final int action = event.mAction; 6447 final ClipData td = event.mClipData; 6448 // Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED. 6449 event.mX = 0; 6450 event.mY = 0; 6451 event.mClipData = null; 6452 6453 if (mCurrentDragView != null) { 6454 event.mAction = DragEvent.ACTION_DRAG_EXITED; 6455 mCurrentDragView.callDragEventHandler(event); 6456 } 6457 6458 if (newDragTarget != null) { 6459 event.mAction = DragEvent.ACTION_DRAG_ENTERED; 6460 newDragTarget.callDragEventHandler(event); 6461 } 6462 6463 event.mAction = action; 6464 event.mX = tx; 6465 event.mY = ty; 6466 event.mClipData = td; 6467 } 6468 6469 mCurrentDragView = newDragTarget; 6470 } 6471 6472 private AudioManager getAudioManager() { 6473 if (mView == null) { 6474 throw new IllegalStateException("getAudioManager called when there is no mView"); 6475 } 6476 if (mAudioManager == null) { 6477 mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE); 6478 } 6479 return mAudioManager; 6480 } 6481 6482 private @Nullable AutofillManager getAutofillManager() { 6483 if (mView instanceof ViewGroup) { 6484 ViewGroup decorView = (ViewGroup) mView; 6485 if (decorView.getChildCount() > 0) { 6486 // We cannot use decorView's Context for querying AutofillManager: DecorView's 6487 // context is based on Application Context, it would allocate a different 6488 // AutofillManager instance. 6489 return decorView.getChildAt(0).getContext() 6490 .getSystemService(AutofillManager.class); 6491 } 6492 } 6493 return null; 6494 } 6495 6496 private boolean isAutofillUiShowing() { 6497 AutofillManager afm = getAutofillManager(); 6498 if (afm == null) { 6499 return false; 6500 } 6501 return afm.isAutofillUiShowing(); 6502 } 6503 6504 public AccessibilityInteractionController getAccessibilityInteractionController() { 6505 if (mView == null) { 6506 throw new IllegalStateException("getAccessibilityInteractionController" 6507 + " called when there is no mView"); 6508 } 6509 if (mAccessibilityInteractionController == null) { 6510 mAccessibilityInteractionController = new AccessibilityInteractionController(this); 6511 } 6512 return mAccessibilityInteractionController; 6513 } 6514 6515 private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, 6516 boolean insetsPending) throws RemoteException { 6517 6518 float appScale = mAttachInfo.mApplicationScale; 6519 boolean restore = false; 6520 if (params != null && mTranslator != null) { 6521 restore = true; 6522 params.backup(); 6523 mTranslator.translateWindowLayout(params); 6524 } 6525 6526 if (params != null) { 6527 if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params); 6528 6529 if (mOrigWindowType != params.type) { 6530 // For compatibility with old apps, don't crash here. 6531 if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 6532 Slog.w(mTag, "Window type can not be changed after " 6533 + "the window is added; ignoring change of " + mView); 6534 params.type = mOrigWindowType; 6535 } 6536 } 6537 } 6538 6539 long frameNumber = -1; 6540 if (mSurface.isValid()) { 6541 frameNumber = mSurface.getNextFrameNumber(); 6542 } 6543 6544 int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params, 6545 (int) (mView.getMeasuredWidth() * appScale + 0.5f), 6546 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, 6547 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, 6548 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, 6549 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout, 6550 mPendingMergedConfiguration, mSurface); 6551 6552 mPendingAlwaysConsumeNavBar = 6553 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0; 6554 6555 if (restore) { 6556 params.restore(); 6557 } 6558 6559 if (mTranslator != null) { 6560 mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); 6561 mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets); 6562 mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); 6563 mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); 6564 mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets); 6565 } 6566 return relayoutResult; 6567 } 6568 6569 /** 6570 * {@inheritDoc} 6571 */ 6572 @Override 6573 public void playSoundEffect(int effectId) { 6574 checkThread(); 6575 6576 try { 6577 final AudioManager audioManager = getAudioManager(); 6578 6579 switch (effectId) { 6580 case SoundEffectConstants.CLICK: 6581 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 6582 return; 6583 case SoundEffectConstants.NAVIGATION_DOWN: 6584 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); 6585 return; 6586 case SoundEffectConstants.NAVIGATION_LEFT: 6587 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); 6588 return; 6589 case SoundEffectConstants.NAVIGATION_RIGHT: 6590 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); 6591 return; 6592 case SoundEffectConstants.NAVIGATION_UP: 6593 audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); 6594 return; 6595 default: 6596 throw new IllegalArgumentException("unknown effect id " + effectId + 6597 " not defined in " + SoundEffectConstants.class.getCanonicalName()); 6598 } 6599 } catch (IllegalStateException e) { 6600 // Exception thrown by getAudioManager() when mView is null 6601 Log.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e); 6602 e.printStackTrace(); 6603 } 6604 } 6605 6606 /** 6607 * {@inheritDoc} 6608 */ 6609 @Override 6610 public boolean performHapticFeedback(int effectId, boolean always) { 6611 try { 6612 return mWindowSession.performHapticFeedback(mWindow, effectId, always); 6613 } catch (RemoteException e) { 6614 return false; 6615 } 6616 } 6617 6618 /** 6619 * {@inheritDoc} 6620 */ 6621 @Override 6622 public View focusSearch(View focused, int direction) { 6623 checkThread(); 6624 if (!(mView instanceof ViewGroup)) { 6625 return null; 6626 } 6627 return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction); 6628 } 6629 6630 /** 6631 * {@inheritDoc} 6632 */ 6633 @Override 6634 public View keyboardNavigationClusterSearch(View currentCluster, 6635 @FocusDirection int direction) { 6636 checkThread(); 6637 return FocusFinder.getInstance().findNextKeyboardNavigationCluster( 6638 mView, currentCluster, direction); 6639 } 6640 6641 public void debug() { 6642 mView.debug(); 6643 } 6644 6645 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 6646 String innerPrefix = prefix + " "; 6647 writer.print(prefix); writer.println("ViewRoot:"); 6648 writer.print(innerPrefix); writer.print("mAdded="); writer.print(mAdded); 6649 writer.print(" mRemoved="); writer.println(mRemoved); 6650 writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled="); 6651 writer.println(mConsumeBatchedInputScheduled); 6652 writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled="); 6653 writer.println(mConsumeBatchedInputImmediatelyScheduled); 6654 writer.print(innerPrefix); writer.print("mPendingInputEventCount="); 6655 writer.println(mPendingInputEventCount); 6656 writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled="); 6657 writer.println(mProcessInputEventsScheduled); 6658 writer.print(innerPrefix); writer.print("mTraversalScheduled="); 6659 writer.print(mTraversalScheduled); 6660 writer.print(innerPrefix); writer.print("mIsAmbientMode="); 6661 writer.print(mIsAmbientMode); 6662 if (mTraversalScheduled) { 6663 writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")"); 6664 } else { 6665 writer.println(); 6666 } 6667 mFirstInputStage.dump(innerPrefix, writer); 6668 6669 mChoreographer.dump(prefix, writer); 6670 6671 writer.print(prefix); writer.println("View Hierarchy:"); 6672 dumpViewHierarchy(innerPrefix, writer, mView); 6673 } 6674 6675 private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) { 6676 writer.print(prefix); 6677 if (view == null) { 6678 writer.println("null"); 6679 return; 6680 } 6681 writer.println(view.toString()); 6682 if (!(view instanceof ViewGroup)) { 6683 return; 6684 } 6685 ViewGroup grp = (ViewGroup)view; 6686 final int N = grp.getChildCount(); 6687 if (N <= 0) { 6688 return; 6689 } 6690 prefix = prefix + " "; 6691 for (int i=0; i<N; i++) { 6692 dumpViewHierarchy(prefix, writer, grp.getChildAt(i)); 6693 } 6694 } 6695 6696 public void dumpGfxInfo(int[] info) { 6697 info[0] = info[1] = 0; 6698 if (mView != null) { 6699 getGfxInfo(mView, info); 6700 } 6701 } 6702 6703 private static void getGfxInfo(View view, int[] info) { 6704 RenderNode renderNode = view.mRenderNode; 6705 info[0]++; 6706 if (renderNode != null) { 6707 info[1] += renderNode.getDebugSize(); 6708 } 6709 6710 if (view instanceof ViewGroup) { 6711 ViewGroup group = (ViewGroup) view; 6712 6713 int count = group.getChildCount(); 6714 for (int i = 0; i < count; i++) { 6715 getGfxInfo(group.getChildAt(i), info); 6716 } 6717 } 6718 } 6719 6720 /** 6721 * @param immediate True, do now if not in traversal. False, put on queue and do later. 6722 * @return True, request has been queued. False, request has been completed. 6723 */ 6724 boolean die(boolean immediate) { 6725 // Make sure we do execute immediately if we are in the middle of a traversal or the damage 6726 // done by dispatchDetachedFromWindow will cause havoc on return. 6727 if (immediate && !mIsInTraversal) { 6728 doDie(); 6729 return false; 6730 } 6731 6732 if (!mIsDrawing) { 6733 destroyHardwareRenderer(); 6734 } else { 6735 Log.e(mTag, "Attempting to destroy the window while drawing!\n" + 6736 " window=" + this + ", title=" + mWindowAttributes.getTitle()); 6737 } 6738 mHandler.sendEmptyMessage(MSG_DIE); 6739 return true; 6740 } 6741 6742 void doDie() { 6743 checkThread(); 6744 if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface); 6745 synchronized (this) { 6746 if (mRemoved) { 6747 return; 6748 } 6749 mRemoved = true; 6750 if (mAdded) { 6751 dispatchDetachedFromWindow(); 6752 } 6753 6754 if (mAdded && !mFirst) { 6755 destroyHardwareRenderer(); 6756 6757 if (mView != null) { 6758 int viewVisibility = mView.getVisibility(); 6759 boolean viewVisibilityChanged = mViewVisibility != viewVisibility; 6760 if (mWindowAttributesChanged || viewVisibilityChanged) { 6761 // If layout params have been changed, first give them 6762 // to the window manager to make sure it has the correct 6763 // animation info. 6764 try { 6765 if ((relayoutWindow(mWindowAttributes, viewVisibility, false) 6766 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 6767 mWindowSession.finishDrawing(mWindow); 6768 } 6769 } catch (RemoteException e) { 6770 } 6771 } 6772 6773 mSurface.release(); 6774 } 6775 } 6776 6777 mAdded = false; 6778 } 6779 WindowManagerGlobal.getInstance().doRemoveView(this); 6780 } 6781 6782 public void requestUpdateConfiguration(Configuration config) { 6783 Message msg = mHandler.obtainMessage(MSG_UPDATE_CONFIGURATION, config); 6784 mHandler.sendMessage(msg); 6785 } 6786 6787 public void loadSystemProperties() { 6788 mHandler.post(new Runnable() { 6789 @Override 6790 public void run() { 6791 // Profiling 6792 mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false); 6793 profileRendering(mAttachInfo.mHasWindowFocus); 6794 6795 // Hardware rendering 6796 if (mAttachInfo.mThreadedRenderer != null) { 6797 if (mAttachInfo.mThreadedRenderer.loadSystemProperties()) { 6798 invalidate(); 6799 } 6800 } 6801 6802 // Layout debugging 6803 boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false); 6804 if (layout != mAttachInfo.mDebugLayout) { 6805 mAttachInfo.mDebugLayout = layout; 6806 if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { 6807 mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200); 6808 } 6809 } 6810 } 6811 }); 6812 } 6813 6814 private void destroyHardwareRenderer() { 6815 ThreadedRenderer hardwareRenderer = mAttachInfo.mThreadedRenderer; 6816 6817 if (hardwareRenderer != null) { 6818 if (mView != null) { 6819 hardwareRenderer.destroyHardwareResources(mView); 6820 } 6821 hardwareRenderer.destroy(); 6822 hardwareRenderer.setRequested(false); 6823 6824 mAttachInfo.mThreadedRenderer = null; 6825 mAttachInfo.mHardwareAccelerated = false; 6826 } 6827 } 6828 6829 private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets, 6830 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, 6831 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, 6832 boolean alwaysConsumeNavBar, int displayId, 6833 DisplayCutout.ParcelableWrapper displayCutout) { 6834 if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString() 6835 + " contentInsets=" + contentInsets.toShortString() 6836 + " visibleInsets=" + visibleInsets.toShortString() 6837 + " reportDraw=" + reportDraw 6838 + " backDropFrame=" + backDropFrame); 6839 6840 // Tell all listeners that we are resizing the window so that the chrome can get 6841 // updated as fast as possible on a separate thread, 6842 if (mDragResizing && mUseMTRenderer) { 6843 boolean fullscreen = frame.equals(backDropFrame); 6844 synchronized (mWindowCallbacks) { 6845 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 6846 mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen, 6847 visibleInsets, stableInsets); 6848 } 6849 } 6850 } 6851 6852 Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED); 6853 if (mTranslator != null) { 6854 mTranslator.translateRectInScreenToAppWindow(frame); 6855 mTranslator.translateRectInScreenToAppWindow(overscanInsets); 6856 mTranslator.translateRectInScreenToAppWindow(contentInsets); 6857 mTranslator.translateRectInScreenToAppWindow(visibleInsets); 6858 } 6859 SomeArgs args = SomeArgs.obtain(); 6860 final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid()); 6861 args.arg1 = sameProcessCall ? new Rect(frame) : frame; 6862 args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets; 6863 args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets; 6864 args.arg4 = sameProcessCall && mergedConfiguration != null 6865 ? new MergedConfiguration(mergedConfiguration) : mergedConfiguration; 6866 args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets; 6867 args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets; 6868 args.arg7 = sameProcessCall ? new Rect(outsets) : outsets; 6869 args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame; 6870 args.arg9 = displayCutout.get(); // DisplayCutout is immutable. 6871 args.argi1 = forceLayout ? 1 : 0; 6872 args.argi2 = alwaysConsumeNavBar ? 1 : 0; 6873 args.argi3 = displayId; 6874 msg.obj = args; 6875 mHandler.sendMessage(msg); 6876 } 6877 6878 public void dispatchMoved(int newX, int newY) { 6879 if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY); 6880 if (mTranslator != null) { 6881 PointF point = new PointF(newX, newY); 6882 mTranslator.translatePointInScreenToAppWindow(point); 6883 newX = (int) (point.x + 0.5); 6884 newY = (int) (point.y + 0.5); 6885 } 6886 Message msg = mHandler.obtainMessage(MSG_WINDOW_MOVED, newX, newY); 6887 mHandler.sendMessage(msg); 6888 } 6889 6890 /** 6891 * Represents a pending input event that is waiting in a queue. 6892 * 6893 * Input events are processed in serial order by the timestamp specified by 6894 * {@link InputEvent#getEventTimeNano()}. In general, the input dispatcher delivers 6895 * one input event to the application at a time and waits for the application 6896 * to finish handling it before delivering the next one. 6897 * 6898 * However, because the application or IME can synthesize and inject multiple 6899 * key events at a time without going through the input dispatcher, we end up 6900 * needing a queue on the application's side. 6901 */ 6902 private static final class QueuedInputEvent { 6903 public static final int FLAG_DELIVER_POST_IME = 1 << 0; 6904 public static final int FLAG_DEFERRED = 1 << 1; 6905 public static final int FLAG_FINISHED = 1 << 2; 6906 public static final int FLAG_FINISHED_HANDLED = 1 << 3; 6907 public static final int FLAG_RESYNTHESIZED = 1 << 4; 6908 public static final int FLAG_UNHANDLED = 1 << 5; 6909 6910 public QueuedInputEvent mNext; 6911 6912 public InputEvent mEvent; 6913 public InputEventReceiver mReceiver; 6914 public int mFlags; 6915 6916 public boolean shouldSkipIme() { 6917 if ((mFlags & FLAG_DELIVER_POST_IME) != 0) { 6918 return true; 6919 } 6920 return mEvent instanceof MotionEvent 6921 && (mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) 6922 || mEvent.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER)); 6923 } 6924 6925 public boolean shouldSendToSynthesizer() { 6926 if ((mFlags & FLAG_UNHANDLED) != 0) { 6927 return true; 6928 } 6929 6930 return false; 6931 } 6932 6933 @Override 6934 public String toString() { 6935 StringBuilder sb = new StringBuilder("QueuedInputEvent{flags="); 6936 boolean hasPrevious = false; 6937 hasPrevious = flagToString("DELIVER_POST_IME", FLAG_DELIVER_POST_IME, hasPrevious, sb); 6938 hasPrevious = flagToString("DEFERRED", FLAG_DEFERRED, hasPrevious, sb); 6939 hasPrevious = flagToString("FINISHED", FLAG_FINISHED, hasPrevious, sb); 6940 hasPrevious = flagToString("FINISHED_HANDLED", FLAG_FINISHED_HANDLED, hasPrevious, sb); 6941 hasPrevious = flagToString("RESYNTHESIZED", FLAG_RESYNTHESIZED, hasPrevious, sb); 6942 hasPrevious = flagToString("UNHANDLED", FLAG_UNHANDLED, hasPrevious, sb); 6943 if (!hasPrevious) { 6944 sb.append("0"); 6945 } 6946 sb.append(", hasNextQueuedEvent=" + (mEvent != null ? "true" : "false")); 6947 sb.append(", hasInputEventReceiver=" + (mReceiver != null ? "true" : "false")); 6948 sb.append(", mEvent=" + mEvent + "}"); 6949 return sb.toString(); 6950 } 6951 6952 private boolean flagToString(String name, int flag, 6953 boolean hasPrevious, StringBuilder sb) { 6954 if ((mFlags & flag) != 0) { 6955 if (hasPrevious) { 6956 sb.append("|"); 6957 } 6958 sb.append(name); 6959 return true; 6960 } 6961 return hasPrevious; 6962 } 6963 } 6964 6965 private QueuedInputEvent obtainQueuedInputEvent(InputEvent event, 6966 InputEventReceiver receiver, int flags) { 6967 QueuedInputEvent q = mQueuedInputEventPool; 6968 if (q != null) { 6969 mQueuedInputEventPoolSize -= 1; 6970 mQueuedInputEventPool = q.mNext; 6971 q.mNext = null; 6972 } else { 6973 q = new QueuedInputEvent(); 6974 } 6975 6976 q.mEvent = event; 6977 q.mReceiver = receiver; 6978 q.mFlags = flags; 6979 return q; 6980 } 6981 6982 private void recycleQueuedInputEvent(QueuedInputEvent q) { 6983 q.mEvent = null; 6984 q.mReceiver = null; 6985 6986 if (mQueuedInputEventPoolSize < MAX_QUEUED_INPUT_EVENT_POOL_SIZE) { 6987 mQueuedInputEventPoolSize += 1; 6988 q.mNext = mQueuedInputEventPool; 6989 mQueuedInputEventPool = q; 6990 } 6991 } 6992 6993 void enqueueInputEvent(InputEvent event) { 6994 enqueueInputEvent(event, null, 0, false); 6995 } 6996 6997 void enqueueInputEvent(InputEvent event, 6998 InputEventReceiver receiver, int flags, boolean processImmediately) { 6999 adjustInputEventForCompatibility(event); 7000 QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); 7001 7002 // Always enqueue the input event in order, regardless of its time stamp. 7003 // We do this because the application or the IME may inject key events 7004 // in response to touch events and we want to ensure that the injected keys 7005 // are processed in the order they were received and we cannot trust that 7006 // the time stamp of injected events are monotonic. 7007 QueuedInputEvent last = mPendingInputEventTail; 7008 if (last == null) { 7009 mPendingInputEventHead = q; 7010 mPendingInputEventTail = q; 7011 } else { 7012 last.mNext = q; 7013 mPendingInputEventTail = q; 7014 } 7015 mPendingInputEventCount += 1; 7016 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, 7017 mPendingInputEventCount); 7018 7019 if (processImmediately) { 7020 doProcessInputEvents(); 7021 } else { 7022 scheduleProcessInputEvents(); 7023 } 7024 } 7025 7026 private void scheduleProcessInputEvents() { 7027 if (!mProcessInputEventsScheduled) { 7028 mProcessInputEventsScheduled = true; 7029 Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS); 7030 msg.setAsynchronous(true); 7031 mHandler.sendMessage(msg); 7032 } 7033 } 7034 7035 void doProcessInputEvents() { 7036 // Deliver all pending input events in the queue. 7037 while (mPendingInputEventHead != null) { 7038 QueuedInputEvent q = mPendingInputEventHead; 7039 mPendingInputEventHead = q.mNext; 7040 if (mPendingInputEventHead == null) { 7041 mPendingInputEventTail = null; 7042 } 7043 q.mNext = null; 7044 7045 mPendingInputEventCount -= 1; 7046 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, 7047 mPendingInputEventCount); 7048 7049 long eventTime = q.mEvent.getEventTimeNano(); 7050 long oldestEventTime = eventTime; 7051 if (q.mEvent instanceof MotionEvent) { 7052 MotionEvent me = (MotionEvent)q.mEvent; 7053 if (me.getHistorySize() > 0) { 7054 oldestEventTime = me.getHistoricalEventTimeNano(0); 7055 } 7056 } 7057 mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime); 7058 7059 deliverInputEvent(q); 7060 } 7061 7062 // We are done processing all input events that we can process right now 7063 // so we can clear the pending flag immediately. 7064 if (mProcessInputEventsScheduled) { 7065 mProcessInputEventsScheduled = false; 7066 mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); 7067 } 7068 } 7069 7070 private void deliverInputEvent(QueuedInputEvent q) { 7071 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent", 7072 q.mEvent.getSequenceNumber()); 7073 if (mInputEventConsistencyVerifier != null) { 7074 mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0); 7075 } 7076 7077 InputStage stage; 7078 if (q.shouldSendToSynthesizer()) { 7079 stage = mSyntheticInputStage; 7080 } else { 7081 stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; 7082 } 7083 7084 if (q.mEvent instanceof KeyEvent) { 7085 mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); 7086 } 7087 7088 if (stage != null) { 7089 handleWindowFocusChanged(); 7090 stage.deliver(q); 7091 } else { 7092 finishInputEvent(q); 7093 } 7094 } 7095 7096 private void finishInputEvent(QueuedInputEvent q) { 7097 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent", 7098 q.mEvent.getSequenceNumber()); 7099 7100 if (q.mReceiver != null) { 7101 boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; 7102 q.mReceiver.finishInputEvent(q.mEvent, handled); 7103 } else { 7104 q.mEvent.recycleIfNeededAfterDispatch(); 7105 } 7106 7107 recycleQueuedInputEvent(q); 7108 } 7109 7110 private void adjustInputEventForCompatibility(InputEvent e) { 7111 if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) { 7112 MotionEvent motion = (MotionEvent) e; 7113 final int mask = 7114 MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY; 7115 final int buttonState = motion.getButtonState(); 7116 final int compatButtonState = (buttonState & mask) >> 4; 7117 if (compatButtonState != 0) { 7118 motion.setButtonState(buttonState | compatButtonState); 7119 } 7120 } 7121 } 7122 7123 static boolean isTerminalInputEvent(InputEvent event) { 7124 if (event instanceof KeyEvent) { 7125 final KeyEvent keyEvent = (KeyEvent)event; 7126 return keyEvent.getAction() == KeyEvent.ACTION_UP; 7127 } else { 7128 final MotionEvent motionEvent = (MotionEvent)event; 7129 final int action = motionEvent.getAction(); 7130 return action == MotionEvent.ACTION_UP 7131 || action == MotionEvent.ACTION_CANCEL 7132 || action == MotionEvent.ACTION_HOVER_EXIT; 7133 } 7134 } 7135 7136 void scheduleConsumeBatchedInput() { 7137 if (!mConsumeBatchedInputScheduled) { 7138 mConsumeBatchedInputScheduled = true; 7139 mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, 7140 mConsumedBatchedInputRunnable, null); 7141 } 7142 } 7143 7144 void unscheduleConsumeBatchedInput() { 7145 if (mConsumeBatchedInputScheduled) { 7146 mConsumeBatchedInputScheduled = false; 7147 mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, 7148 mConsumedBatchedInputRunnable, null); 7149 } 7150 } 7151 7152 void scheduleConsumeBatchedInputImmediately() { 7153 if (!mConsumeBatchedInputImmediatelyScheduled) { 7154 unscheduleConsumeBatchedInput(); 7155 mConsumeBatchedInputImmediatelyScheduled = true; 7156 mHandler.post(mConsumeBatchedInputImmediatelyRunnable); 7157 } 7158 } 7159 7160 void doConsumeBatchedInput(long frameTimeNanos) { 7161 if (mConsumeBatchedInputScheduled) { 7162 mConsumeBatchedInputScheduled = false; 7163 if (mInputEventReceiver != null) { 7164 if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos) 7165 && frameTimeNanos != -1) { 7166 // If we consumed a batch here, we want to go ahead and schedule the 7167 // consumption of batched input events on the next frame. Otherwise, we would 7168 // wait until we have more input events pending and might get starved by other 7169 // things occurring in the process. If the frame time is -1, however, then 7170 // we're in a non-batching mode, so there's no need to schedule this. 7171 scheduleConsumeBatchedInput(); 7172 } 7173 } 7174 doProcessInputEvents(); 7175 } 7176 } 7177 7178 final class TraversalRunnable implements Runnable { 7179 @Override 7180 public void run() { 7181 doTraversal(); 7182 } 7183 } 7184 final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 7185 7186 final class WindowInputEventReceiver extends InputEventReceiver { 7187 public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { 7188 super(inputChannel, looper); 7189 } 7190 7191 @Override 7192 public void onInputEvent(InputEvent event, int displayId) { 7193 enqueueInputEvent(event, this, 0, true); 7194 } 7195 7196 @Override 7197 public void onBatchedInputEventPending() { 7198 if (mUnbufferedInputDispatch) { 7199 super.onBatchedInputEventPending(); 7200 } else { 7201 scheduleConsumeBatchedInput(); 7202 } 7203 } 7204 7205 @Override 7206 public void dispose() { 7207 unscheduleConsumeBatchedInput(); 7208 super.dispose(); 7209 } 7210 } 7211 WindowInputEventReceiver mInputEventReceiver; 7212 7213 final class ConsumeBatchedInputRunnable implements Runnable { 7214 @Override 7215 public void run() { 7216 doConsumeBatchedInput(mChoreographer.getFrameTimeNanos()); 7217 } 7218 } 7219 final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable = 7220 new ConsumeBatchedInputRunnable(); 7221 boolean mConsumeBatchedInputScheduled; 7222 7223 final class ConsumeBatchedInputImmediatelyRunnable implements Runnable { 7224 @Override 7225 public void run() { 7226 doConsumeBatchedInput(-1); 7227 } 7228 } 7229 final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable = 7230 new ConsumeBatchedInputImmediatelyRunnable(); 7231 boolean mConsumeBatchedInputImmediatelyScheduled; 7232 7233 final class InvalidateOnAnimationRunnable implements Runnable { 7234 private boolean mPosted; 7235 private final ArrayList<View> mViews = new ArrayList<View>(); 7236 private final ArrayList<AttachInfo.InvalidateInfo> mViewRects = 7237 new ArrayList<AttachInfo.InvalidateInfo>(); 7238 private View[] mTempViews; 7239 private AttachInfo.InvalidateInfo[] mTempViewRects; 7240 7241 public void addView(View view) { 7242 synchronized (this) { 7243 mViews.add(view); 7244 postIfNeededLocked(); 7245 } 7246 } 7247 7248 public void addViewRect(AttachInfo.InvalidateInfo info) { 7249 synchronized (this) { 7250 mViewRects.add(info); 7251 postIfNeededLocked(); 7252 } 7253 } 7254 7255 public void removeView(View view) { 7256 synchronized (this) { 7257 mViews.remove(view); 7258 7259 for (int i = mViewRects.size(); i-- > 0; ) { 7260 AttachInfo.InvalidateInfo info = mViewRects.get(i); 7261 if (info.target == view) { 7262 mViewRects.remove(i); 7263 info.recycle(); 7264 } 7265 } 7266 7267 if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) { 7268 mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null); 7269 mPosted = false; 7270 } 7271 } 7272 } 7273 7274 @Override 7275 public void run() { 7276 final int viewCount; 7277 final int viewRectCount; 7278 synchronized (this) { 7279 mPosted = false; 7280 7281 viewCount = mViews.size(); 7282 if (viewCount != 0) { 7283 mTempViews = mViews.toArray(mTempViews != null 7284 ? mTempViews : new View[viewCount]); 7285 mViews.clear(); 7286 } 7287 7288 viewRectCount = mViewRects.size(); 7289 if (viewRectCount != 0) { 7290 mTempViewRects = mViewRects.toArray(mTempViewRects != null 7291 ? mTempViewRects : new AttachInfo.InvalidateInfo[viewRectCount]); 7292 mViewRects.clear(); 7293 } 7294 } 7295 7296 for (int i = 0; i < viewCount; i++) { 7297 mTempViews[i].invalidate(); 7298 mTempViews[i] = null; 7299 } 7300 7301 for (int i = 0; i < viewRectCount; i++) { 7302 final View.AttachInfo.InvalidateInfo info = mTempViewRects[i]; 7303 info.target.invalidate(info.left, info.top, info.right, info.bottom); 7304 info.recycle(); 7305 } 7306 } 7307 7308 private void postIfNeededLocked() { 7309 if (!mPosted) { 7310 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); 7311 mPosted = true; 7312 } 7313 } 7314 } 7315 final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable = 7316 new InvalidateOnAnimationRunnable(); 7317 7318 public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { 7319 Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); 7320 mHandler.sendMessageDelayed(msg, delayMilliseconds); 7321 } 7322 7323 public void dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info, 7324 long delayMilliseconds) { 7325 final Message msg = mHandler.obtainMessage(MSG_INVALIDATE_RECT, info); 7326 mHandler.sendMessageDelayed(msg, delayMilliseconds); 7327 } 7328 7329 public void dispatchInvalidateOnAnimation(View view) { 7330 mInvalidateOnAnimationRunnable.addView(view); 7331 } 7332 7333 public void dispatchInvalidateRectOnAnimation(AttachInfo.InvalidateInfo info) { 7334 mInvalidateOnAnimationRunnable.addViewRect(info); 7335 } 7336 7337 public void cancelInvalidate(View view) { 7338 mHandler.removeMessages(MSG_INVALIDATE, view); 7339 // fixme: might leak the AttachInfo.InvalidateInfo objects instead of returning 7340 // them to the pool 7341 mHandler.removeMessages(MSG_INVALIDATE_RECT, view); 7342 mInvalidateOnAnimationRunnable.removeView(view); 7343 } 7344 7345 public void dispatchInputEvent(InputEvent event) { 7346 dispatchInputEvent(event, null); 7347 } 7348 7349 public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { 7350 SomeArgs args = SomeArgs.obtain(); 7351 args.arg1 = event; 7352 args.arg2 = receiver; 7353 Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args); 7354 msg.setAsynchronous(true); 7355 mHandler.sendMessage(msg); 7356 } 7357 7358 public void synthesizeInputEvent(InputEvent event) { 7359 Message msg = mHandler.obtainMessage(MSG_SYNTHESIZE_INPUT_EVENT, event); 7360 msg.setAsynchronous(true); 7361 mHandler.sendMessage(msg); 7362 } 7363 7364 public void dispatchKeyFromIme(KeyEvent event) { 7365 Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event); 7366 msg.setAsynchronous(true); 7367 mHandler.sendMessage(msg); 7368 } 7369 7370 public void dispatchKeyFromAutofill(KeyEvent event) { 7371 Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_AUTOFILL, event); 7372 msg.setAsynchronous(true); 7373 mHandler.sendMessage(msg); 7374 } 7375 7376 /** 7377 * Reinject unhandled {@link InputEvent}s in order to synthesize fallbacks events. 7378 * 7379 * Note that it is the responsibility of the caller of this API to recycle the InputEvent it 7380 * passes in. 7381 */ 7382 public void dispatchUnhandledInputEvent(InputEvent event) { 7383 if (event instanceof MotionEvent) { 7384 event = MotionEvent.obtain((MotionEvent) event); 7385 } 7386 synthesizeInputEvent(event); 7387 } 7388 7389 public void dispatchAppVisibility(boolean visible) { 7390 Message msg = mHandler.obtainMessage(MSG_DISPATCH_APP_VISIBILITY); 7391 msg.arg1 = visible ? 1 : 0; 7392 mHandler.sendMessage(msg); 7393 } 7394 7395 public void dispatchGetNewSurface() { 7396 Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE); 7397 mHandler.sendMessage(msg); 7398 } 7399 7400 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 7401 synchronized (this) { 7402 mWindowFocusChanged = true; 7403 mUpcomingWindowFocus = hasFocus; 7404 mUpcomingInTouchMode = inTouchMode; 7405 } 7406 Message msg = Message.obtain(); 7407 msg.what = MSG_WINDOW_FOCUS_CHANGED; 7408 mHandler.sendMessage(msg); 7409 } 7410 7411 public void dispatchWindowShown() { 7412 mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN); 7413 } 7414 7415 public void dispatchCloseSystemDialogs(String reason) { 7416 Message msg = Message.obtain(); 7417 msg.what = MSG_CLOSE_SYSTEM_DIALOGS; 7418 msg.obj = reason; 7419 mHandler.sendMessage(msg); 7420 } 7421 7422 public void dispatchDragEvent(DragEvent event) { 7423 final int what; 7424 if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) { 7425 what = MSG_DISPATCH_DRAG_LOCATION_EVENT; 7426 mHandler.removeMessages(what); 7427 } else { 7428 what = MSG_DISPATCH_DRAG_EVENT; 7429 } 7430 Message msg = mHandler.obtainMessage(what, event); 7431 mHandler.sendMessage(msg); 7432 } 7433 7434 public void updatePointerIcon(float x, float y) { 7435 final int what = MSG_UPDATE_POINTER_ICON; 7436 mHandler.removeMessages(what); 7437 final long now = SystemClock.uptimeMillis(); 7438 final MotionEvent event = MotionEvent.obtain( 7439 0, now, MotionEvent.ACTION_HOVER_MOVE, x, y, 0); 7440 Message msg = mHandler.obtainMessage(what, event); 7441 mHandler.sendMessage(msg); 7442 } 7443 7444 public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, 7445 int localValue, int localChanges) { 7446 SystemUiVisibilityInfo args = new SystemUiVisibilityInfo(); 7447 args.seq = seq; 7448 args.globalVisibility = globalVisibility; 7449 args.localValue = localValue; 7450 args.localChanges = localChanges; 7451 mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); 7452 } 7453 7454 public void dispatchCheckFocus() { 7455 if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) { 7456 // This will result in a call to checkFocus() below. 7457 mHandler.sendEmptyMessage(MSG_CHECK_FOCUS); 7458 } 7459 } 7460 7461 public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver, int deviceId) { 7462 mHandler.obtainMessage( 7463 MSG_REQUEST_KEYBOARD_SHORTCUTS, deviceId, 0, receiver).sendToTarget(); 7464 } 7465 7466 public void dispatchPointerCaptureChanged(boolean on) { 7467 final int what = MSG_POINTER_CAPTURE_CHANGED; 7468 mHandler.removeMessages(what); 7469 Message msg = mHandler.obtainMessage(what); 7470 msg.arg1 = on ? 1 : 0; 7471 mHandler.sendMessage(msg); 7472 } 7473 7474 /** 7475 * Post a callback to send a 7476 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 7477 * This event is send at most once every 7478 * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}. 7479 */ 7480 private void postSendWindowContentChangedCallback(View source, int changeType) { 7481 if (mSendWindowContentChangedAccessibilityEvent == null) { 7482 mSendWindowContentChangedAccessibilityEvent = 7483 new SendWindowContentChangedAccessibilityEvent(); 7484 } 7485 mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType); 7486 } 7487 7488 /** 7489 * Remove a posted callback to send a 7490 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. 7491 */ 7492 private void removeSendWindowContentChangedCallback() { 7493 if (mSendWindowContentChangedAccessibilityEvent != null) { 7494 mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent); 7495 } 7496 } 7497 7498 @Override 7499 public boolean showContextMenuForChild(View originalView) { 7500 return false; 7501 } 7502 7503 @Override 7504 public boolean showContextMenuForChild(View originalView, float x, float y) { 7505 return false; 7506 } 7507 7508 @Override 7509 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) { 7510 return null; 7511 } 7512 7513 @Override 7514 public ActionMode startActionModeForChild( 7515 View originalView, ActionMode.Callback callback, int type) { 7516 return null; 7517 } 7518 7519 @Override 7520 public void createContextMenu(ContextMenu menu) { 7521 } 7522 7523 @Override 7524 public void childDrawableStateChanged(View child) { 7525 } 7526 7527 @Override 7528 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { 7529 if (mView == null || mStopped || mPausedForTransition) { 7530 return false; 7531 } 7532 7533 // Immediately flush pending content changed event (if any) to preserve event order 7534 if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 7535 && mSendWindowContentChangedAccessibilityEvent != null 7536 && mSendWindowContentChangedAccessibilityEvent.mSource != null) { 7537 mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun(); 7538 } 7539 7540 // Intercept accessibility focus events fired by virtual nodes to keep 7541 // track of accessibility focus position in such nodes. 7542 final int eventType = event.getEventType(); 7543 switch (eventType) { 7544 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 7545 final long sourceNodeId = event.getSourceNodeId(); 7546 final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( 7547 sourceNodeId); 7548 View source = mView.findViewByAccessibilityId(accessibilityViewId); 7549 if (source != null) { 7550 AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); 7551 if (provider != null) { 7552 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( 7553 sourceNodeId); 7554 final AccessibilityNodeInfo node; 7555 node = provider.createAccessibilityNodeInfo(virtualNodeId); 7556 setAccessibilityFocus(source, node); 7557 } 7558 } 7559 } break; 7560 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { 7561 final long sourceNodeId = event.getSourceNodeId(); 7562 final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( 7563 sourceNodeId); 7564 View source = mView.findViewByAccessibilityId(accessibilityViewId); 7565 if (source != null) { 7566 AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); 7567 if (provider != null) { 7568 setAccessibilityFocus(null, null); 7569 } 7570 } 7571 } break; 7572 7573 7574 case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: { 7575 handleWindowContentChangedEvent(event); 7576 } break; 7577 } 7578 mAccessibilityManager.sendAccessibilityEvent(event); 7579 return true; 7580 } 7581 7582 /** 7583 * Updates the focused virtual view, when necessary, in response to a 7584 * content changed event. 7585 * <p> 7586 * This is necessary to get updated bounds after a position change. 7587 * 7588 * @param event an accessibility event of type 7589 * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} 7590 */ 7591 private void handleWindowContentChangedEvent(AccessibilityEvent event) { 7592 final View focusedHost = mAccessibilityFocusedHost; 7593 if (focusedHost == null || mAccessibilityFocusedVirtualView == null) { 7594 // No virtual view focused, nothing to do here. 7595 return; 7596 } 7597 7598 final AccessibilityNodeProvider provider = focusedHost.getAccessibilityNodeProvider(); 7599 if (provider == null) { 7600 // Error state: virtual view with no provider. Clear focus. 7601 mAccessibilityFocusedHost = null; 7602 mAccessibilityFocusedVirtualView = null; 7603 focusedHost.clearAccessibilityFocusNoCallbacks(0); 7604 return; 7605 } 7606 7607 // We only care about change types that may affect the bounds of the 7608 // focused virtual view. 7609 final int changes = event.getContentChangeTypes(); 7610 if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) == 0 7611 && changes != AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) { 7612 return; 7613 } 7614 7615 final long eventSourceNodeId = event.getSourceNodeId(); 7616 final int changedViewId = AccessibilityNodeInfo.getAccessibilityViewId(eventSourceNodeId); 7617 7618 // Search up the tree for subtree containment. 7619 boolean hostInSubtree = false; 7620 View root = mAccessibilityFocusedHost; 7621 while (root != null && !hostInSubtree) { 7622 if (changedViewId == root.getAccessibilityViewId()) { 7623 hostInSubtree = true; 7624 } else { 7625 final ViewParent parent = root.getParent(); 7626 if (parent instanceof View) { 7627 root = (View) parent; 7628 } else { 7629 root = null; 7630 } 7631 } 7632 } 7633 7634 // We care only about changes in subtrees containing the host view. 7635 if (!hostInSubtree) { 7636 return; 7637 } 7638 7639 final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId(); 7640 int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId); 7641 7642 // Refresh the node for the focused virtual view. 7643 final Rect oldBounds = mTempRect; 7644 mAccessibilityFocusedVirtualView.getBoundsInScreen(oldBounds); 7645 mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId); 7646 if (mAccessibilityFocusedVirtualView == null) { 7647 // Error state: The node no longer exists. Clear focus. 7648 mAccessibilityFocusedHost = null; 7649 focusedHost.clearAccessibilityFocusNoCallbacks(0); 7650 7651 // This will probably fail, but try to keep the provider's internal 7652 // state consistent by clearing focus. 7653 provider.performAction(focusedChildId, 7654 AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), null); 7655 invalidateRectOnScreen(oldBounds); 7656 } else { 7657 // The node was refreshed, invalidate bounds if necessary. 7658 final Rect newBounds = mAccessibilityFocusedVirtualView.getBoundsInScreen(); 7659 if (!oldBounds.equals(newBounds)) { 7660 oldBounds.union(newBounds); 7661 invalidateRectOnScreen(oldBounds); 7662 } 7663 } 7664 } 7665 7666 @Override 7667 public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) { 7668 postSendWindowContentChangedCallback(Preconditions.checkNotNull(source), changeType); 7669 } 7670 7671 @Override 7672 public boolean canResolveLayoutDirection() { 7673 return true; 7674 } 7675 7676 @Override 7677 public boolean isLayoutDirectionResolved() { 7678 return true; 7679 } 7680 7681 @Override 7682 public int getLayoutDirection() { 7683 return View.LAYOUT_DIRECTION_RESOLVED_DEFAULT; 7684 } 7685 7686 @Override 7687 public boolean canResolveTextDirection() { 7688 return true; 7689 } 7690 7691 @Override 7692 public boolean isTextDirectionResolved() { 7693 return true; 7694 } 7695 7696 @Override 7697 public int getTextDirection() { 7698 return View.TEXT_DIRECTION_RESOLVED_DEFAULT; 7699 } 7700 7701 @Override 7702 public boolean canResolveTextAlignment() { 7703 return true; 7704 } 7705 7706 @Override 7707 public boolean isTextAlignmentResolved() { 7708 return true; 7709 } 7710 7711 @Override 7712 public int getTextAlignment() { 7713 return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT; 7714 } 7715 7716 private View getCommonPredecessor(View first, View second) { 7717 if (mTempHashSet == null) { 7718 mTempHashSet = new HashSet<View>(); 7719 } 7720 HashSet<View> seen = mTempHashSet; 7721 seen.clear(); 7722 View firstCurrent = first; 7723 while (firstCurrent != null) { 7724 seen.add(firstCurrent); 7725 ViewParent firstCurrentParent = firstCurrent.mParent; 7726 if (firstCurrentParent instanceof View) { 7727 firstCurrent = (View) firstCurrentParent; 7728 } else { 7729 firstCurrent = null; 7730 } 7731 } 7732 View secondCurrent = second; 7733 while (secondCurrent != null) { 7734 if (seen.contains(secondCurrent)) { 7735 seen.clear(); 7736 return secondCurrent; 7737 } 7738 ViewParent secondCurrentParent = secondCurrent.mParent; 7739 if (secondCurrentParent instanceof View) { 7740 secondCurrent = (View) secondCurrentParent; 7741 } else { 7742 secondCurrent = null; 7743 } 7744 } 7745 seen.clear(); 7746 return null; 7747 } 7748 7749 void checkThread() { 7750 if (mThread != Thread.currentThread()) { 7751 throw new CalledFromWrongThreadException( 7752 "Only the original thread that created a view hierarchy can touch its views."); 7753 } 7754 } 7755 7756 @Override 7757 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 7758 // ViewAncestor never intercepts touch event, so this can be a no-op 7759 } 7760 7761 @Override 7762 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 7763 if (rectangle == null) { 7764 return scrollToRectOrFocus(null, immediate); 7765 } 7766 rectangle.offset(child.getLeft() - child.getScrollX(), 7767 child.getTop() - child.getScrollY()); 7768 final boolean scrolled = scrollToRectOrFocus(rectangle, immediate); 7769 mTempRect.set(rectangle); 7770 mTempRect.offset(0, -mCurScrollY); 7771 mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop); 7772 try { 7773 mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect); 7774 } catch (RemoteException re) { 7775 /* ignore */ 7776 } 7777 return scrolled; 7778 } 7779 7780 @Override 7781 public void childHasTransientStateChanged(View child, boolean hasTransientState) { 7782 // Do nothing. 7783 } 7784 7785 @Override 7786 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 7787 return false; 7788 } 7789 7790 @Override 7791 public void onStopNestedScroll(View target) { 7792 } 7793 7794 @Override 7795 public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { 7796 } 7797 7798 @Override 7799 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, 7800 int dxUnconsumed, int dyUnconsumed) { 7801 } 7802 7803 @Override 7804 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 7805 } 7806 7807 @Override 7808 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 7809 return false; 7810 } 7811 7812 @Override 7813 public boolean onNestedPreFling(View target, float velocityX, float velocityY) { 7814 return false; 7815 } 7816 7817 @Override 7818 public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { 7819 return false; 7820 } 7821 7822 7823 private void reportNextDraw() { 7824 if (mReportNextDraw == false) { 7825 drawPending(); 7826 } 7827 mReportNextDraw = true; 7828 } 7829 7830 /** 7831 * Force the window to report its next draw. 7832 * <p> 7833 * This method is only supposed to be used to speed up the interaction from SystemUI and window 7834 * manager when waiting for the first frame to be drawn when turning on the screen. DO NOT USE 7835 * unless you fully understand this interaction. 7836 * @hide 7837 */ 7838 public void setReportNextDraw() { 7839 reportNextDraw(); 7840 invalidate(); 7841 } 7842 7843 void changeCanvasOpacity(boolean opaque) { 7844 Log.d(mTag, "changeCanvasOpacity: opaque=" + opaque); 7845 if (mAttachInfo.mThreadedRenderer != null) { 7846 mAttachInfo.mThreadedRenderer.setOpaque(opaque); 7847 } 7848 } 7849 7850 /** 7851 * Dispatches a KeyEvent to all registered key fallback handlers. 7852 * 7853 * @param event 7854 * @return {@code true} if the event was handled, {@code false} otherwise. 7855 */ 7856 public boolean dispatchUnhandledKeyEvent(KeyEvent event) { 7857 return mUnhandledKeyManager.dispatch(mView, event); 7858 } 7859 7860 class TakenSurfaceHolder extends BaseSurfaceHolder { 7861 @Override 7862 public boolean onAllowLockCanvas() { 7863 return mDrawingAllowed; 7864 } 7865 7866 @Override 7867 public void onRelayoutContainer() { 7868 // Not currently interesting -- from changing between fixed and layout size. 7869 } 7870 7871 @Override 7872 public void setFormat(int format) { 7873 ((RootViewSurfaceTaker)mView).setSurfaceFormat(format); 7874 } 7875 7876 @Override 7877 public void setType(int type) { 7878 ((RootViewSurfaceTaker)mView).setSurfaceType(type); 7879 } 7880 7881 @Override 7882 public void onUpdateSurface() { 7883 // We take care of format and type changes on our own. 7884 throw new IllegalStateException("Shouldn't be here"); 7885 } 7886 7887 @Override 7888 public boolean isCreating() { 7889 return mIsCreating; 7890 } 7891 7892 @Override 7893 public void setFixedSize(int width, int height) { 7894 throw new UnsupportedOperationException( 7895 "Currently only support sizing from layout"); 7896 } 7897 7898 @Override 7899 public void setKeepScreenOn(boolean screenOn) { 7900 ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn); 7901 } 7902 } 7903 7904 static class W extends IWindow.Stub { 7905 private final WeakReference<ViewRootImpl> mViewAncestor; 7906 private final IWindowSession mWindowSession; 7907 7908 W(ViewRootImpl viewAncestor) { 7909 mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); 7910 mWindowSession = viewAncestor.mWindowSession; 7911 } 7912 7913 @Override 7914 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 7915 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, 7916 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, 7917 boolean alwaysConsumeNavBar, int displayId, 7918 DisplayCutout.ParcelableWrapper displayCutout) { 7919 final ViewRootImpl viewAncestor = mViewAncestor.get(); 7920 if (viewAncestor != null) { 7921 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets, 7922 visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration, 7923 backDropFrame, forceLayout, alwaysConsumeNavBar, displayId, displayCutout); 7924 } 7925 } 7926 7927 @Override 7928 public void moved(int newX, int newY) { 7929 final ViewRootImpl viewAncestor = mViewAncestor.get(); 7930 if (viewAncestor != null) { 7931 viewAncestor.dispatchMoved(newX, newY); 7932 } 7933 } 7934 7935 @Override 7936 public void dispatchAppVisibility(boolean visible) { 7937 final ViewRootImpl viewAncestor = mViewAncestor.get(); 7938 if (viewAncestor != null) { 7939 viewAncestor.dispatchAppVisibility(visible); 7940 } 7941 } 7942 7943 @Override 7944 public void dispatchGetNewSurface() { 7945 final ViewRootImpl viewAncestor = mViewAncestor.get(); 7946 if (viewAncestor != null) { 7947 viewAncestor.dispatchGetNewSurface(); 7948 } 7949 } 7950 7951 @Override 7952 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { 7953 final ViewRootImpl viewAncestor = mViewAncestor.get(); 7954 if (viewAncestor != null) { 7955 viewAncestor.windowFocusChanged(hasFocus, inTouchMode); 7956 } 7957 } 7958 7959 private static int checkCallingPermission(String permission) { 7960 try { 7961 return ActivityManager.getService().checkPermission( 7962 permission, Binder.getCallingPid(), Binder.getCallingUid()); 7963 } catch (RemoteException e) { 7964 return PackageManager.PERMISSION_DENIED; 7965 } 7966 } 7967 7968 @Override 7969 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 7970 final ViewRootImpl viewAncestor = mViewAncestor.get(); 7971 if (viewAncestor != null) { 7972 final View view = viewAncestor.mView; 7973 if (view != null) { 7974 if (checkCallingPermission(Manifest.permission.DUMP) != 7975 PackageManager.PERMISSION_GRANTED) { 7976 throw new SecurityException("Insufficient permissions to invoke" 7977 + " executeCommand() from pid=" + Binder.getCallingPid() 7978 + ", uid=" + Binder.getCallingUid()); 7979 } 7980 7981 OutputStream clientStream = null; 7982 try { 7983 clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out); 7984 ViewDebug.dispatchCommand(view, command, parameters, clientStream); 7985 } catch (IOException e) { 7986 e.printStackTrace(); 7987 } finally { 7988 if (clientStream != null) { 7989 try { 7990 clientStream.close(); 7991 } catch (IOException e) { 7992 e.printStackTrace(); 7993 } 7994 } 7995 } 7996 } 7997 } 7998 } 7999 8000 @Override 8001 public void closeSystemDialogs(String reason) { 8002 final ViewRootImpl viewAncestor = mViewAncestor.get(); 8003 if (viewAncestor != null) { 8004 viewAncestor.dispatchCloseSystemDialogs(reason); 8005 } 8006 } 8007 8008 @Override 8009 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 8010 boolean sync) { 8011 if (sync) { 8012 try { 8013 mWindowSession.wallpaperOffsetsComplete(asBinder()); 8014 } catch (RemoteException e) { 8015 } 8016 } 8017 } 8018 8019 @Override 8020 public void dispatchWallpaperCommand(String action, int x, int y, 8021 int z, Bundle extras, boolean sync) { 8022 if (sync) { 8023 try { 8024 mWindowSession.wallpaperCommandComplete(asBinder(), null); 8025 } catch (RemoteException e) { 8026 } 8027 } 8028 } 8029 8030 /* Drag/drop */ 8031 @Override 8032 public void dispatchDragEvent(DragEvent event) { 8033 final ViewRootImpl viewAncestor = mViewAncestor.get(); 8034 if (viewAncestor != null) { 8035 viewAncestor.dispatchDragEvent(event); 8036 } 8037 } 8038 8039 @Override 8040 public void updatePointerIcon(float x, float y) { 8041 final ViewRootImpl viewAncestor = mViewAncestor.get(); 8042 if (viewAncestor != null) { 8043 viewAncestor.updatePointerIcon(x, y); 8044 } 8045 } 8046 8047 @Override 8048 public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, 8049 int localValue, int localChanges) { 8050 final ViewRootImpl viewAncestor = mViewAncestor.get(); 8051 if (viewAncestor != null) { 8052 viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility, 8053 localValue, localChanges); 8054 } 8055 } 8056 8057 @Override 8058 public void dispatchWindowShown() { 8059 final ViewRootImpl viewAncestor = mViewAncestor.get(); 8060 if (viewAncestor != null) { 8061 viewAncestor.dispatchWindowShown(); 8062 } 8063 } 8064 8065 @Override 8066 public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) { 8067 ViewRootImpl viewAncestor = mViewAncestor.get(); 8068 if (viewAncestor != null) { 8069 viewAncestor.dispatchRequestKeyboardShortcuts(receiver, deviceId); 8070 } 8071 } 8072 8073 @Override 8074 public void dispatchPointerCaptureChanged(boolean hasCapture) { 8075 final ViewRootImpl viewAncestor = mViewAncestor.get(); 8076 if (viewAncestor != null) { 8077 viewAncestor.dispatchPointerCaptureChanged(hasCapture); 8078 } 8079 } 8080 8081 } 8082 8083 public static final class CalledFromWrongThreadException extends AndroidRuntimeException { 8084 public CalledFromWrongThreadException(String msg) { 8085 super(msg); 8086 } 8087 } 8088 8089 static HandlerActionQueue getRunQueue() { 8090 HandlerActionQueue rq = sRunQueues.get(); 8091 if (rq != null) { 8092 return rq; 8093 } 8094 rq = new HandlerActionQueue(); 8095 sRunQueues.set(rq); 8096 return rq; 8097 } 8098 8099 /** 8100 * Start a drag resizing which will inform all listeners that a window resize is taking place. 8101 */ 8102 private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets, 8103 Rect stableInsets, int resizeMode) { 8104 if (!mDragResizing) { 8105 mDragResizing = true; 8106 if (mUseMTRenderer) { 8107 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 8108 mWindowCallbacks.get(i).onWindowDragResizeStart( 8109 initialBounds, fullscreen, systemInsets, stableInsets, resizeMode); 8110 } 8111 } 8112 mFullRedrawNeeded = true; 8113 } 8114 } 8115 8116 /** 8117 * End a drag resize which will inform all listeners that a window resize has ended. 8118 */ 8119 private void endDragResizing() { 8120 if (mDragResizing) { 8121 mDragResizing = false; 8122 if (mUseMTRenderer) { 8123 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 8124 mWindowCallbacks.get(i).onWindowDragResizeEnd(); 8125 } 8126 } 8127 mFullRedrawNeeded = true; 8128 } 8129 } 8130 8131 private boolean updateContentDrawBounds() { 8132 boolean updated = false; 8133 if (mUseMTRenderer) { 8134 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 8135 updated |= 8136 mWindowCallbacks.get(i).onContentDrawn(mWindowAttributes.surfaceInsets.left, 8137 mWindowAttributes.surfaceInsets.top, mWidth, mHeight); 8138 } 8139 } 8140 return updated | (mDragResizing && mReportNextDraw); 8141 } 8142 8143 private void requestDrawWindow() { 8144 if (!mUseMTRenderer) { 8145 return; 8146 } 8147 mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size()); 8148 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { 8149 mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw); 8150 } 8151 } 8152 8153 /** 8154 * Tells this instance that its corresponding activity has just relaunched. In this case, we 8155 * need to force a relayout of the window to make sure we get the correct bounds from window 8156 * manager. 8157 */ 8158 public void reportActivityRelaunched() { 8159 mActivityRelaunched = true; 8160 } 8161 8162 /** 8163 * Class for managing the accessibility interaction connection 8164 * based on the global accessibility state. 8165 */ 8166 final class AccessibilityInteractionConnectionManager 8167 implements AccessibilityStateChangeListener { 8168 @Override 8169 public void onAccessibilityStateChanged(boolean enabled) { 8170 if (enabled) { 8171 ensureConnection(); 8172 if (mAttachInfo.mHasWindowFocus && (mView != null)) { 8173 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 8174 View focusedView = mView.findFocus(); 8175 if (focusedView != null && focusedView != mView) { 8176 focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 8177 } 8178 } 8179 } else { 8180 ensureNoConnection(); 8181 mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget(); 8182 } 8183 } 8184 8185 public void ensureConnection() { 8186 final boolean registered = mAttachInfo.mAccessibilityWindowId 8187 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 8188 if (!registered) { 8189 mAttachInfo.mAccessibilityWindowId = 8190 mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, 8191 mContext.getPackageName(), 8192 new AccessibilityInteractionConnection(ViewRootImpl.this)); 8193 } 8194 } 8195 8196 public void ensureNoConnection() { 8197 final boolean registered = mAttachInfo.mAccessibilityWindowId 8198 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 8199 if (registered) { 8200 mAttachInfo.mAccessibilityWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 8201 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); 8202 } 8203 } 8204 } 8205 8206 final class HighContrastTextManager implements HighTextContrastChangeListener { 8207 HighContrastTextManager() { 8208 ThreadedRenderer.setHighContrastText(mAccessibilityManager.isHighTextContrastEnabled()); 8209 } 8210 @Override 8211 public void onHighTextContrastStateChanged(boolean enabled) { 8212 ThreadedRenderer.setHighContrastText(enabled); 8213 8214 // Destroy Displaylists so they can be recreated with high contrast recordings 8215 destroyHardwareResources(); 8216 8217 // Schedule redraw, which will rerecord + redraw all text 8218 invalidate(); 8219 } 8220 } 8221 8222 /** 8223 * This class is an interface this ViewAncestor provides to the 8224 * AccessibilityManagerService to the latter can interact with 8225 * the view hierarchy in this ViewAncestor. 8226 */ 8227 static final class AccessibilityInteractionConnection 8228 extends IAccessibilityInteractionConnection.Stub { 8229 private final WeakReference<ViewRootImpl> mViewRootImpl; 8230 8231 AccessibilityInteractionConnection(ViewRootImpl viewRootImpl) { 8232 mViewRootImpl = new WeakReference<ViewRootImpl>(viewRootImpl); 8233 } 8234 8235 @Override 8236 public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, 8237 Region interactiveRegion, int interactionId, 8238 IAccessibilityInteractionConnectionCallback callback, int flags, 8239 int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) { 8240 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 8241 if (viewRootImpl != null && viewRootImpl.mView != null) { 8242 viewRootImpl.getAccessibilityInteractionController() 8243 .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, 8244 interactiveRegion, interactionId, callback, flags, interrogatingPid, 8245 interrogatingTid, spec, args); 8246 } else { 8247 // We cannot make the call and notify the caller so it does not wait. 8248 try { 8249 callback.setFindAccessibilityNodeInfosResult(null, interactionId); 8250 } catch (RemoteException re) { 8251 /* best effort - ignore */ 8252 } 8253 } 8254 } 8255 8256 @Override 8257 public void performAccessibilityAction(long accessibilityNodeId, int action, 8258 Bundle arguments, int interactionId, 8259 IAccessibilityInteractionConnectionCallback callback, int flags, 8260 int interrogatingPid, long interrogatingTid) { 8261 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 8262 if (viewRootImpl != null && viewRootImpl.mView != null) { 8263 viewRootImpl.getAccessibilityInteractionController() 8264 .performAccessibilityActionClientThread(accessibilityNodeId, action, arguments, 8265 interactionId, callback, flags, interrogatingPid, interrogatingTid); 8266 } else { 8267 // We cannot make the call and notify the caller so it does not wait. 8268 try { 8269 callback.setPerformAccessibilityActionResult(false, interactionId); 8270 } catch (RemoteException re) { 8271 /* best effort - ignore */ 8272 } 8273 } 8274 } 8275 8276 @Override 8277 public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, 8278 String viewId, Region interactiveRegion, int interactionId, 8279 IAccessibilityInteractionConnectionCallback callback, int flags, 8280 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 8281 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 8282 if (viewRootImpl != null && viewRootImpl.mView != null) { 8283 viewRootImpl.getAccessibilityInteractionController() 8284 .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId, 8285 viewId, interactiveRegion, interactionId, callback, flags, 8286 interrogatingPid, interrogatingTid, spec); 8287 } else { 8288 // We cannot make the call and notify the caller so it does not wait. 8289 try { 8290 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 8291 } catch (RemoteException re) { 8292 /* best effort - ignore */ 8293 } 8294 } 8295 } 8296 8297 @Override 8298 public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, 8299 Region interactiveRegion, int interactionId, 8300 IAccessibilityInteractionConnectionCallback callback, int flags, 8301 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 8302 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 8303 if (viewRootImpl != null && viewRootImpl.mView != null) { 8304 viewRootImpl.getAccessibilityInteractionController() 8305 .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, 8306 interactiveRegion, interactionId, callback, flags, interrogatingPid, 8307 interrogatingTid, spec); 8308 } else { 8309 // We cannot make the call and notify the caller so it does not wait. 8310 try { 8311 callback.setFindAccessibilityNodeInfosResult(null, interactionId); 8312 } catch (RemoteException re) { 8313 /* best effort - ignore */ 8314 } 8315 } 8316 } 8317 8318 @Override 8319 public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, 8320 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, 8321 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 8322 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 8323 if (viewRootImpl != null && viewRootImpl.mView != null) { 8324 viewRootImpl.getAccessibilityInteractionController() 8325 .findFocusClientThread(accessibilityNodeId, focusType, interactiveRegion, 8326 interactionId, callback, flags, interrogatingPid, interrogatingTid, 8327 spec); 8328 } else { 8329 // We cannot make the call and notify the caller so it does not wait. 8330 try { 8331 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 8332 } catch (RemoteException re) { 8333 /* best effort - ignore */ 8334 } 8335 } 8336 } 8337 8338 @Override 8339 public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, 8340 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, 8341 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { 8342 ViewRootImpl viewRootImpl = mViewRootImpl.get(); 8343 if (viewRootImpl != null && viewRootImpl.mView != null) { 8344 viewRootImpl.getAccessibilityInteractionController() 8345 .focusSearchClientThread(accessibilityNodeId, direction, interactiveRegion, 8346 interactionId, callback, flags, interrogatingPid, interrogatingTid, 8347 spec); 8348 } else { 8349 // We cannot make the call and notify the caller so it does not wait. 8350 try { 8351 callback.setFindAccessibilityNodeInfoResult(null, interactionId); 8352 } catch (RemoteException re) { 8353 /* best effort - ignore */ 8354 } 8355 } 8356 } 8357 } 8358 8359 private class SendWindowContentChangedAccessibilityEvent implements Runnable { 8360 private int mChangeTypes = 0; 8361 8362 public View mSource; 8363 public long mLastEventTimeMillis; 8364 /** 8365 * Override for {@link AccessibilityEvent#originStackTrace} to provide the stack trace 8366 * of the original {@link #runOrPost} call instead of one for sending the delayed event 8367 * from a looper. 8368 */ 8369 public StackTraceElement[] mOrigin; 8370 8371 @Override 8372 public void run() { 8373 // Protect against re-entrant code and attempt to do the right thing in the case that 8374 // we're multithreaded. 8375 View source = mSource; 8376 mSource = null; 8377 if (source == null) { 8378 Log.e(TAG, "Accessibility content change has no source"); 8379 return; 8380 } 8381 // The accessibility may be turned off while we were waiting so check again. 8382 if (AccessibilityManager.getInstance(mContext).isEnabled()) { 8383 mLastEventTimeMillis = SystemClock.uptimeMillis(); 8384 AccessibilityEvent event = AccessibilityEvent.obtain(); 8385 event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 8386 event.setContentChangeTypes(mChangeTypes); 8387 if (AccessibilityEvent.DEBUG_ORIGIN) event.originStackTrace = mOrigin; 8388 source.sendAccessibilityEventUnchecked(event); 8389 } else { 8390 mLastEventTimeMillis = 0; 8391 } 8392 // In any case reset to initial state. 8393 source.resetSubtreeAccessibilityStateChanged(); 8394 mChangeTypes = 0; 8395 if (AccessibilityEvent.DEBUG_ORIGIN) mOrigin = null; 8396 } 8397 8398 public void runOrPost(View source, int changeType) { 8399 if (mHandler.getLooper() != Looper.myLooper()) { 8400 CalledFromWrongThreadException e = new CalledFromWrongThreadException("Only the " 8401 + "original thread that created a view hierarchy can touch its views."); 8402 // TODO: Throw the exception 8403 Log.e(TAG, "Accessibility content change on non-UI thread. Future Android " 8404 + "versions will throw an exception.", e); 8405 // Attempt to recover. This code does not eliminate the thread safety issue, but 8406 // it should force any issues to happen near the above log. 8407 mHandler.removeCallbacks(this); 8408 if (mSource != null) { 8409 // Dispatch whatever was pending. It's still possible that the runnable started 8410 // just before we removed the callbacks, and bad things will happen, but at 8411 // least they should happen very close to the logged error. 8412 run(); 8413 } 8414 } 8415 if (mSource != null) { 8416 // If there is no common predecessor, then mSource points to 8417 // a removed view, hence in this case always prefer the source. 8418 View predecessor = getCommonPredecessor(mSource, source); 8419 if (predecessor != null) { 8420 predecessor = predecessor.getSelfOrParentImportantForA11y(); 8421 } 8422 mSource = (predecessor != null) ? predecessor : source; 8423 mChangeTypes |= changeType; 8424 return; 8425 } 8426 mSource = source; 8427 mChangeTypes = changeType; 8428 if (AccessibilityEvent.DEBUG_ORIGIN) { 8429 mOrigin = Thread.currentThread().getStackTrace(); 8430 } 8431 final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis; 8432 final long minEventIntevalMillis = 8433 ViewConfiguration.getSendRecurringAccessibilityEventsInterval(); 8434 if (timeSinceLastMillis >= minEventIntevalMillis) { 8435 removeCallbacksAndRun(); 8436 } else { 8437 mHandler.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis); 8438 } 8439 } 8440 8441 public void removeCallbacksAndRun() { 8442 mHandler.removeCallbacks(this); 8443 run(); 8444 } 8445 } 8446 8447 private static class UnhandledKeyManager { 8448 // This is used to ensure that unhandled events are only dispatched once. We attempt 8449 // to dispatch more than once in order to achieve a certain order. Specifically, if we 8450 // are in an Activity or Dialog (and have a Window.Callback), the unhandled events should 8451 // be dispatched after the view hierarchy, but before the Callback. However, if we aren't 8452 // in an activity, we still want unhandled keys to be dispatched. 8453 private boolean mDispatched = true; 8454 8455 // Keeps track of which Views have unhandled key focus for which keys. This doesn't 8456 // include modifiers. 8457 private final SparseArray<WeakReference<View>> mCapturedKeys = new SparseArray<>(); 8458 8459 // The current receiver. This value is transient and used between the pre-dispatch and 8460 // pre-view phase to ensure that other input-stages don't interfere with tracking. 8461 private WeakReference<View> mCurrentReceiver = null; 8462 8463 boolean dispatch(View root, KeyEvent event) { 8464 if (mDispatched) { 8465 return false; 8466 } 8467 View consumer; 8468 try { 8469 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "UnhandledKeyEvent dispatch"); 8470 mDispatched = true; 8471 8472 consumer = root.dispatchUnhandledKeyEvent(event); 8473 8474 // If an unhandled listener handles one, then keep track of it so that the 8475 // consuming view is first to receive its repeats and release as well. 8476 if (event.getAction() == KeyEvent.ACTION_DOWN) { 8477 int keycode = event.getKeyCode(); 8478 if (consumer != null && !KeyEvent.isModifierKey(keycode)) { 8479 mCapturedKeys.put(keycode, new WeakReference<>(consumer)); 8480 } 8481 } 8482 } finally { 8483 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 8484 } 8485 return consumer != null; 8486 } 8487 8488 /** 8489 * Called before the event gets dispatched to anything 8490 */ 8491 void preDispatch(KeyEvent event) { 8492 // Always clean-up 'up' events since it's possible for earlier dispatch stages to 8493 // consume them without consuming the corresponding 'down' event. 8494 mCurrentReceiver = null; 8495 if (event.getAction() == KeyEvent.ACTION_UP) { 8496 int idx = mCapturedKeys.indexOfKey(event.getKeyCode()); 8497 if (idx >= 0) { 8498 mCurrentReceiver = mCapturedKeys.valueAt(idx); 8499 mCapturedKeys.removeAt(idx); 8500 } 8501 } 8502 } 8503 8504 /** 8505 * Called before the event gets dispatched to the view hierarchy 8506 * @return {@code true} if an unhandled handler has focus and consumed the event 8507 */ 8508 boolean preViewDispatch(KeyEvent event) { 8509 mDispatched = false; 8510 if (mCurrentReceiver == null) { 8511 mCurrentReceiver = mCapturedKeys.get(event.getKeyCode()); 8512 } 8513 if (mCurrentReceiver != null) { 8514 View target = mCurrentReceiver.get(); 8515 if (event.getAction() == KeyEvent.ACTION_UP) { 8516 mCurrentReceiver = null; 8517 } 8518 if (target != null && target.isAttachedToWindow()) { 8519 target.onUnhandledKeyEvent(event); 8520 } 8521 // consume anyways so that we don't feed uncaptured key events to other views 8522 return true; 8523 } 8524 return false; 8525 } 8526 } 8527} 8528