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