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