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