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