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