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