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