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