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