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