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