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