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