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