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