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