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