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