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 com.android.internal.view.BaseSurfaceHolder;
20import com.android.internal.view.IInputMethodCallback;
21import com.android.internal.view.IInputMethodSession;
22import com.android.internal.view.RootViewSurfaceTaker;
23
24import android.graphics.Canvas;
25import android.graphics.PixelFormat;
26import android.graphics.PorterDuff;
27import android.graphics.Rect;
28import android.graphics.Region;
29import android.os.*;
30import android.os.Process;
31import android.util.AndroidRuntimeException;
32import android.util.Config;
33import android.util.DisplayMetrics;
34import android.util.Log;
35import android.util.EventLog;
36import android.util.Slog;
37import android.util.SparseArray;
38import android.view.View.MeasureSpec;
39import android.view.accessibility.AccessibilityEvent;
40import android.view.accessibility.AccessibilityManager;
41import android.view.inputmethod.InputConnection;
42import android.view.inputmethod.InputMethodManager;
43import android.widget.Scroller;
44import android.content.pm.PackageManager;
45import android.content.res.CompatibilityInfo;
46import android.content.res.Configuration;
47import android.content.res.Resources;
48import android.content.ComponentCallbacks;
49import android.content.Context;
50import android.app.ActivityManagerNative;
51import android.Manifest;
52import android.media.AudioManager;
53
54import java.lang.ref.WeakReference;
55import java.io.IOException;
56import java.io.OutputStream;
57import java.util.ArrayList;
58
59import javax.microedition.khronos.egl.*;
60import javax.microedition.khronos.opengles.*;
61import static javax.microedition.khronos.opengles.GL10.*;
62
63/**
64 * The top of a view hierarchy, implementing the needed protocol between View
65 * and the WindowManager.  This is for the most part an internal implementation
66 * detail of {@link WindowManagerImpl}.
67 *
68 * {@hide}
69 */
70@SuppressWarnings({"EmptyCatchBlock"})
71public final class ViewRoot extends Handler implements ViewParent,
72        View.AttachInfo.Callbacks {
73    private static final String TAG = "ViewRoot";
74    private static final boolean DBG = false;
75    private static final boolean SHOW_FPS = false;
76    @SuppressWarnings({"ConstantConditionalExpression"})
77    private static final boolean LOCAL_LOGV = false ? Config.LOGD : Config.LOGV;
78    /** @noinspection PointlessBooleanExpression*/
79    private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
80    private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
81    private static final boolean DEBUG_INPUT = true || LOCAL_LOGV;
82    private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;
83    private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
84    private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
85    private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
86    private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
87    private static final boolean WATCH_POINTER = false;
88
89    private static final boolean MEASURE_LATENCY = false;
90    private static LatencyTimer lt;
91
92    /**
93     * Maximum time we allow the user to roll the trackball enough to generate
94     * a key event, before resetting the counters.
95     */
96    static final int MAX_TRACKBALL_DELAY = 250;
97
98    static long sInstanceCount = 0;
99
100    static IWindowSession sWindowSession;
101
102    static final Object mStaticInit = new Object();
103    static boolean mInitialized = false;
104
105    static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
106
107    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
108    static boolean sFirstDrawComplete = false;
109
110    static final ArrayList<ComponentCallbacks> sConfigCallbacks
111            = new ArrayList<ComponentCallbacks>();
112
113    private static int sDrawTime;
114
115    long mLastTrackballTime = 0;
116    final TrackballAxis mTrackballAxisX = new TrackballAxis();
117    final TrackballAxis mTrackballAxisY = new TrackballAxis();
118
119    final int[] mTmpLocation = new int[2];
120
121    final InputMethodCallback mInputMethodCallback;
122    final SparseArray<Object> mPendingEvents = new SparseArray<Object>();
123    int mPendingEventSeq = 0;
124
125    final Thread mThread;
126
127    final WindowLeaked mLocation;
128
129    final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
130
131    final W mWindow;
132
133    View mView;
134    View mFocusedView;
135    View mRealFocusedView;  // this is not set to null in touch mode
136    int mViewVisibility;
137    boolean mAppVisible = true;
138
139    SurfaceHolder.Callback2 mSurfaceHolderCallback;
140    BaseSurfaceHolder mSurfaceHolder;
141    boolean mIsCreating;
142    boolean mDrawingAllowed;
143
144    final Region mTransparentRegion;
145    final Region mPreviousTransparentRegion;
146
147    int mWidth;
148    int mHeight;
149    Rect mDirty; // will be a graphics.Region soon
150    boolean mIsAnimating;
151
152    CompatibilityInfo.Translator mTranslator;
153
154    final View.AttachInfo mAttachInfo;
155    InputChannel mInputChannel;
156    InputQueue.Callback mInputQueueCallback;
157    InputQueue mInputQueue;
158
159    final Rect mTempRect; // used in the transaction to not thrash the heap.
160    final Rect mVisRect; // used to retrieve visible rect of focused view.
161
162    boolean mTraversalScheduled;
163    boolean mWillDrawSoon;
164    boolean mLayoutRequested;
165    boolean mFirst;
166    boolean mReportNextDraw;
167    boolean mFullRedrawNeeded;
168    boolean mNewSurfaceNeeded;
169    boolean mHasHadWindowFocus;
170    boolean mLastWasImTarget;
171
172    boolean mWindowAttributesChanged = false;
173
174    // These can be accessed by any thread, must be protected with a lock.
175    // Surface can never be reassigned or cleared (use Surface.clear()).
176    private final Surface mSurface = new Surface();
177
178    boolean mAdded;
179    boolean mAddedTouchMode;
180
181    /*package*/ int mAddNesting;
182
183    // These are accessed by multiple threads.
184    final Rect mWinFrame; // frame given by window manager.
185
186    final Rect mPendingVisibleInsets = new Rect();
187    final Rect mPendingContentInsets = new Rect();
188    final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
189            = new ViewTreeObserver.InternalInsetsInfo();
190
191    final Configuration mLastConfiguration = new Configuration();
192    final Configuration mPendingConfiguration = new Configuration();
193
194    class ResizedInfo {
195        Rect coveredInsets;
196        Rect visibleInsets;
197        Configuration newConfig;
198    }
199
200    boolean mScrollMayChange;
201    int mSoftInputMode;
202    View mLastScrolledFocus;
203    int mScrollY;
204    int mCurScrollY;
205    Scroller mScroller;
206
207    EGL10 mEgl;
208    EGLDisplay mEglDisplay;
209    EGLContext mEglContext;
210    EGLSurface mEglSurface;
211    GL11 mGL;
212    Canvas mGlCanvas;
213    boolean mUseGL;
214    boolean mGlWanted;
215
216    final ViewConfiguration mViewConfiguration;
217
218    /**
219     * see {@link #playSoundEffect(int)}
220     */
221    AudioManager mAudioManager;
222
223    private final int mDensity;
224
225    public static IWindowSession getWindowSession(Looper mainLooper) {
226        synchronized (mStaticInit) {
227            if (!mInitialized) {
228                try {
229                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
230                    sWindowSession = IWindowManager.Stub.asInterface(
231                            ServiceManager.getService("window"))
232                            .openSession(imm.getClient(), imm.getInputContext());
233                    mInitialized = true;
234                } catch (RemoteException e) {
235                }
236            }
237            return sWindowSession;
238        }
239    }
240
241    public ViewRoot(Context context) {
242        super();
243
244        if (MEASURE_LATENCY && lt == null) {
245            lt = new LatencyTimer(100, 1000);
246        }
247
248        // For debug only
249        //++sInstanceCount;
250
251        // Initialize the statics when this class is first instantiated. This is
252        // done here instead of in the static block because Zygote does not
253        // allow the spawning of threads.
254        getWindowSession(context.getMainLooper());
255
256        mThread = Thread.currentThread();
257        mLocation = new WindowLeaked(null);
258        mLocation.fillInStackTrace();
259        mWidth = -1;
260        mHeight = -1;
261        mDirty = new Rect();
262        mTempRect = new Rect();
263        mVisRect = new Rect();
264        mWinFrame = new Rect();
265        mWindow = new W(this, context);
266        mInputMethodCallback = new InputMethodCallback(this);
267        mViewVisibility = View.GONE;
268        mTransparentRegion = new Region();
269        mPreviousTransparentRegion = new Region();
270        mFirst = true; // true for the first time the view is added
271        mAdded = false;
272        mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
273        mViewConfiguration = ViewConfiguration.get(context);
274        mDensity = context.getResources().getDisplayMetrics().densityDpi;
275    }
276
277    // For debug only
278    /*
279    @Override
280    protected void finalize() throws Throwable {
281        super.finalize();
282        --sInstanceCount;
283    }
284    */
285
286    public static long getInstanceCount() {
287        return sInstanceCount;
288    }
289
290    public static void addFirstDrawHandler(Runnable callback) {
291        synchronized (sFirstDrawHandlers) {
292            if (!sFirstDrawComplete) {
293                sFirstDrawHandlers.add(callback);
294            }
295        }
296    }
297
298    public static void addConfigCallback(ComponentCallbacks callback) {
299        synchronized (sConfigCallbacks) {
300            sConfigCallbacks.add(callback);
301        }
302    }
303
304    // FIXME for perf testing only
305    private boolean mProfile = false;
306
307    /**
308     * Call this to profile the next traversal call.
309     * FIXME for perf testing only. Remove eventually
310     */
311    public void profile() {
312        mProfile = true;
313    }
314
315    /**
316     * Indicates whether we are in touch mode. Calling this method triggers an IPC
317     * call and should be avoided whenever possible.
318     *
319     * @return True, if the device is in touch mode, false otherwise.
320     *
321     * @hide
322     */
323    static boolean isInTouchMode() {
324        if (mInitialized) {
325            try {
326                return sWindowSession.getInTouchMode();
327            } catch (RemoteException e) {
328            }
329        }
330        return false;
331    }
332
333    private void initializeGL() {
334        initializeGLInner();
335        int err = mEgl.eglGetError();
336        if (err != EGL10.EGL_SUCCESS) {
337            // give-up on using GL
338            destroyGL();
339            mGlWanted = false;
340        }
341    }
342
343    private void initializeGLInner() {
344        final EGL10 egl = (EGL10) EGLContext.getEGL();
345        mEgl = egl;
346
347        /*
348         * Get to the default display.
349         */
350        final EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
351        mEglDisplay = eglDisplay;
352
353        /*
354         * We can now initialize EGL for that display
355         */
356        int[] version = new int[2];
357        egl.eglInitialize(eglDisplay, version);
358
359        /*
360         * Specify a configuration for our opengl session
361         * and grab the first configuration that matches is
362         */
363        final int[] configSpec = {
364                EGL10.EGL_RED_SIZE,      5,
365                EGL10.EGL_GREEN_SIZE,    6,
366                EGL10.EGL_BLUE_SIZE,     5,
367                EGL10.EGL_DEPTH_SIZE,    0,
368                EGL10.EGL_NONE
369        };
370        final EGLConfig[] configs = new EGLConfig[1];
371        final int[] num_config = new int[1];
372        egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, num_config);
373        final EGLConfig config = configs[0];
374
375        /*
376         * Create an OpenGL ES context. This must be done only once, an
377         * OpenGL context is a somewhat heavy object.
378         */
379        final EGLContext context = egl.eglCreateContext(eglDisplay, config,
380                EGL10.EGL_NO_CONTEXT, null);
381        mEglContext = context;
382
383        /*
384         * Create an EGL surface we can render into.
385         */
386        final EGLSurface surface = egl.eglCreateWindowSurface(eglDisplay, config, mHolder, null);
387        mEglSurface = surface;
388
389        /*
390         * Before we can issue GL commands, we need to make sure
391         * the context is current and bound to a surface.
392         */
393        egl.eglMakeCurrent(eglDisplay, surface, surface, context);
394
395        /*
396         * Get to the appropriate GL interface.
397         * This is simply done by casting the GL context to either
398         * GL10 or GL11.
399         */
400        final GL11 gl = (GL11) context.getGL();
401        mGL = gl;
402        mGlCanvas = new Canvas(gl);
403        mUseGL = true;
404    }
405
406    private void destroyGL() {
407        // inform skia that the context is gone
408        nativeAbandonGlCaches();
409
410        mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
411                EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
412        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
413        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
414        mEgl.eglTerminate(mEglDisplay);
415        mEglContext = null;
416        mEglSurface = null;
417        mEglDisplay = null;
418        mEgl = null;
419        mGlCanvas = null;
420        mGL = null;
421        mUseGL = false;
422    }
423
424    private void checkEglErrors() {
425        if (mUseGL) {
426            int err = mEgl.eglGetError();
427            if (err != EGL10.EGL_SUCCESS) {
428                // something bad has happened revert to
429                // normal rendering.
430                destroyGL();
431                if (err != EGL11.EGL_CONTEXT_LOST) {
432                    // we'll try again if it was context lost
433                    mGlWanted = false;
434                }
435            }
436        }
437    }
438
439    /**
440     * We have one child
441     */
442    public void setView(View view, WindowManager.LayoutParams attrs,
443            View panelParentView) {
444        synchronized (this) {
445            if (mView == null) {
446                mView = view;
447                mWindowAttributes.copyFrom(attrs);
448                attrs = mWindowAttributes;
449                if (view instanceof RootViewSurfaceTaker) {
450                    mSurfaceHolderCallback =
451                            ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
452                    if (mSurfaceHolderCallback != null) {
453                        mSurfaceHolder = new TakenSurfaceHolder();
454                        mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
455                    }
456                }
457                Resources resources = mView.getContext().getResources();
458                CompatibilityInfo compatibilityInfo = resources.getCompatibilityInfo();
459                mTranslator = compatibilityInfo.getTranslator();
460
461                if (mTranslator != null || !compatibilityInfo.supportsScreen()) {
462                    mSurface.setCompatibleDisplayMetrics(resources.getDisplayMetrics(),
463                            mTranslator);
464                }
465
466                boolean restore = false;
467                if (mTranslator != null) {
468                    restore = true;
469                    attrs.backup();
470                    mTranslator.translateWindowLayout(attrs);
471                }
472                if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
473
474                if (!compatibilityInfo.supportsScreen()) {
475                    attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
476                }
477
478                mSoftInputMode = attrs.softInputMode;
479                mWindowAttributesChanged = true;
480                mAttachInfo.mRootView = view;
481                mAttachInfo.mScalingRequired = mTranslator != null;
482                mAttachInfo.mApplicationScale =
483                        mTranslator == null ? 1.0f : mTranslator.applicationScale;
484                if (panelParentView != null) {
485                    mAttachInfo.mPanelParentWindowToken
486                            = panelParentView.getApplicationWindowToken();
487                }
488                mAdded = true;
489                int res; /* = WindowManagerImpl.ADD_OKAY; */
490
491                // Schedule the first layout -before- adding to the window
492                // manager, to make sure we do the relayout before receiving
493                // any other events from the system.
494                requestLayout();
495                mInputChannel = new InputChannel();
496                try {
497                    res = sWindowSession.add(mWindow, mWindowAttributes,
498                            getHostVisibility(), mAttachInfo.mContentInsets,
499                            mInputChannel);
500                } catch (RemoteException e) {
501                    mAdded = false;
502                    mView = null;
503                    mAttachInfo.mRootView = null;
504                    mInputChannel = null;
505                    unscheduleTraversals();
506                    throw new RuntimeException("Adding window failed", e);
507                } finally {
508                    if (restore) {
509                        attrs.restore();
510                    }
511                }
512
513                if (mTranslator != null) {
514                    mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
515                }
516                mPendingContentInsets.set(mAttachInfo.mContentInsets);
517                mPendingVisibleInsets.set(0, 0, 0, 0);
518                if (Config.LOGV) Log.v(TAG, "Added window " + mWindow);
519                if (res < WindowManagerImpl.ADD_OKAY) {
520                    mView = null;
521                    mAttachInfo.mRootView = null;
522                    mAdded = false;
523                    unscheduleTraversals();
524                    switch (res) {
525                        case WindowManagerImpl.ADD_BAD_APP_TOKEN:
526                        case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
527                            throw new WindowManagerImpl.BadTokenException(
528                                "Unable to add window -- token " + attrs.token
529                                + " is not valid; is your activity running?");
530                        case WindowManagerImpl.ADD_NOT_APP_TOKEN:
531                            throw new WindowManagerImpl.BadTokenException(
532                                "Unable to add window -- token " + attrs.token
533                                + " is not for an application");
534                        case WindowManagerImpl.ADD_APP_EXITING:
535                            throw new WindowManagerImpl.BadTokenException(
536                                "Unable to add window -- app for token " + attrs.token
537                                + " is exiting");
538                        case WindowManagerImpl.ADD_DUPLICATE_ADD:
539                            throw new WindowManagerImpl.BadTokenException(
540                                "Unable to add window -- window " + mWindow
541                                + " has already been added");
542                        case WindowManagerImpl.ADD_STARTING_NOT_NEEDED:
543                            // Silently ignore -- we would have just removed it
544                            // right away, anyway.
545                            return;
546                        case WindowManagerImpl.ADD_MULTIPLE_SINGLETON:
547                            throw new WindowManagerImpl.BadTokenException(
548                                "Unable to add window " + mWindow +
549                                " -- another window of this type already exists");
550                        case WindowManagerImpl.ADD_PERMISSION_DENIED:
551                            throw new WindowManagerImpl.BadTokenException(
552                                "Unable to add window " + mWindow +
553                                " -- permission denied for this window type");
554                    }
555                    throw new RuntimeException(
556                        "Unable to add window -- unknown error code " + res);
557                }
558
559                if (view instanceof RootViewSurfaceTaker) {
560                    mInputQueueCallback =
561                        ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
562                }
563                if (mInputQueueCallback != null) {
564                    mInputQueue = new InputQueue(mInputChannel);
565                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
566                } else {
567                    InputQueue.registerInputChannel(mInputChannel, mInputHandler,
568                            Looper.myQueue());
569                }
570
571                view.assignParent(this);
572                mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
573                mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
574            }
575        }
576    }
577
578    public View getView() {
579        return mView;
580    }
581
582    final WindowLeaked getLocation() {
583        return mLocation;
584    }
585
586    void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
587        synchronized (this) {
588            int oldSoftInputMode = mWindowAttributes.softInputMode;
589            // preserve compatible window flag if exists.
590            int compatibleWindowFlag =
591                mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
592            mWindowAttributes.copyFrom(attrs);
593            mWindowAttributes.flags |= compatibleWindowFlag;
594
595            if (newView) {
596                mSoftInputMode = attrs.softInputMode;
597                requestLayout();
598            }
599            // Don't lose the mode we last auto-computed.
600            if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
601                    == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
602                mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
603                        & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
604                        | (oldSoftInputMode
605                                & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
606            }
607            mWindowAttributesChanged = true;
608            scheduleTraversals();
609        }
610    }
611
612    void handleAppVisibility(boolean visible) {
613        if (mAppVisible != visible) {
614            mAppVisible = visible;
615            scheduleTraversals();
616        }
617    }
618
619    void handleGetNewSurface() {
620        mNewSurfaceNeeded = true;
621        mFullRedrawNeeded = true;
622        scheduleTraversals();
623    }
624
625    /**
626     * {@inheritDoc}
627     */
628    public void requestLayout() {
629        checkThread();
630        mLayoutRequested = true;
631        scheduleTraversals();
632    }
633
634    /**
635     * {@inheritDoc}
636     */
637    public boolean isLayoutRequested() {
638        return mLayoutRequested;
639    }
640
641    public void invalidateChild(View child, Rect dirty) {
642        checkThread();
643        if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
644        if (mCurScrollY != 0 || mTranslator != null) {
645            mTempRect.set(dirty);
646            dirty = mTempRect;
647            if (mCurScrollY != 0) {
648               dirty.offset(0, -mCurScrollY);
649            }
650            if (mTranslator != null) {
651                mTranslator.translateRectInAppWindowToScreen(dirty);
652            }
653            if (mAttachInfo.mScalingRequired) {
654                dirty.inset(-1, -1);
655            }
656        }
657        mDirty.union(dirty);
658        if (!mWillDrawSoon) {
659            scheduleTraversals();
660        }
661    }
662
663    public ViewParent getParent() {
664        return null;
665    }
666
667    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
668        invalidateChild(null, dirty);
669        return null;
670    }
671
672    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
673        if (child != mView) {
674            throw new RuntimeException("child is not mine, honest!");
675        }
676        // Note: don't apply scroll offset, because we want to know its
677        // visibility in the virtual canvas being given to the view hierarchy.
678        return r.intersect(0, 0, mWidth, mHeight);
679    }
680
681    public void bringChildToFront(View child) {
682    }
683
684    public void scheduleTraversals() {
685        if (!mTraversalScheduled) {
686            mTraversalScheduled = true;
687            sendEmptyMessage(DO_TRAVERSAL);
688        }
689    }
690
691    public void unscheduleTraversals() {
692        if (mTraversalScheduled) {
693            mTraversalScheduled = false;
694            removeMessages(DO_TRAVERSAL);
695        }
696    }
697
698    int getHostVisibility() {
699        return mAppVisible ? mView.getVisibility() : View.GONE;
700    }
701
702    private void performTraversals() {
703        // cache mView since it is used so much below...
704        final View host = mView;
705
706        if (DBG) {
707            System.out.println("======================================");
708            System.out.println("performTraversals");
709            host.debug();
710        }
711
712        if (host == null || !mAdded)
713            return;
714
715        mTraversalScheduled = false;
716        mWillDrawSoon = true;
717        boolean windowResizesToFitContent = false;
718        boolean fullRedrawNeeded = mFullRedrawNeeded;
719        boolean newSurface = false;
720        boolean surfaceChanged = false;
721        WindowManager.LayoutParams lp = mWindowAttributes;
722
723        int desiredWindowWidth;
724        int desiredWindowHeight;
725        int childWidthMeasureSpec;
726        int childHeightMeasureSpec;
727
728        final View.AttachInfo attachInfo = mAttachInfo;
729
730        final int viewVisibility = getHostVisibility();
731        boolean viewVisibilityChanged = mViewVisibility != viewVisibility
732                || mNewSurfaceNeeded;
733
734        float appScale = mAttachInfo.mApplicationScale;
735
736        WindowManager.LayoutParams params = null;
737        if (mWindowAttributesChanged) {
738            mWindowAttributesChanged = false;
739            surfaceChanged = true;
740            params = lp;
741        }
742        Rect frame = mWinFrame;
743        if (mFirst) {
744            fullRedrawNeeded = true;
745            mLayoutRequested = true;
746
747            DisplayMetrics packageMetrics =
748                mView.getContext().getResources().getDisplayMetrics();
749            desiredWindowWidth = packageMetrics.widthPixels;
750            desiredWindowHeight = packageMetrics.heightPixels;
751
752            // For the very first time, tell the view hierarchy that it
753            // is attached to the window.  Note that at this point the surface
754            // object is not initialized to its backing store, but soon it
755            // will be (assuming the window is visible).
756            attachInfo.mSurface = mSurface;
757            attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
758                    lp.format == PixelFormat.RGBX_8888;
759            attachInfo.mHasWindowFocus = false;
760            attachInfo.mWindowVisibility = viewVisibility;
761            attachInfo.mRecomputeGlobalAttributes = false;
762            attachInfo.mKeepScreenOn = false;
763            viewVisibilityChanged = false;
764            mLastConfiguration.setTo(host.getResources().getConfiguration());
765            host.dispatchAttachedToWindow(attachInfo, 0);
766            //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
767
768        } else {
769            desiredWindowWidth = frame.width();
770            desiredWindowHeight = frame.height();
771            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
772                if (DEBUG_ORIENTATION) Log.v(TAG,
773                        "View " + host + " resized to: " + frame);
774                fullRedrawNeeded = true;
775                mLayoutRequested = true;
776                windowResizesToFitContent = true;
777            }
778        }
779
780        if (viewVisibilityChanged) {
781            attachInfo.mWindowVisibility = viewVisibility;
782            host.dispatchWindowVisibilityChanged(viewVisibility);
783            if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
784                if (mUseGL) {
785                    destroyGL();
786                }
787            }
788            if (viewVisibility == View.GONE) {
789                // After making a window gone, we will count it as being
790                // shown for the first time the next time it gets focus.
791                mHasHadWindowFocus = false;
792            }
793        }
794
795        boolean insetsChanged = false;
796
797        if (mLayoutRequested) {
798            // Execute enqueued actions on every layout in case a view that was detached
799            // enqueued an action after being detached
800            getRunQueue().executeActions(attachInfo.mHandler);
801
802            if (mFirst) {
803                host.fitSystemWindows(mAttachInfo.mContentInsets);
804                // make sure touch mode code executes by setting cached value
805                // to opposite of the added touch mode.
806                mAttachInfo.mInTouchMode = !mAddedTouchMode;
807                ensureTouchModeLocally(mAddedTouchMode);
808            } else {
809                if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
810                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
811                    host.fitSystemWindows(mAttachInfo.mContentInsets);
812                    insetsChanged = true;
813                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
814                            + mAttachInfo.mContentInsets);
815                }
816                if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) {
817                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
818                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
819                            + mAttachInfo.mVisibleInsets);
820                }
821                if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
822                        || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
823                    windowResizesToFitContent = true;
824
825                    DisplayMetrics packageMetrics =
826                        mView.getContext().getResources().getDisplayMetrics();
827                    desiredWindowWidth = packageMetrics.widthPixels;
828                    desiredWindowHeight = packageMetrics.heightPixels;
829                }
830            }
831
832            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
833            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
834
835            // Ask host how big it wants to be
836            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
837                    "Measuring " + host + " in display " + desiredWindowWidth
838                    + "x" + desiredWindowHeight + "...");
839            host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
840
841            if (DBG) {
842                System.out.println("======================================");
843                System.out.println("performTraversals -- after measure");
844                host.debug();
845            }
846        }
847
848        if (attachInfo.mRecomputeGlobalAttributes) {
849            //Log.i(TAG, "Computing screen on!");
850            attachInfo.mRecomputeGlobalAttributes = false;
851            boolean oldVal = attachInfo.mKeepScreenOn;
852            attachInfo.mKeepScreenOn = false;
853            host.dispatchCollectViewAttributes(0);
854            if (attachInfo.mKeepScreenOn != oldVal) {
855                params = lp;
856                //Log.i(TAG, "Keep screen on changed: " + attachInfo.mKeepScreenOn);
857            }
858        }
859
860        if (mFirst || attachInfo.mViewVisibilityChanged) {
861            attachInfo.mViewVisibilityChanged = false;
862            int resizeMode = mSoftInputMode &
863                    WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
864            // If we are in auto resize mode, then we need to determine
865            // what mode to use now.
866            if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
867                final int N = attachInfo.mScrollContainers.size();
868                for (int i=0; i<N; i++) {
869                    if (attachInfo.mScrollContainers.get(i).isShown()) {
870                        resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
871                    }
872                }
873                if (resizeMode == 0) {
874                    resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
875                }
876                if ((lp.softInputMode &
877                        WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
878                    lp.softInputMode = (lp.softInputMode &
879                            ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
880                            resizeMode;
881                    params = lp;
882                }
883            }
884        }
885
886        if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
887            if (!PixelFormat.formatHasAlpha(params.format)) {
888                params.format = PixelFormat.TRANSLUCENT;
889            }
890        }
891
892        boolean windowShouldResize = mLayoutRequested && windowResizesToFitContent
893            && ((mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight)
894                || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
895                        frame.width() < desiredWindowWidth && frame.width() != mWidth)
896                || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
897                        frame.height() < desiredWindowHeight && frame.height() != mHeight));
898
899        final boolean computesInternalInsets =
900                attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
901        boolean insetsPending = false;
902        int relayoutResult = 0;
903        if (mFirst || windowShouldResize || insetsChanged
904                || viewVisibilityChanged || params != null) {
905
906            if (viewVisibility == View.VISIBLE) {
907                // If this window is giving internal insets to the window
908                // manager, and it is being added or changing its visibility,
909                // then we want to first give the window manager "fake"
910                // insets to cause it to effectively ignore the content of
911                // the window during layout.  This avoids it briefly causing
912                // other windows to resize/move based on the raw frame of the
913                // window, waiting until we can finish laying out this window
914                // and get back to the window manager with the ultimately
915                // computed insets.
916                insetsPending = computesInternalInsets
917                        && (mFirst || viewVisibilityChanged);
918
919                if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) {
920                    if (params == null) {
921                        params = mWindowAttributes;
922                    }
923                    mGlWanted = true;
924                }
925            }
926
927            if (mSurfaceHolder != null) {
928                mSurfaceHolder.mSurfaceLock.lock();
929                mDrawingAllowed = true;
930            }
931
932            boolean initialized = false;
933            boolean contentInsetsChanged = false;
934            boolean visibleInsetsChanged;
935            boolean hadSurface = mSurface.isValid();
936            try {
937                int fl = 0;
938                if (params != null) {
939                    fl = params.flags;
940                    if (attachInfo.mKeepScreenOn) {
941                        params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
942                    }
943                }
944                if (DEBUG_LAYOUT) {
945                    Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" +
946                            host.mMeasuredHeight + ", params=" + params);
947                }
948                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
949
950                if (params != null) {
951                    params.flags = fl;
952                }
953
954                if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
955                        + " content=" + mPendingContentInsets.toShortString()
956                        + " visible=" + mPendingVisibleInsets.toShortString()
957                        + " surface=" + mSurface);
958
959                if (mPendingConfiguration.seq != 0) {
960                    if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
961                            + mPendingConfiguration);
962                    updateConfiguration(mPendingConfiguration, !mFirst);
963                    mPendingConfiguration.seq = 0;
964                }
965
966                contentInsetsChanged = !mPendingContentInsets.equals(
967                        mAttachInfo.mContentInsets);
968                visibleInsetsChanged = !mPendingVisibleInsets.equals(
969                        mAttachInfo.mVisibleInsets);
970                if (contentInsetsChanged) {
971                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
972                    host.fitSystemWindows(mAttachInfo.mContentInsets);
973                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
974                            + mAttachInfo.mContentInsets);
975                }
976                if (visibleInsetsChanged) {
977                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
978                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
979                            + mAttachInfo.mVisibleInsets);
980                }
981
982                if (!hadSurface) {
983                    if (mSurface.isValid()) {
984                        // If we are creating a new surface, then we need to
985                        // completely redraw it.  Also, when we get to the
986                        // point of drawing it we will hold off and schedule
987                        // a new traversal instead.  This is so we can tell the
988                        // window manager about all of the windows being displayed
989                        // before actually drawing them, so it can display then
990                        // all at once.
991                        newSurface = true;
992                        fullRedrawNeeded = true;
993                        mPreviousTransparentRegion.setEmpty();
994
995                        if (mGlWanted && !mUseGL) {
996                            initializeGL();
997                            initialized = mGlCanvas != null;
998                        }
999                    }
1000                } else if (!mSurface.isValid()) {
1001                    // If the surface has been removed, then reset the scroll
1002                    // positions.
1003                    mLastScrolledFocus = null;
1004                    mScrollY = mCurScrollY = 0;
1005                    if (mScroller != null) {
1006                        mScroller.abortAnimation();
1007                    }
1008                }
1009            } catch (RemoteException e) {
1010            }
1011
1012            if (DEBUG_ORIENTATION) Log.v(
1013                    TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
1014
1015            attachInfo.mWindowLeft = frame.left;
1016            attachInfo.mWindowTop = frame.top;
1017
1018            // !!FIXME!! This next section handles the case where we did not get the
1019            // window size we asked for. We should avoid this by getting a maximum size from
1020            // the window session beforehand.
1021            mWidth = frame.width();
1022            mHeight = frame.height();
1023
1024            if (mSurfaceHolder != null) {
1025                // The app owns the surface; tell it about what is going on.
1026                if (mSurface.isValid()) {
1027                    // XXX .copyFrom() doesn't work!
1028                    //mSurfaceHolder.mSurface.copyFrom(mSurface);
1029                    mSurfaceHolder.mSurface = mSurface;
1030                }
1031                mSurfaceHolder.mSurfaceLock.unlock();
1032                if (mSurface.isValid()) {
1033                    if (!hadSurface) {
1034                        mSurfaceHolder.ungetCallbacks();
1035
1036                        mIsCreating = true;
1037                        mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
1038                        SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
1039                        if (callbacks != null) {
1040                            for (SurfaceHolder.Callback c : callbacks) {
1041                                c.surfaceCreated(mSurfaceHolder);
1042                            }
1043                        }
1044                        surfaceChanged = true;
1045                    }
1046                    if (surfaceChanged) {
1047                        mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
1048                                lp.format, mWidth, mHeight);
1049                        SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
1050                        if (callbacks != null) {
1051                            for (SurfaceHolder.Callback c : callbacks) {
1052                                c.surfaceChanged(mSurfaceHolder, lp.format,
1053                                        mWidth, mHeight);
1054                            }
1055                        }
1056                    }
1057                    mIsCreating = false;
1058                } else if (hadSurface) {
1059                    mSurfaceHolder.ungetCallbacks();
1060                    SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
1061                    mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
1062                    if (callbacks != null) {
1063                        for (SurfaceHolder.Callback c : callbacks) {
1064                            c.surfaceDestroyed(mSurfaceHolder);
1065                        }
1066                    }
1067                    mSurfaceHolder.mSurfaceLock.lock();
1068                    // Make surface invalid.
1069                    //mSurfaceHolder.mSurface.copyFrom(mSurface);
1070                    mSurfaceHolder.mSurface = new Surface();
1071                    mSurfaceHolder.mSurfaceLock.unlock();
1072                }
1073            }
1074
1075            if (initialized) {
1076                mGlCanvas.setViewport((int) (mWidth * appScale + 0.5f),
1077                        (int) (mHeight * appScale + 0.5f));
1078            }
1079
1080            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
1081                    (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);
1082            if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth
1083                    || mHeight != host.mMeasuredHeight || contentInsetsChanged) {
1084                childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
1085                childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
1086
1087                if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="
1088                        + mWidth + " measuredWidth=" + host.mMeasuredWidth
1089                        + " mHeight=" + mHeight
1090                        + " measuredHeight" + host.mMeasuredHeight
1091                        + " coveredInsetsChanged=" + contentInsetsChanged);
1092
1093                 // Ask host how big it wants to be
1094                host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
1095
1096                // Implementation of weights from WindowManager.LayoutParams
1097                // We just grow the dimensions as needed and re-measure if
1098                // needs be
1099                int width = host.mMeasuredWidth;
1100                int height = host.mMeasuredHeight;
1101                boolean measureAgain = false;
1102
1103                if (lp.horizontalWeight > 0.0f) {
1104                    width += (int) ((mWidth - width) * lp.horizontalWeight);
1105                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
1106                            MeasureSpec.EXACTLY);
1107                    measureAgain = true;
1108                }
1109                if (lp.verticalWeight > 0.0f) {
1110                    height += (int) ((mHeight - height) * lp.verticalWeight);
1111                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
1112                            MeasureSpec.EXACTLY);
1113                    measureAgain = true;
1114                }
1115
1116                if (measureAgain) {
1117                    if (DEBUG_LAYOUT) Log.v(TAG,
1118                            "And hey let's measure once more: width=" + width
1119                            + " height=" + height);
1120                    host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
1121                }
1122
1123                mLayoutRequested = true;
1124            }
1125        }
1126
1127        final boolean didLayout = mLayoutRequested;
1128        boolean triggerGlobalLayoutListener = didLayout
1129                || attachInfo.mRecomputeGlobalAttributes;
1130        if (didLayout) {
1131            mLayoutRequested = false;
1132            mScrollMayChange = true;
1133            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
1134                TAG, "Laying out " + host + " to (" +
1135                host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
1136            long startTime = 0L;
1137            if (Config.DEBUG && ViewDebug.profileLayout) {
1138                startTime = SystemClock.elapsedRealtime();
1139            }
1140            host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
1141
1142            if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
1143                if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
1144                    throw new IllegalStateException("The view hierarchy is an inconsistent state,"
1145                            + "please refer to the logs with the tag "
1146                            + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");
1147                }
1148            }
1149
1150            if (Config.DEBUG && ViewDebug.profileLayout) {
1151                EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
1152            }
1153
1154            // By this point all views have been sized and positionned
1155            // We can compute the transparent area
1156
1157            if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
1158                // start out transparent
1159                // TODO: AVOID THAT CALL BY CACHING THE RESULT?
1160                host.getLocationInWindow(mTmpLocation);
1161                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
1162                        mTmpLocation[0] + host.mRight - host.mLeft,
1163                        mTmpLocation[1] + host.mBottom - host.mTop);
1164
1165                host.gatherTransparentRegion(mTransparentRegion);
1166                if (mTranslator != null) {
1167                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
1168                }
1169
1170                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
1171                    mPreviousTransparentRegion.set(mTransparentRegion);
1172                    // reconfigure window manager
1173                    try {
1174                        sWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
1175                    } catch (RemoteException e) {
1176                    }
1177                }
1178            }
1179
1180            if (DBG) {
1181                System.out.println("======================================");
1182                System.out.println("performTraversals -- after setFrame");
1183                host.debug();
1184            }
1185        }
1186
1187        if (triggerGlobalLayoutListener) {
1188            attachInfo.mRecomputeGlobalAttributes = false;
1189            attachInfo.mTreeObserver.dispatchOnGlobalLayout();
1190        }
1191
1192        if (computesInternalInsets) {
1193            ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
1194            final Rect givenContent = attachInfo.mGivenInternalInsets.contentInsets;
1195            final Rect givenVisible = attachInfo.mGivenInternalInsets.visibleInsets;
1196            givenContent.left = givenContent.top = givenContent.right
1197                    = givenContent.bottom = givenVisible.left = givenVisible.top
1198                    = givenVisible.right = givenVisible.bottom = 0;
1199            attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
1200            Rect contentInsets = insets.contentInsets;
1201            Rect visibleInsets = insets.visibleInsets;
1202            if (mTranslator != null) {
1203                contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
1204                visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
1205            }
1206            if (insetsPending || !mLastGivenInsets.equals(insets)) {
1207                mLastGivenInsets.set(insets);
1208                try {
1209                    sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
1210                            contentInsets, visibleInsets);
1211                } catch (RemoteException e) {
1212                }
1213            }
1214        }
1215
1216        if (mFirst) {
1217            // handle first focus request
1218            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
1219                    + mView.hasFocus());
1220            if (mView != null) {
1221                if (!mView.hasFocus()) {
1222                    mView.requestFocus(View.FOCUS_FORWARD);
1223                    mFocusedView = mRealFocusedView = mView.findFocus();
1224                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
1225                            + mFocusedView);
1226                } else {
1227                    mRealFocusedView = mView.findFocus();
1228                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
1229                            + mRealFocusedView);
1230                }
1231            }
1232        }
1233
1234        mFirst = false;
1235        mWillDrawSoon = false;
1236        mNewSurfaceNeeded = false;
1237        mViewVisibility = viewVisibility;
1238
1239        if (mAttachInfo.mHasWindowFocus) {
1240            final boolean imTarget = WindowManager.LayoutParams
1241                    .mayUseInputMethod(mWindowAttributes.flags);
1242            if (imTarget != mLastWasImTarget) {
1243                mLastWasImTarget = imTarget;
1244                InputMethodManager imm = InputMethodManager.peekInstance();
1245                if (imm != null && imTarget) {
1246                    imm.startGettingWindowFocus(mView);
1247                    imm.onWindowFocus(mView, mView.findFocus(),
1248                            mWindowAttributes.softInputMode,
1249                            !mHasHadWindowFocus, mWindowAttributes.flags);
1250                }
1251            }
1252        }
1253
1254        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
1255
1256        if (!cancelDraw && !newSurface) {
1257            mFullRedrawNeeded = false;
1258            draw(fullRedrawNeeded);
1259
1260            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
1261                    || mReportNextDraw) {
1262                if (LOCAL_LOGV) {
1263                    Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
1264                }
1265                mReportNextDraw = false;
1266                if (mSurfaceHolder != null && mSurface.isValid()) {
1267                    mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
1268                    SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
1269                    if (callbacks != null) {
1270                        for (SurfaceHolder.Callback c : callbacks) {
1271                            if (c instanceof SurfaceHolder.Callback2) {
1272                                ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
1273                                        mSurfaceHolder);
1274                            }
1275                        }
1276                    }
1277                }
1278                try {
1279                    sWindowSession.finishDrawing(mWindow);
1280                } catch (RemoteException e) {
1281                }
1282            }
1283        } else {
1284            // We were supposed to report when we are done drawing. Since we canceled the
1285            // draw, remember it here.
1286            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
1287                mReportNextDraw = true;
1288            }
1289            if (fullRedrawNeeded) {
1290                mFullRedrawNeeded = true;
1291            }
1292            // Try again
1293            scheduleTraversals();
1294        }
1295    }
1296
1297    public void requestTransparentRegion(View child) {
1298        // the test below should not fail unless someone is messing with us
1299        checkThread();
1300        if (mView == child) {
1301            mView.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
1302            // Need to make sure we re-evaluate the window attributes next
1303            // time around, to ensure the window has the correct format.
1304            mWindowAttributesChanged = true;
1305            requestLayout();
1306        }
1307    }
1308
1309    /**
1310     * Figures out the measure spec for the root view in a window based on it's
1311     * layout params.
1312     *
1313     * @param windowSize
1314     *            The available width or height of the window
1315     *
1316     * @param rootDimension
1317     *            The layout params for one dimension (width or height) of the
1318     *            window.
1319     *
1320     * @return The measure spec to use to measure the root view.
1321     */
1322    private int getRootMeasureSpec(int windowSize, int rootDimension) {
1323        int measureSpec;
1324        switch (rootDimension) {
1325
1326        case ViewGroup.LayoutParams.MATCH_PARENT:
1327            // Window can't resize. Force root view to be windowSize.
1328            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
1329            break;
1330        case ViewGroup.LayoutParams.WRAP_CONTENT:
1331            // Window can resize. Set max size for root view.
1332            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
1333            break;
1334        default:
1335            // Window wants to be an exact size. Force root view to be that size.
1336            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
1337            break;
1338        }
1339        return measureSpec;
1340    }
1341
1342    private void draw(boolean fullRedrawNeeded) {
1343        Surface surface = mSurface;
1344        if (surface == null || !surface.isValid()) {
1345            return;
1346        }
1347
1348        if (!sFirstDrawComplete) {
1349            synchronized (sFirstDrawHandlers) {
1350                sFirstDrawComplete = true;
1351                for (int i=0; i<sFirstDrawHandlers.size(); i++) {
1352                    post(sFirstDrawHandlers.get(i));
1353                }
1354            }
1355        }
1356
1357        scrollToRectOrFocus(null, false);
1358
1359        if (mAttachInfo.mViewScrollChanged) {
1360            mAttachInfo.mViewScrollChanged = false;
1361            mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
1362        }
1363
1364        int yoff;
1365        final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
1366        if (scrolling) {
1367            yoff = mScroller.getCurrY();
1368        } else {
1369            yoff = mScrollY;
1370        }
1371        if (mCurScrollY != yoff) {
1372            mCurScrollY = yoff;
1373            fullRedrawNeeded = true;
1374        }
1375        float appScale = mAttachInfo.mApplicationScale;
1376        boolean scalingRequired = mAttachInfo.mScalingRequired;
1377
1378        Rect dirty = mDirty;
1379        if (mSurfaceHolder != null) {
1380            // The app owns the surface, we won't draw.
1381            dirty.setEmpty();
1382            return;
1383        }
1384
1385        if (mUseGL) {
1386            if (!dirty.isEmpty()) {
1387                Canvas canvas = mGlCanvas;
1388                if (mGL != null && canvas != null) {
1389                    mGL.glDisable(GL_SCISSOR_TEST);
1390                    mGL.glClearColor(0, 0, 0, 0);
1391                    mGL.glClear(GL_COLOR_BUFFER_BIT);
1392                    mGL.glEnable(GL_SCISSOR_TEST);
1393
1394                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
1395                    mAttachInfo.mIgnoreDirtyState = true;
1396                    mView.mPrivateFlags |= View.DRAWN;
1397
1398                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
1399                    try {
1400                        canvas.translate(0, -yoff);
1401                        if (mTranslator != null) {
1402                            mTranslator.translateCanvas(canvas);
1403                        }
1404                        canvas.setScreenDensity(scalingRequired
1405                                ? DisplayMetrics.DENSITY_DEVICE : 0);
1406                        mView.draw(canvas);
1407                        if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
1408                            mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
1409                        }
1410                    } finally {
1411                        canvas.restoreToCount(saveCount);
1412                    }
1413
1414                    mAttachInfo.mIgnoreDirtyState = false;
1415
1416                    mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
1417                    checkEglErrors();
1418
1419                    if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
1420                        int now = (int)SystemClock.elapsedRealtime();
1421                        if (sDrawTime != 0) {
1422                            nativeShowFPS(canvas, now - sDrawTime);
1423                        }
1424                        sDrawTime = now;
1425                    }
1426                }
1427            }
1428            if (scrolling) {
1429                mFullRedrawNeeded = true;
1430                scheduleTraversals();
1431            }
1432            return;
1433        }
1434
1435        if (fullRedrawNeeded) {
1436            mAttachInfo.mIgnoreDirtyState = true;
1437            dirty.union(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
1438        }
1439
1440        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
1441            Log.v(TAG, "Draw " + mView + "/"
1442                    + mWindowAttributes.getTitle()
1443                    + ": dirty={" + dirty.left + "," + dirty.top
1444                    + "," + dirty.right + "," + dirty.bottom + "} surface="
1445                    + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
1446                    appScale + ", width=" + mWidth + ", height=" + mHeight);
1447        }
1448
1449        if (!dirty.isEmpty() || mIsAnimating) {
1450            Canvas canvas;
1451            try {
1452                int left = dirty.left;
1453                int top = dirty.top;
1454                int right = dirty.right;
1455                int bottom = dirty.bottom;
1456                canvas = surface.lockCanvas(dirty);
1457
1458                if (left != dirty.left || top != dirty.top || right != dirty.right ||
1459                        bottom != dirty.bottom) {
1460                    mAttachInfo.mIgnoreDirtyState = true;
1461                }
1462
1463                // TODO: Do this in native
1464                canvas.setDensity(mDensity);
1465            } catch (Surface.OutOfResourcesException e) {
1466                Log.e(TAG, "OutOfResourcesException locking surface", e);
1467                // TODO: we should ask the window manager to do something!
1468                // for now we just do nothing
1469                return;
1470            } catch (IllegalArgumentException e) {
1471                Log.e(TAG, "IllegalArgumentException locking surface", e);
1472                // TODO: we should ask the window manager to do something!
1473                // for now we just do nothing
1474                return;
1475            }
1476
1477            try {
1478                if (!dirty.isEmpty() || mIsAnimating) {
1479                    long startTime = 0L;
1480
1481                    if (DEBUG_ORIENTATION || DEBUG_DRAW) {
1482                        Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
1483                                + canvas.getWidth() + ", h=" + canvas.getHeight());
1484                        //canvas.drawARGB(255, 255, 0, 0);
1485                    }
1486
1487                    if (Config.DEBUG && ViewDebug.profileDrawing) {
1488                        startTime = SystemClock.elapsedRealtime();
1489                    }
1490
1491                    // If this bitmap's format includes an alpha channel, we
1492                    // need to clear it before drawing so that the child will
1493                    // properly re-composite its drawing on a transparent
1494                    // background. This automatically respects the clip/dirty region
1495                    // or
1496                    // If we are applying an offset, we need to clear the area
1497                    // where the offset doesn't appear to avoid having garbage
1498                    // left in the blank areas.
1499                    if (!canvas.isOpaque() || yoff != 0) {
1500                        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
1501                    }
1502
1503                    dirty.setEmpty();
1504                    mIsAnimating = false;
1505                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
1506                    mView.mPrivateFlags |= View.DRAWN;
1507
1508                    if (DEBUG_DRAW) {
1509                        Context cxt = mView.getContext();
1510                        Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
1511                                ", metrics=" + cxt.getResources().getDisplayMetrics() +
1512                                ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
1513                    }
1514                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
1515                    try {
1516                        canvas.translate(0, -yoff);
1517                        if (mTranslator != null) {
1518                            mTranslator.translateCanvas(canvas);
1519                        }
1520                        canvas.setScreenDensity(scalingRequired
1521                                ? DisplayMetrics.DENSITY_DEVICE : 0);
1522                        mView.draw(canvas);
1523                    } finally {
1524                        mAttachInfo.mIgnoreDirtyState = false;
1525                        canvas.restoreToCount(saveCount);
1526                    }
1527
1528                    if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
1529                        mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
1530                    }
1531
1532                    if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
1533                        int now = (int)SystemClock.elapsedRealtime();
1534                        if (sDrawTime != 0) {
1535                            nativeShowFPS(canvas, now - sDrawTime);
1536                        }
1537                        sDrawTime = now;
1538                    }
1539
1540                    if (Config.DEBUG && ViewDebug.profileDrawing) {
1541                        EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
1542                    }
1543                }
1544
1545            } finally {
1546                surface.unlockCanvasAndPost(canvas);
1547            }
1548        }
1549
1550        if (LOCAL_LOGV) {
1551            Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
1552        }
1553
1554        if (scrolling) {
1555            mFullRedrawNeeded = true;
1556            scheduleTraversals();
1557        }
1558    }
1559
1560    boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
1561        final View.AttachInfo attachInfo = mAttachInfo;
1562        final Rect ci = attachInfo.mContentInsets;
1563        final Rect vi = attachInfo.mVisibleInsets;
1564        int scrollY = 0;
1565        boolean handled = false;
1566
1567        if (vi.left > ci.left || vi.top > ci.top
1568                || vi.right > ci.right || vi.bottom > ci.bottom) {
1569            // We'll assume that we aren't going to change the scroll
1570            // offset, since we want to avoid that unless it is actually
1571            // going to make the focus visible...  otherwise we scroll
1572            // all over the place.
1573            scrollY = mScrollY;
1574            // We can be called for two different situations: during a draw,
1575            // to update the scroll position if the focus has changed (in which
1576            // case 'rectangle' is null), or in response to a
1577            // requestChildRectangleOnScreen() call (in which case 'rectangle'
1578            // is non-null and we just want to scroll to whatever that
1579            // rectangle is).
1580            View focus = mRealFocusedView;
1581
1582            // When in touch mode, focus points to the previously focused view,
1583            // which may have been removed from the view hierarchy. The following
1584            // line checks whether the view is still in our hierarchy.
1585            if (focus == null || focus.mAttachInfo != mAttachInfo) {
1586                mRealFocusedView = null;
1587                return false;
1588            }
1589
1590            if (focus != mLastScrolledFocus) {
1591                // If the focus has changed, then ignore any requests to scroll
1592                // to a rectangle; first we want to make sure the entire focus
1593                // view is visible.
1594                rectangle = null;
1595            }
1596            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
1597                    + " rectangle=" + rectangle + " ci=" + ci
1598                    + " vi=" + vi);
1599            if (focus == mLastScrolledFocus && !mScrollMayChange
1600                    && rectangle == null) {
1601                // Optimization: if the focus hasn't changed since last
1602                // time, and no layout has happened, then just leave things
1603                // as they are.
1604                if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
1605                        + mScrollY + " vi=" + vi.toShortString());
1606            } else if (focus != null) {
1607                // We need to determine if the currently focused view is
1608                // within the visible part of the window and, if not, apply
1609                // a pan so it can be seen.
1610                mLastScrolledFocus = focus;
1611                mScrollMayChange = false;
1612                if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
1613                // Try to find the rectangle from the focus view.
1614                if (focus.getGlobalVisibleRect(mVisRect, null)) {
1615                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w="
1616                            + mView.getWidth() + " h=" + mView.getHeight()
1617                            + " ci=" + ci.toShortString()
1618                            + " vi=" + vi.toShortString());
1619                    if (rectangle == null) {
1620                        focus.getFocusedRect(mTempRect);
1621                        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
1622                                + ": focusRect=" + mTempRect.toShortString());
1623                        if (mView instanceof ViewGroup) {
1624                            ((ViewGroup) mView).offsetDescendantRectToMyCoords(
1625                                    focus, mTempRect);
1626                        }
1627                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1628                                "Focus in window: focusRect="
1629                                + mTempRect.toShortString()
1630                                + " visRect=" + mVisRect.toShortString());
1631                    } else {
1632                        mTempRect.set(rectangle);
1633                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1634                                "Request scroll to rect: "
1635                                + mTempRect.toShortString()
1636                                + " visRect=" + mVisRect.toShortString());
1637                    }
1638                    if (mTempRect.intersect(mVisRect)) {
1639                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1640                                "Focus window visible rect: "
1641                                + mTempRect.toShortString());
1642                        if (mTempRect.height() >
1643                                (mView.getHeight()-vi.top-vi.bottom)) {
1644                            // If the focus simply is not going to fit, then
1645                            // best is probably just to leave things as-is.
1646                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1647                                    "Too tall; leaving scrollY=" + scrollY);
1648                        } else if ((mTempRect.top-scrollY) < vi.top) {
1649                            scrollY -= vi.top - (mTempRect.top-scrollY);
1650                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1651                                    "Top covered; scrollY=" + scrollY);
1652                        } else if ((mTempRect.bottom-scrollY)
1653                                > (mView.getHeight()-vi.bottom)) {
1654                            scrollY += (mTempRect.bottom-scrollY)
1655                                    - (mView.getHeight()-vi.bottom);
1656                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
1657                                    "Bottom covered; scrollY=" + scrollY);
1658                        }
1659                        handled = true;
1660                    }
1661                }
1662            }
1663        }
1664
1665        if (scrollY != mScrollY) {
1666            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
1667                    + mScrollY + " , new=" + scrollY);
1668            if (!immediate) {
1669                if (mScroller == null) {
1670                    mScroller = new Scroller(mView.getContext());
1671                }
1672                mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);
1673            } else if (mScroller != null) {
1674                mScroller.abortAnimation();
1675            }
1676            mScrollY = scrollY;
1677        }
1678
1679        return handled;
1680    }
1681
1682    public void requestChildFocus(View child, View focused) {
1683        checkThread();
1684        if (mFocusedView != focused) {
1685            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
1686            scheduleTraversals();
1687        }
1688        mFocusedView = mRealFocusedView = focused;
1689        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
1690                + mFocusedView);
1691    }
1692
1693    public void clearChildFocus(View child) {
1694        checkThread();
1695
1696        View oldFocus = mFocusedView;
1697
1698        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus");
1699        mFocusedView = mRealFocusedView = null;
1700        if (mView != null && !mView.hasFocus()) {
1701            // If a view gets the focus, the listener will be invoked from requestChildFocus()
1702            if (!mView.requestFocus(View.FOCUS_FORWARD)) {
1703                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
1704            }
1705        } else if (oldFocus != null) {
1706            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
1707        }
1708    }
1709
1710
1711    public void focusableViewAvailable(View v) {
1712        checkThread();
1713
1714        if (mView != null && !mView.hasFocus()) {
1715            v.requestFocus();
1716        } else {
1717            // the one case where will transfer focus away from the current one
1718            // is if the current view is a view group that prefers to give focus
1719            // to its children first AND the view is a descendant of it.
1720            mFocusedView = mView.findFocus();
1721            boolean descendantsHaveDibsOnFocus =
1722                    (mFocusedView instanceof ViewGroup) &&
1723                        (((ViewGroup) mFocusedView).getDescendantFocusability() ==
1724                                ViewGroup.FOCUS_AFTER_DESCENDANTS);
1725            if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
1726                // If a view gets the focus, the listener will be invoked from requestChildFocus()
1727                v.requestFocus();
1728            }
1729        }
1730    }
1731
1732    public void recomputeViewAttributes(View child) {
1733        checkThread();
1734        if (mView == child) {
1735            mAttachInfo.mRecomputeGlobalAttributes = true;
1736            if (!mWillDrawSoon) {
1737                scheduleTraversals();
1738            }
1739        }
1740    }
1741
1742    void dispatchDetachedFromWindow() {
1743        if (Config.LOGV) Log.v(TAG, "Detaching in " + this + " of " + mSurface);
1744
1745        if (mView != null) {
1746            mView.dispatchDetachedFromWindow();
1747        }
1748
1749        mView = null;
1750        mAttachInfo.mRootView = null;
1751        mAttachInfo.mSurface = null;
1752
1753        if (mUseGL) {
1754            destroyGL();
1755        }
1756        mSurface.release();
1757
1758        if (mInputChannel != null) {
1759            if (mInputQueueCallback != null) {
1760                mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
1761                mInputQueueCallback = null;
1762            } else {
1763                InputQueue.unregisterInputChannel(mInputChannel);
1764            }
1765        }
1766
1767        try {
1768            sWindowSession.remove(mWindow);
1769        } catch (RemoteException e) {
1770        }
1771
1772        // Dispose the input channel after removing the window so the Window Manager
1773        // doesn't interpret the input channel being closed as an abnormal termination.
1774        if (mInputChannel != null) {
1775            mInputChannel.dispose();
1776            mInputChannel = null;
1777        }
1778    }
1779
1780    void updateConfiguration(Configuration config, boolean force) {
1781        if (DEBUG_CONFIGURATION) Log.v(TAG,
1782                "Applying new config to window "
1783                + mWindowAttributes.getTitle()
1784                + ": " + config);
1785        synchronized (sConfigCallbacks) {
1786            for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
1787                sConfigCallbacks.get(i).onConfigurationChanged(config);
1788            }
1789        }
1790        if (mView != null) {
1791            // At this point the resources have been updated to
1792            // have the most recent config, whatever that is.  Use
1793            // the on in them which may be newer.
1794            if (mView != null) {
1795                config = mView.getResources().getConfiguration();
1796            }
1797            if (force || mLastConfiguration.diff(config) != 0) {
1798                mLastConfiguration.setTo(config);
1799                mView.dispatchConfigurationChanged(config);
1800            }
1801        }
1802    }
1803
1804    /**
1805     * Return true if child is an ancestor of parent, (or equal to the parent).
1806     */
1807    private static boolean isViewDescendantOf(View child, View parent) {
1808        if (child == parent) {
1809            return true;
1810        }
1811
1812        final ViewParent theParent = child.getParent();
1813        return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
1814    }
1815
1816    private static void forceLayout(View view) {
1817        view.forceLayout();
1818        if (view instanceof ViewGroup) {
1819            ViewGroup group = (ViewGroup) view;
1820            final int count = group.getChildCount();
1821            for (int i = 0; i < count; i++) {
1822                forceLayout(group.getChildAt(i));
1823            }
1824        }
1825    }
1826
1827    public final static int DO_TRAVERSAL = 1000;
1828    public final static int DIE = 1001;
1829    public final static int RESIZED = 1002;
1830    public final static int RESIZED_REPORT = 1003;
1831    public final static int WINDOW_FOCUS_CHANGED = 1004;
1832    public final static int DISPATCH_KEY = 1005;
1833    public final static int DISPATCH_POINTER = 1006;
1834    public final static int DISPATCH_TRACKBALL = 1007;
1835    public final static int DISPATCH_APP_VISIBILITY = 1008;
1836    public final static int DISPATCH_GET_NEW_SURFACE = 1009;
1837    public final static int FINISHED_EVENT = 1010;
1838    public final static int DISPATCH_KEY_FROM_IME = 1011;
1839    public final static int FINISH_INPUT_CONNECTION = 1012;
1840    public final static int CHECK_FOCUS = 1013;
1841    public final static int CLOSE_SYSTEM_DIALOGS = 1014;
1842
1843    @Override
1844    public void handleMessage(Message msg) {
1845        switch (msg.what) {
1846        case View.AttachInfo.INVALIDATE_MSG:
1847            ((View) msg.obj).invalidate();
1848            break;
1849        case View.AttachInfo.INVALIDATE_RECT_MSG:
1850            final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
1851            info.target.invalidate(info.left, info.top, info.right, info.bottom);
1852            info.release();
1853            break;
1854        case DO_TRAVERSAL:
1855            if (mProfile) {
1856                Debug.startMethodTracing("ViewRoot");
1857            }
1858
1859            performTraversals();
1860
1861            if (mProfile) {
1862                Debug.stopMethodTracing();
1863                mProfile = false;
1864            }
1865            break;
1866        case FINISHED_EVENT:
1867            handleFinishedEvent(msg.arg1, msg.arg2 != 0);
1868            break;
1869        case DISPATCH_KEY:
1870            if (LOCAL_LOGV) Log.v(
1871                TAG, "Dispatching key "
1872                + msg.obj + " to " + mView);
1873            deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
1874            break;
1875        case DISPATCH_POINTER: {
1876            MotionEvent event = (MotionEvent) msg.obj;
1877            try {
1878                deliverPointerEvent(event);
1879            } finally {
1880                event.recycle();
1881                if (msg.arg1 != 0) {
1882                    finishInputEvent();
1883                }
1884                if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
1885            }
1886        } break;
1887        case DISPATCH_TRACKBALL: {
1888            MotionEvent event = (MotionEvent) msg.obj;
1889            try {
1890                deliverTrackballEvent(event);
1891            } finally {
1892                event.recycle();
1893                if (msg.arg1 != 0) {
1894                    finishInputEvent();
1895                }
1896            }
1897        } break;
1898        case DISPATCH_APP_VISIBILITY:
1899            handleAppVisibility(msg.arg1 != 0);
1900            break;
1901        case DISPATCH_GET_NEW_SURFACE:
1902            handleGetNewSurface();
1903            break;
1904        case RESIZED:
1905            ResizedInfo ri = (ResizedInfo)msg.obj;
1906
1907            if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
1908                    && mPendingContentInsets.equals(ri.coveredInsets)
1909                    && mPendingVisibleInsets.equals(ri.visibleInsets)
1910                    && ((ResizedInfo)msg.obj).newConfig == null) {
1911                break;
1912            }
1913            // fall through...
1914        case RESIZED_REPORT:
1915            if (mAdded) {
1916                Configuration config = ((ResizedInfo)msg.obj).newConfig;
1917                if (config != null) {
1918                    updateConfiguration(config, false);
1919                }
1920                mWinFrame.left = 0;
1921                mWinFrame.right = msg.arg1;
1922                mWinFrame.top = 0;
1923                mWinFrame.bottom = msg.arg2;
1924                mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets);
1925                mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets);
1926                if (msg.what == RESIZED_REPORT) {
1927                    mReportNextDraw = true;
1928                }
1929
1930                if (mView != null) {
1931                    forceLayout(mView);
1932                }
1933                requestLayout();
1934            }
1935            break;
1936        case WINDOW_FOCUS_CHANGED: {
1937            if (mAdded) {
1938                boolean hasWindowFocus = msg.arg1 != 0;
1939                mAttachInfo.mHasWindowFocus = hasWindowFocus;
1940                if (hasWindowFocus) {
1941                    boolean inTouchMode = msg.arg2 != 0;
1942                    ensureTouchModeLocally(inTouchMode);
1943
1944                    if (mGlWanted) {
1945                        checkEglErrors();
1946                        // we lost the gl context, so recreate it.
1947                        if (mGlWanted && !mUseGL) {
1948                            initializeGL();
1949                            if (mGlCanvas != null) {
1950                                float appScale = mAttachInfo.mApplicationScale;
1951                                mGlCanvas.setViewport(
1952                                        (int) (mWidth * appScale + 0.5f),
1953                                        (int) (mHeight * appScale + 0.5f));
1954                            }
1955                        }
1956                    }
1957                }
1958
1959                mLastWasImTarget = WindowManager.LayoutParams
1960                        .mayUseInputMethod(mWindowAttributes.flags);
1961
1962                InputMethodManager imm = InputMethodManager.peekInstance();
1963                if (mView != null) {
1964                    if (hasWindowFocus && imm != null && mLastWasImTarget) {
1965                        imm.startGettingWindowFocus(mView);
1966                    }
1967                    mAttachInfo.mKeyDispatchState.reset();
1968                    mView.dispatchWindowFocusChanged(hasWindowFocus);
1969                }
1970
1971                // Note: must be done after the focus change callbacks,
1972                // so all of the view state is set up correctly.
1973                if (hasWindowFocus) {
1974                    if (imm != null && mLastWasImTarget) {
1975                        imm.onWindowFocus(mView, mView.findFocus(),
1976                                mWindowAttributes.softInputMode,
1977                                !mHasHadWindowFocus, mWindowAttributes.flags);
1978                    }
1979                    // Clear the forward bit.  We can just do this directly, since
1980                    // the window manager doesn't care about it.
1981                    mWindowAttributes.softInputMode &=
1982                            ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
1983                    ((WindowManager.LayoutParams)mView.getLayoutParams())
1984                            .softInputMode &=
1985                                ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
1986                    mHasHadWindowFocus = true;
1987                }
1988
1989                if (hasWindowFocus && mView != null) {
1990                    sendAccessibilityEvents();
1991                }
1992            }
1993        } break;
1994        case DIE:
1995            doDie();
1996            break;
1997        case DISPATCH_KEY_FROM_IME: {
1998            if (LOCAL_LOGV) Log.v(
1999                TAG, "Dispatching key "
2000                + msg.obj + " from IME to " + mView);
2001            KeyEvent event = (KeyEvent)msg.obj;
2002            if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
2003                // The IME is trying to say this event is from the
2004                // system!  Bad bad bad!
2005                event = KeyEvent.changeFlags(event,
2006                        event.getFlags()&~KeyEvent.FLAG_FROM_SYSTEM);
2007            }
2008            deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
2009        } break;
2010        case FINISH_INPUT_CONNECTION: {
2011            InputMethodManager imm = InputMethodManager.peekInstance();
2012            if (imm != null) {
2013                imm.reportFinishInputConnection((InputConnection)msg.obj);
2014            }
2015        } break;
2016        case CHECK_FOCUS: {
2017            InputMethodManager imm = InputMethodManager.peekInstance();
2018            if (imm != null) {
2019                imm.checkFocus();
2020            }
2021        } break;
2022        case CLOSE_SYSTEM_DIALOGS: {
2023            if (mView != null) {
2024                mView.onCloseSystemDialogs((String)msg.obj);
2025            }
2026        } break;
2027        }
2028    }
2029
2030    private void startInputEvent(Runnable finishedCallback) {
2031        if (mFinishedCallback != null) {
2032            Slog.w(TAG, "Received a new input event from the input queue but there is "
2033                    + "already an unfinished input event in progress.");
2034        }
2035
2036        mFinishedCallback = finishedCallback;
2037    }
2038
2039    private void finishInputEvent() {
2040        if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
2041
2042        if (mFinishedCallback != null) {
2043            mFinishedCallback.run();
2044            mFinishedCallback = null;
2045        } else {
2046            Slog.w(TAG, "Attempted to tell the input queue that the current input event "
2047                    + "is finished but there is no input event actually in progress.");
2048        }
2049    }
2050
2051    /**
2052     * Something in the current window tells us we need to change the touch mode.  For
2053     * example, we are not in touch mode, and the user touches the screen.
2054     *
2055     * If the touch mode has changed, tell the window manager, and handle it locally.
2056     *
2057     * @param inTouchMode Whether we want to be in touch mode.
2058     * @return True if the touch mode changed and focus changed was changed as a result
2059     */
2060    boolean ensureTouchMode(boolean inTouchMode) {
2061        if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
2062                + "touch mode is " + mAttachInfo.mInTouchMode);
2063        if (mAttachInfo.mInTouchMode == inTouchMode) return false;
2064
2065        // tell the window manager
2066        try {
2067            sWindowSession.setInTouchMode(inTouchMode);
2068        } catch (RemoteException e) {
2069            throw new RuntimeException(e);
2070        }
2071
2072        // handle the change
2073        return ensureTouchModeLocally(inTouchMode);
2074    }
2075
2076    /**
2077     * Ensure that the touch mode for this window is set, and if it is changing,
2078     * take the appropriate action.
2079     * @param inTouchMode Whether we want to be in touch mode.
2080     * @return True if the touch mode changed and focus changed was changed as a result
2081     */
2082    private boolean ensureTouchModeLocally(boolean inTouchMode) {
2083        if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current "
2084                + "touch mode is " + mAttachInfo.mInTouchMode);
2085
2086        if (mAttachInfo.mInTouchMode == inTouchMode) return false;
2087
2088        mAttachInfo.mInTouchMode = inTouchMode;
2089        mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);
2090
2091        return (inTouchMode) ? enterTouchMode() : leaveTouchMode();
2092    }
2093
2094    private boolean enterTouchMode() {
2095        if (mView != null) {
2096            if (mView.hasFocus()) {
2097                // note: not relying on mFocusedView here because this could
2098                // be when the window is first being added, and mFocused isn't
2099                // set yet.
2100                final View focused = mView.findFocus();
2101                if (focused != null && !focused.isFocusableInTouchMode()) {
2102
2103                    final ViewGroup ancestorToTakeFocus =
2104                            findAncestorToTakeFocusInTouchMode(focused);
2105                    if (ancestorToTakeFocus != null) {
2106                        // there is an ancestor that wants focus after its descendants that
2107                        // is focusable in touch mode.. give it focus
2108                        return ancestorToTakeFocus.requestFocus();
2109                    } else {
2110                        // nothing appropriate to have focus in touch mode, clear it out
2111                        mView.unFocus();
2112                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
2113                        mFocusedView = null;
2114                        return true;
2115                    }
2116                }
2117            }
2118        }
2119        return false;
2120    }
2121
2122
2123    /**
2124     * Find an ancestor of focused that wants focus after its descendants and is
2125     * focusable in touch mode.
2126     * @param focused The currently focused view.
2127     * @return An appropriate view, or null if no such view exists.
2128     */
2129    private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
2130        ViewParent parent = focused.getParent();
2131        while (parent instanceof ViewGroup) {
2132            final ViewGroup vgParent = (ViewGroup) parent;
2133            if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
2134                    && vgParent.isFocusableInTouchMode()) {
2135                return vgParent;
2136            }
2137            if (vgParent.isRootNamespace()) {
2138                return null;
2139            } else {
2140                parent = vgParent.getParent();
2141            }
2142        }
2143        return null;
2144    }
2145
2146    private boolean leaveTouchMode() {
2147        if (mView != null) {
2148            if (mView.hasFocus()) {
2149                // i learned the hard way to not trust mFocusedView :)
2150                mFocusedView = mView.findFocus();
2151                if (!(mFocusedView instanceof ViewGroup)) {
2152                    // some view has focus, let it keep it
2153                    return false;
2154                } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
2155                        ViewGroup.FOCUS_AFTER_DESCENDANTS) {
2156                    // some view group has focus, and doesn't prefer its children
2157                    // over itself for focus, so let them keep it.
2158                    return false;
2159                }
2160            }
2161
2162            // find the best view to give focus to in this brave new non-touch-mode
2163            // world
2164            final View focused = focusSearch(null, View.FOCUS_DOWN);
2165            if (focused != null) {
2166                return focused.requestFocus(View.FOCUS_DOWN);
2167            }
2168        }
2169        return false;
2170    }
2171
2172    private void deliverPointerEvent(MotionEvent event) {
2173        if (mTranslator != null) {
2174            mTranslator.translateEventInScreenToAppWindow(event);
2175        }
2176
2177        boolean handled;
2178        if (mView != null && mAdded) {
2179
2180            // enter touch mode on the down
2181            boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
2182            if (isDown) {
2183                ensureTouchMode(true);
2184            }
2185            if(Config.LOGV) {
2186                captureMotionLog("captureDispatchPointer", event);
2187            }
2188            if (mCurScrollY != 0) {
2189                event.offsetLocation(0, mCurScrollY);
2190            }
2191            if (MEASURE_LATENCY) {
2192                lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
2193            }
2194            handled = mView.dispatchTouchEvent(event);
2195            if (MEASURE_LATENCY) {
2196                lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
2197            }
2198            if (!handled && isDown) {
2199                int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
2200
2201                final int edgeFlags = event.getEdgeFlags();
2202                int direction = View.FOCUS_UP;
2203                int x = (int)event.getX();
2204                int y = (int)event.getY();
2205                final int[] deltas = new int[2];
2206
2207                if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
2208                    direction = View.FOCUS_DOWN;
2209                    if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
2210                        deltas[0] = edgeSlop;
2211                        x += edgeSlop;
2212                    } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
2213                        deltas[0] = -edgeSlop;
2214                        x -= edgeSlop;
2215                    }
2216                } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
2217                    direction = View.FOCUS_UP;
2218                    if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
2219                        deltas[0] = edgeSlop;
2220                        x += edgeSlop;
2221                    } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
2222                        deltas[0] = -edgeSlop;
2223                        x -= edgeSlop;
2224                    }
2225                } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
2226                    direction = View.FOCUS_RIGHT;
2227                } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
2228                    direction = View.FOCUS_LEFT;
2229                }
2230
2231                if (edgeFlags != 0 && mView instanceof ViewGroup) {
2232                    View nearest = FocusFinder.getInstance().findNearestTouchable(
2233                            ((ViewGroup) mView), x, y, direction, deltas);
2234                    if (nearest != null) {
2235                        event.offsetLocation(deltas[0], deltas[1]);
2236                        event.setEdgeFlags(0);
2237                        mView.dispatchTouchEvent(event);
2238                    }
2239                }
2240            }
2241        }
2242    }
2243
2244    private void deliverTrackballEvent(MotionEvent event) {
2245        if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
2246
2247        boolean handled = false;
2248        if (mView != null && mAdded) {
2249            handled = mView.dispatchTrackballEvent(event);
2250            if (handled) {
2251                // If we reach this, we delivered a trackball event to mView and
2252                // mView consumed it. Because we will not translate the trackball
2253                // event into a key event, touch mode will not exit, so we exit
2254                // touch mode here.
2255                ensureTouchMode(false);
2256                return;
2257            }
2258
2259            // Otherwise we could do something here, like changing the focus
2260            // or something?
2261        }
2262
2263        final TrackballAxis x = mTrackballAxisX;
2264        final TrackballAxis y = mTrackballAxisY;
2265
2266        long curTime = SystemClock.uptimeMillis();
2267        if ((mLastTrackballTime+MAX_TRACKBALL_DELAY) < curTime) {
2268            // It has been too long since the last movement,
2269            // so restart at the beginning.
2270            x.reset(0);
2271            y.reset(0);
2272            mLastTrackballTime = curTime;
2273        }
2274
2275        final int action = event.getAction();
2276        final int metastate = event.getMetaState();
2277        switch (action) {
2278            case MotionEvent.ACTION_DOWN:
2279                x.reset(2);
2280                y.reset(2);
2281                deliverKeyEvent(new KeyEvent(curTime, curTime,
2282                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER,
2283                        0, metastate), false);
2284                break;
2285            case MotionEvent.ACTION_UP:
2286                x.reset(2);
2287                y.reset(2);
2288                deliverKeyEvent(new KeyEvent(curTime, curTime,
2289                        KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER,
2290                        0, metastate), false);
2291                break;
2292        }
2293
2294        if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
2295                + x.step + " dir=" + x.dir + " acc=" + x.acceleration
2296                + " move=" + event.getX()
2297                + " / Y=" + y.position + " step="
2298                + y.step + " dir=" + y.dir + " acc=" + y.acceleration
2299                + " move=" + event.getY());
2300        final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
2301        final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
2302
2303        // Generate DPAD events based on the trackball movement.
2304        // We pick the axis that has moved the most as the direction of
2305        // the DPAD.  When we generate DPAD events for one axis, then the
2306        // other axis is reset -- we don't want to perform DPAD jumps due
2307        // to slight movements in the trackball when making major movements
2308        // along the other axis.
2309        int keycode = 0;
2310        int movement = 0;
2311        float accel = 1;
2312        if (xOff > yOff) {
2313            movement = x.generate((2/event.getXPrecision()));
2314            if (movement != 0) {
2315                keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
2316                        : KeyEvent.KEYCODE_DPAD_LEFT;
2317                accel = x.acceleration;
2318                y.reset(2);
2319            }
2320        } else if (yOff > 0) {
2321            movement = y.generate((2/event.getYPrecision()));
2322            if (movement != 0) {
2323                keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
2324                        : KeyEvent.KEYCODE_DPAD_UP;
2325                accel = y.acceleration;
2326                x.reset(2);
2327            }
2328        }
2329
2330        if (keycode != 0) {
2331            if (movement < 0) movement = -movement;
2332            int accelMovement = (int)(movement * accel);
2333            if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
2334                    + " accelMovement=" + accelMovement
2335                    + " accel=" + accel);
2336            if (accelMovement > movement) {
2337                if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
2338                        + keycode);
2339                movement--;
2340                deliverKeyEvent(new KeyEvent(curTime, curTime,
2341                        KeyEvent.ACTION_MULTIPLE, keycode,
2342                        accelMovement-movement, metastate), false);
2343            }
2344            while (movement > 0) {
2345                if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
2346                        + keycode);
2347                movement--;
2348                curTime = SystemClock.uptimeMillis();
2349                deliverKeyEvent(new KeyEvent(curTime, curTime,
2350                        KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false);
2351                deliverKeyEvent(new KeyEvent(curTime, curTime,
2352                        KeyEvent.ACTION_UP, keycode, 0, metastate), false);
2353            }
2354            mLastTrackballTime = curTime;
2355        }
2356    }
2357
2358    /**
2359     * @param keyCode The key code
2360     * @return True if the key is directional.
2361     */
2362    static boolean isDirectional(int keyCode) {
2363        switch (keyCode) {
2364        case KeyEvent.KEYCODE_DPAD_LEFT:
2365        case KeyEvent.KEYCODE_DPAD_RIGHT:
2366        case KeyEvent.KEYCODE_DPAD_UP:
2367        case KeyEvent.KEYCODE_DPAD_DOWN:
2368            return true;
2369        }
2370        return false;
2371    }
2372
2373    /**
2374     * Returns true if this key is a keyboard key.
2375     * @param keyEvent The key event.
2376     * @return whether this key is a keyboard key.
2377     */
2378    private static boolean isKeyboardKey(KeyEvent keyEvent) {
2379      final int convertedKey = keyEvent.getUnicodeChar();
2380        return convertedKey > 0;
2381    }
2382
2383
2384
2385    /**
2386     * See if the key event means we should leave touch mode (and leave touch
2387     * mode if so).
2388     * @param event The key event.
2389     * @return Whether this key event should be consumed (meaning the act of
2390     *   leaving touch mode alone is considered the event).
2391     */
2392    private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
2393        final int action = event.getAction();
2394        if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) {
2395            return false;
2396        }
2397        if ((event.getFlags()&KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
2398            return false;
2399        }
2400
2401        // only relevant if we are in touch mode
2402        if (!mAttachInfo.mInTouchMode) {
2403            return false;
2404        }
2405
2406        // if something like an edit text has focus and the user is typing,
2407        // leave touch mode
2408        //
2409        // note: the condition of not being a keyboard key is kind of a hacky
2410        // approximation of whether we think the focused view will want the
2411        // key; if we knew for sure whether the focused view would consume
2412        // the event, that would be better.
2413        if (isKeyboardKey(event) && mView != null && mView.hasFocus()) {
2414            mFocusedView = mView.findFocus();
2415            if ((mFocusedView instanceof ViewGroup)
2416                    && ((ViewGroup) mFocusedView).getDescendantFocusability() ==
2417                    ViewGroup.FOCUS_AFTER_DESCENDANTS) {
2418                // something has focus, but is holding it weakly as a container
2419                return false;
2420            }
2421            if (ensureTouchMode(false)) {
2422                throw new IllegalStateException("should not have changed focus "
2423                        + "when leaving touch mode while a view has focus.");
2424            }
2425            return false;
2426        }
2427
2428        if (isDirectional(event.getKeyCode())) {
2429            // no view has focus, so we leave touch mode (and find something
2430            // to give focus to).  the event is consumed if we were able to
2431            // find something to give focus to.
2432            return ensureTouchMode(false);
2433        }
2434        return false;
2435    }
2436
2437    /**
2438     * log motion events
2439     */
2440    private static void captureMotionLog(String subTag, MotionEvent ev) {
2441        //check dynamic switch
2442        if (ev == null ||
2443                SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
2444            return;
2445        }
2446
2447        StringBuilder sb = new StringBuilder(subTag + ": ");
2448        sb.append(ev.getDownTime()).append(',');
2449        sb.append(ev.getEventTime()).append(',');
2450        sb.append(ev.getAction()).append(',');
2451        sb.append(ev.getX()).append(',');
2452        sb.append(ev.getY()).append(',');
2453        sb.append(ev.getPressure()).append(',');
2454        sb.append(ev.getSize()).append(',');
2455        sb.append(ev.getMetaState()).append(',');
2456        sb.append(ev.getXPrecision()).append(',');
2457        sb.append(ev.getYPrecision()).append(',');
2458        sb.append(ev.getDeviceId()).append(',');
2459        sb.append(ev.getEdgeFlags());
2460        Log.d(TAG, sb.toString());
2461    }
2462    /**
2463     * log motion events
2464     */
2465    private static void captureKeyLog(String subTag, KeyEvent ev) {
2466        //check dynamic switch
2467        if (ev == null ||
2468                SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
2469            return;
2470        }
2471        StringBuilder sb = new StringBuilder(subTag + ": ");
2472        sb.append(ev.getDownTime()).append(',');
2473        sb.append(ev.getEventTime()).append(',');
2474        sb.append(ev.getAction()).append(',');
2475        sb.append(ev.getKeyCode()).append(',');
2476        sb.append(ev.getRepeatCount()).append(',');
2477        sb.append(ev.getMetaState()).append(',');
2478        sb.append(ev.getDeviceId()).append(',');
2479        sb.append(ev.getScanCode());
2480        Log.d(TAG, sb.toString());
2481    }
2482
2483    int enqueuePendingEvent(Object event, boolean sendDone) {
2484        int seq = mPendingEventSeq+1;
2485        if (seq < 0) seq = 0;
2486        mPendingEventSeq = seq;
2487        mPendingEvents.put(seq, event);
2488        return sendDone ? seq : -seq;
2489    }
2490
2491    Object retrievePendingEvent(int seq) {
2492        if (seq < 0) seq = -seq;
2493        Object event = mPendingEvents.get(seq);
2494        if (event != null) {
2495            mPendingEvents.remove(seq);
2496        }
2497        return event;
2498    }
2499
2500    private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
2501        // If mView is null, we just consume the key event because it doesn't
2502        // make sense to do anything else with it.
2503        boolean handled = mView != null
2504                ? mView.dispatchKeyEventPreIme(event) : true;
2505        if (handled) {
2506            if (sendDone) {
2507                finishInputEvent();
2508            }
2509            return;
2510        }
2511        // If it is possible for this window to interact with the input
2512        // method window, then we want to first dispatch our key events
2513        // to the input method.
2514        if (mLastWasImTarget) {
2515            InputMethodManager imm = InputMethodManager.peekInstance();
2516            if (imm != null && mView != null) {
2517                int seq = enqueuePendingEvent(event, sendDone);
2518                if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
2519                        + seq + " event=" + event);
2520                imm.dispatchKeyEvent(mView.getContext(), seq, event,
2521                        mInputMethodCallback);
2522                return;
2523            }
2524        }
2525        deliverKeyEventToViewHierarchy(event, sendDone);
2526    }
2527
2528    void handleFinishedEvent(int seq, boolean handled) {
2529        final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
2530        if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq
2531                + " handled=" + handled + " event=" + event);
2532        if (event != null) {
2533            final boolean sendDone = seq >= 0;
2534            if (!handled) {
2535                deliverKeyEventToViewHierarchy(event, sendDone);
2536                return;
2537            } else if (sendDone) {
2538                finishInputEvent();
2539            } else {
2540                Log.w(TAG, "handleFinishedEvent(seq=" + seq
2541                        + " handled=" + handled + " ev=" + event
2542                        + ") neither delivering nor finishing key");
2543            }
2544        }
2545    }
2546
2547    private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {
2548        try {
2549            if (mView != null && mAdded) {
2550                final int action = event.getAction();
2551                boolean isDown = (action == KeyEvent.ACTION_DOWN);
2552
2553                if (checkForLeavingTouchModeAndConsume(event)) {
2554                    return;
2555                }
2556
2557                if (Config.LOGV) {
2558                    captureKeyLog("captureDispatchKeyEvent", event);
2559                }
2560                boolean keyHandled = mView.dispatchKeyEvent(event);
2561
2562                if (!keyHandled && isDown) {
2563                    int direction = 0;
2564                    switch (event.getKeyCode()) {
2565                    case KeyEvent.KEYCODE_DPAD_LEFT:
2566                        direction = View.FOCUS_LEFT;
2567                        break;
2568                    case KeyEvent.KEYCODE_DPAD_RIGHT:
2569                        direction = View.FOCUS_RIGHT;
2570                        break;
2571                    case KeyEvent.KEYCODE_DPAD_UP:
2572                        direction = View.FOCUS_UP;
2573                        break;
2574                    case KeyEvent.KEYCODE_DPAD_DOWN:
2575                        direction = View.FOCUS_DOWN;
2576                        break;
2577                    }
2578
2579                    if (direction != 0) {
2580
2581                        View focused = mView != null ? mView.findFocus() : null;
2582                        if (focused != null) {
2583                            View v = focused.focusSearch(direction);
2584                            boolean focusPassed = false;
2585                            if (v != null && v != focused) {
2586                                // do the math the get the interesting rect
2587                                // of previous focused into the coord system of
2588                                // newly focused view
2589                                focused.getFocusedRect(mTempRect);
2590                                if (mView instanceof ViewGroup) {
2591                                    ((ViewGroup) mView).offsetDescendantRectToMyCoords(
2592                                            focused, mTempRect);
2593                                    ((ViewGroup) mView).offsetRectIntoDescendantCoords(
2594                                            v, mTempRect);
2595                                }
2596                                focusPassed = v.requestFocus(direction, mTempRect);
2597                            }
2598
2599                            if (!focusPassed) {
2600                                mView.dispatchUnhandledMove(focused, direction);
2601                            } else {
2602                                playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
2603                            }
2604                        }
2605                    }
2606                }
2607            }
2608
2609        } finally {
2610            if (sendDone) {
2611                finishInputEvent();
2612            }
2613            // Let the exception fall through -- the looper will catch
2614            // it and take care of the bad app for us.
2615        }
2616    }
2617
2618    private AudioManager getAudioManager() {
2619        if (mView == null) {
2620            throw new IllegalStateException("getAudioManager called when there is no mView");
2621        }
2622        if (mAudioManager == null) {
2623            mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE);
2624        }
2625        return mAudioManager;
2626    }
2627
2628    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
2629            boolean insetsPending) throws RemoteException {
2630
2631        float appScale = mAttachInfo.mApplicationScale;
2632        boolean restore = false;
2633        if (params != null && mTranslator != null) {
2634            restore = true;
2635            params.backup();
2636            mTranslator.translateWindowLayout(params);
2637        }
2638        if (params != null) {
2639            if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
2640        }
2641        mPendingConfiguration.seq = 0;
2642        //Log.d(TAG, ">>>>>> CALLING relayout");
2643        int relayoutResult = sWindowSession.relayout(
2644                mWindow, params,
2645                (int) (mView.mMeasuredWidth * appScale + 0.5f),
2646                (int) (mView.mMeasuredHeight * appScale + 0.5f),
2647                viewVisibility, insetsPending, mWinFrame,
2648                mPendingContentInsets, mPendingVisibleInsets,
2649                mPendingConfiguration, mSurface);
2650        //Log.d(TAG, "<<<<<< BACK FROM relayout");
2651        if (restore) {
2652            params.restore();
2653        }
2654
2655        if (mTranslator != null) {
2656            mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
2657            mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
2658            mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
2659        }
2660        return relayoutResult;
2661    }
2662
2663    /**
2664     * {@inheritDoc}
2665     */
2666    public void playSoundEffect(int effectId) {
2667        checkThread();
2668
2669        try {
2670            final AudioManager audioManager = getAudioManager();
2671
2672            switch (effectId) {
2673                case SoundEffectConstants.CLICK:
2674                    audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
2675                    return;
2676                case SoundEffectConstants.NAVIGATION_DOWN:
2677                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
2678                    return;
2679                case SoundEffectConstants.NAVIGATION_LEFT:
2680                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
2681                    return;
2682                case SoundEffectConstants.NAVIGATION_RIGHT:
2683                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
2684                    return;
2685                case SoundEffectConstants.NAVIGATION_UP:
2686                    audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
2687                    return;
2688                default:
2689                    throw new IllegalArgumentException("unknown effect id " + effectId +
2690                            " not defined in " + SoundEffectConstants.class.getCanonicalName());
2691            }
2692        } catch (IllegalStateException e) {
2693            // Exception thrown by getAudioManager() when mView is null
2694            Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
2695            e.printStackTrace();
2696        }
2697    }
2698
2699    /**
2700     * {@inheritDoc}
2701     */
2702    public boolean performHapticFeedback(int effectId, boolean always) {
2703        try {
2704            return sWindowSession.performHapticFeedback(mWindow, effectId, always);
2705        } catch (RemoteException e) {
2706            return false;
2707        }
2708    }
2709
2710    /**
2711     * {@inheritDoc}
2712     */
2713    public View focusSearch(View focused, int direction) {
2714        checkThread();
2715        if (!(mView instanceof ViewGroup)) {
2716            return null;
2717        }
2718        return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
2719    }
2720
2721    public void debug() {
2722        mView.debug();
2723    }
2724
2725    public void die(boolean immediate) {
2726        if (immediate) {
2727            doDie();
2728        } else {
2729            sendEmptyMessage(DIE);
2730        }
2731    }
2732
2733    void doDie() {
2734        checkThread();
2735        if (Config.LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
2736        synchronized (this) {
2737            if (mAdded && !mFirst) {
2738                int viewVisibility = mView.getVisibility();
2739                boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
2740                if (mWindowAttributesChanged || viewVisibilityChanged) {
2741                    // If layout params have been changed, first give them
2742                    // to the window manager to make sure it has the correct
2743                    // animation info.
2744                    try {
2745                        if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
2746                                & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
2747                            sWindowSession.finishDrawing(mWindow);
2748                        }
2749                    } catch (RemoteException e) {
2750                    }
2751                }
2752
2753                mSurface.release();
2754            }
2755            if (mAdded) {
2756                mAdded = false;
2757                dispatchDetachedFromWindow();
2758            }
2759        }
2760    }
2761
2762    public void dispatchFinishedEvent(int seq, boolean handled) {
2763        Message msg = obtainMessage(FINISHED_EVENT);
2764        msg.arg1 = seq;
2765        msg.arg2 = handled ? 1 : 0;
2766        sendMessage(msg);
2767    }
2768
2769    public void dispatchResized(int w, int h, Rect coveredInsets,
2770            Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
2771        if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w
2772                + " h=" + h + " coveredInsets=" + coveredInsets.toShortString()
2773                + " visibleInsets=" + visibleInsets.toShortString()
2774                + " reportDraw=" + reportDraw);
2775        Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
2776        if (mTranslator != null) {
2777            mTranslator.translateRectInScreenToAppWindow(coveredInsets);
2778            mTranslator.translateRectInScreenToAppWindow(visibleInsets);
2779            w *= mTranslator.applicationInvertedScale;
2780            h *= mTranslator.applicationInvertedScale;
2781        }
2782        msg.arg1 = w;
2783        msg.arg2 = h;
2784        ResizedInfo ri = new ResizedInfo();
2785        ri.coveredInsets = new Rect(coveredInsets);
2786        ri.visibleInsets = new Rect(visibleInsets);
2787        ri.newConfig = newConfig;
2788        msg.obj = ri;
2789        sendMessage(msg);
2790    }
2791
2792    private Runnable mFinishedCallback;
2793
2794    private final InputHandler mInputHandler = new InputHandler() {
2795        public void handleKey(KeyEvent event, Runnable finishedCallback) {
2796            startInputEvent(finishedCallback);
2797            dispatchKey(event, true);
2798        }
2799
2800        public void handleMotion(MotionEvent event, Runnable finishedCallback) {
2801            startInputEvent(finishedCallback);
2802            dispatchMotion(event, true);
2803        }
2804    };
2805
2806    public void dispatchKey(KeyEvent event) {
2807        dispatchKey(event, false);
2808    }
2809
2810    private void dispatchKey(KeyEvent event, boolean sendDone) {
2811        //noinspection ConstantConditions
2812        if (false && event.getAction() == KeyEvent.ACTION_DOWN) {
2813            if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
2814                if (Config.LOGD) Log.d("keydisp",
2815                        "===================================================");
2816                if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
2817                debug();
2818
2819                if (Config.LOGD) Log.d("keydisp",
2820                        "===================================================");
2821            }
2822        }
2823
2824        Message msg = obtainMessage(DISPATCH_KEY);
2825        msg.obj = event;
2826        msg.arg1 = sendDone ? 1 : 0;
2827
2828        if (LOCAL_LOGV) Log.v(
2829            TAG, "sending key " + event + " to " + mView);
2830
2831        sendMessageAtTime(msg, event.getEventTime());
2832    }
2833
2834    public void dispatchMotion(MotionEvent event) {
2835        dispatchMotion(event, false);
2836    }
2837
2838    private void dispatchMotion(MotionEvent event, boolean sendDone) {
2839        int source = event.getSource();
2840        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
2841            dispatchPointer(event, sendDone);
2842        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
2843            dispatchTrackball(event, sendDone);
2844        } else {
2845            // TODO
2846            Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
2847            if (sendDone) {
2848                finishInputEvent();
2849            }
2850        }
2851    }
2852
2853    public void dispatchPointer(MotionEvent event) {
2854        dispatchPointer(event, false);
2855    }
2856
2857    private void dispatchPointer(MotionEvent event, boolean sendDone) {
2858        Message msg = obtainMessage(DISPATCH_POINTER);
2859        msg.obj = event;
2860        msg.arg1 = sendDone ? 1 : 0;
2861        sendMessageAtTime(msg, event.getEventTime());
2862    }
2863
2864    public void dispatchTrackball(MotionEvent event) {
2865        dispatchTrackball(event, false);
2866    }
2867
2868    private void dispatchTrackball(MotionEvent event, boolean sendDone) {
2869        Message msg = obtainMessage(DISPATCH_TRACKBALL);
2870        msg.obj = event;
2871        msg.arg1 = sendDone ? 1 : 0;
2872        sendMessageAtTime(msg, event.getEventTime());
2873    }
2874
2875    public void dispatchAppVisibility(boolean visible) {
2876        Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
2877        msg.arg1 = visible ? 1 : 0;
2878        sendMessage(msg);
2879    }
2880
2881    public void dispatchGetNewSurface() {
2882        Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE);
2883        sendMessage(msg);
2884    }
2885
2886    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
2887        Message msg = Message.obtain();
2888        msg.what = WINDOW_FOCUS_CHANGED;
2889        msg.arg1 = hasFocus ? 1 : 0;
2890        msg.arg2 = inTouchMode ? 1 : 0;
2891        sendMessage(msg);
2892    }
2893
2894    public void dispatchCloseSystemDialogs(String reason) {
2895        Message msg = Message.obtain();
2896        msg.what = CLOSE_SYSTEM_DIALOGS;
2897        msg.obj = reason;
2898        sendMessage(msg);
2899    }
2900
2901    /**
2902     * The window is getting focus so if there is anything focused/selected
2903     * send an {@link AccessibilityEvent} to announce that.
2904     */
2905    private void sendAccessibilityEvents() {
2906        if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) {
2907            return;
2908        }
2909        mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2910        View focusedView = mView.findFocus();
2911        if (focusedView != null && focusedView != mView) {
2912            focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
2913        }
2914    }
2915
2916    public boolean showContextMenuForChild(View originalView) {
2917        return false;
2918    }
2919
2920    public void createContextMenu(ContextMenu menu) {
2921    }
2922
2923    public void childDrawableStateChanged(View child) {
2924    }
2925
2926    protected Rect getWindowFrame() {
2927        return mWinFrame;
2928    }
2929
2930    void checkThread() {
2931        if (mThread != Thread.currentThread()) {
2932            throw new CalledFromWrongThreadException(
2933                    "Only the original thread that created a view hierarchy can touch its views.");
2934        }
2935    }
2936
2937    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2938        // ViewRoot never intercepts touch event, so this can be a no-op
2939    }
2940
2941    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
2942            boolean immediate) {
2943        return scrollToRectOrFocus(rectangle, immediate);
2944    }
2945
2946    class TakenSurfaceHolder extends BaseSurfaceHolder {
2947        @Override
2948        public boolean onAllowLockCanvas() {
2949            return mDrawingAllowed;
2950        }
2951
2952        @Override
2953        public void onRelayoutContainer() {
2954            // Not currently interesting -- from changing between fixed and layout size.
2955        }
2956
2957        public void setFormat(int format) {
2958            ((RootViewSurfaceTaker)mView).setSurfaceFormat(format);
2959        }
2960
2961        public void setType(int type) {
2962            ((RootViewSurfaceTaker)mView).setSurfaceType(type);
2963        }
2964
2965        @Override
2966        public void onUpdateSurface() {
2967            // We take care of format and type changes on our own.
2968            throw new IllegalStateException("Shouldn't be here");
2969        }
2970
2971        public boolean isCreating() {
2972            return mIsCreating;
2973        }
2974
2975        @Override
2976        public void setFixedSize(int width, int height) {
2977            throw new UnsupportedOperationException(
2978                    "Currently only support sizing from layout");
2979        }
2980
2981        public void setKeepScreenOn(boolean screenOn) {
2982            ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
2983        }
2984    }
2985
2986    static class InputMethodCallback extends IInputMethodCallback.Stub {
2987        private WeakReference<ViewRoot> mViewRoot;
2988
2989        public InputMethodCallback(ViewRoot viewRoot) {
2990            mViewRoot = new WeakReference<ViewRoot>(viewRoot);
2991        }
2992
2993        public void finishedEvent(int seq, boolean handled) {
2994            final ViewRoot viewRoot = mViewRoot.get();
2995            if (viewRoot != null) {
2996                viewRoot.dispatchFinishedEvent(seq, handled);
2997            }
2998        }
2999
3000        public void sessionCreated(IInputMethodSession session) throws RemoteException {
3001            // Stub -- not for use in the client.
3002        }
3003    }
3004
3005    static class W extends IWindow.Stub {
3006        private final WeakReference<ViewRoot> mViewRoot;
3007
3008        public W(ViewRoot viewRoot, Context context) {
3009            mViewRoot = new WeakReference<ViewRoot>(viewRoot);
3010        }
3011
3012        public void resized(int w, int h, Rect coveredInsets,
3013                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
3014            final ViewRoot viewRoot = mViewRoot.get();
3015            if (viewRoot != null) {
3016                viewRoot.dispatchResized(w, h, coveredInsets,
3017                        visibleInsets, reportDraw, newConfig);
3018            }
3019        }
3020
3021        public void dispatchAppVisibility(boolean visible) {
3022            final ViewRoot viewRoot = mViewRoot.get();
3023            if (viewRoot != null) {
3024                viewRoot.dispatchAppVisibility(visible);
3025            }
3026        }
3027
3028        public void dispatchGetNewSurface() {
3029            final ViewRoot viewRoot = mViewRoot.get();
3030            if (viewRoot != null) {
3031                viewRoot.dispatchGetNewSurface();
3032            }
3033        }
3034
3035        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
3036            final ViewRoot viewRoot = mViewRoot.get();
3037            if (viewRoot != null) {
3038                viewRoot.windowFocusChanged(hasFocus, inTouchMode);
3039            }
3040        }
3041
3042        private static int checkCallingPermission(String permission) {
3043            if (!Process.supportsProcesses()) {
3044                return PackageManager.PERMISSION_GRANTED;
3045            }
3046
3047            try {
3048                return ActivityManagerNative.getDefault().checkPermission(
3049                        permission, Binder.getCallingPid(), Binder.getCallingUid());
3050            } catch (RemoteException e) {
3051                return PackageManager.PERMISSION_DENIED;
3052            }
3053        }
3054
3055        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
3056            final ViewRoot viewRoot = mViewRoot.get();
3057            if (viewRoot != null) {
3058                final View view = viewRoot.mView;
3059                if (view != null) {
3060                    if (checkCallingPermission(Manifest.permission.DUMP) !=
3061                            PackageManager.PERMISSION_GRANTED) {
3062                        throw new SecurityException("Insufficient permissions to invoke"
3063                                + " executeCommand() from pid=" + Binder.getCallingPid()
3064                                + ", uid=" + Binder.getCallingUid());
3065                    }
3066
3067                    OutputStream clientStream = null;
3068                    try {
3069                        clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out);
3070                        ViewDebug.dispatchCommand(view, command, parameters, clientStream);
3071                    } catch (IOException e) {
3072                        e.printStackTrace();
3073                    } finally {
3074                        if (clientStream != null) {
3075                            try {
3076                                clientStream.close();
3077                            } catch (IOException e) {
3078                                e.printStackTrace();
3079                            }
3080                        }
3081                    }
3082                }
3083            }
3084        }
3085
3086        public void closeSystemDialogs(String reason) {
3087            final ViewRoot viewRoot = mViewRoot.get();
3088            if (viewRoot != null) {
3089                viewRoot.dispatchCloseSystemDialogs(reason);
3090            }
3091        }
3092
3093        public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
3094                boolean sync) {
3095            if (sync) {
3096                try {
3097                    sWindowSession.wallpaperOffsetsComplete(asBinder());
3098                } catch (RemoteException e) {
3099                }
3100            }
3101        }
3102
3103        public void dispatchWallpaperCommand(String action, int x, int y,
3104                int z, Bundle extras, boolean sync) {
3105            if (sync) {
3106                try {
3107                    sWindowSession.wallpaperCommandComplete(asBinder(), null);
3108                } catch (RemoteException e) {
3109                }
3110            }
3111        }
3112    }
3113
3114    /**
3115     * Maintains state information for a single trackball axis, generating
3116     * discrete (DPAD) movements based on raw trackball motion.
3117     */
3118    static final class TrackballAxis {
3119        /**
3120         * The maximum amount of acceleration we will apply.
3121         */
3122        static final float MAX_ACCELERATION = 20;
3123
3124        /**
3125         * The maximum amount of time (in milliseconds) between events in order
3126         * for us to consider the user to be doing fast trackball movements,
3127         * and thus apply an acceleration.
3128         */
3129        static final long FAST_MOVE_TIME = 150;
3130
3131        /**
3132         * Scaling factor to the time (in milliseconds) between events to how
3133         * much to multiple/divide the current acceleration.  When movement
3134         * is < FAST_MOVE_TIME this multiplies the acceleration; when >
3135         * FAST_MOVE_TIME it divides it.
3136         */
3137        static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
3138
3139        float position;
3140        float absPosition;
3141        float acceleration = 1;
3142        long lastMoveTime = 0;
3143        int step;
3144        int dir;
3145        int nonAccelMovement;
3146
3147        void reset(int _step) {
3148            position = 0;
3149            acceleration = 1;
3150            lastMoveTime = 0;
3151            step = _step;
3152            dir = 0;
3153        }
3154
3155        /**
3156         * Add trackball movement into the state.  If the direction of movement
3157         * has been reversed, the state is reset before adding the
3158         * movement (so that you don't have to compensate for any previously
3159         * collected movement before see the result of the movement in the
3160         * new direction).
3161         *
3162         * @return Returns the absolute value of the amount of movement
3163         * collected so far.
3164         */
3165        float collect(float off, long time, String axis) {
3166            long normTime;
3167            if (off > 0) {
3168                normTime = (long)(off * FAST_MOVE_TIME);
3169                if (dir < 0) {
3170                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
3171                    position = 0;
3172                    step = 0;
3173                    acceleration = 1;
3174                    lastMoveTime = 0;
3175                }
3176                dir = 1;
3177            } else if (off < 0) {
3178                normTime = (long)((-off) * FAST_MOVE_TIME);
3179                if (dir > 0) {
3180                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
3181                    position = 0;
3182                    step = 0;
3183                    acceleration = 1;
3184                    lastMoveTime = 0;
3185                }
3186                dir = -1;
3187            } else {
3188                normTime = 0;
3189            }
3190
3191            // The number of milliseconds between each movement that is
3192            // considered "normal" and will not result in any acceleration
3193            // or deceleration, scaled by the offset we have here.
3194            if (normTime > 0) {
3195                long delta = time - lastMoveTime;
3196                lastMoveTime = time;
3197                float acc = acceleration;
3198                if (delta < normTime) {
3199                    // The user is scrolling rapidly, so increase acceleration.
3200                    float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
3201                    if (scale > 1) acc *= scale;
3202                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
3203                            + off + " normTime=" + normTime + " delta=" + delta
3204                            + " scale=" + scale + " acc=" + acc);
3205                    acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
3206                } else {
3207                    // The user is scrolling slowly, so decrease acceleration.
3208                    float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
3209                    if (scale > 1) acc /= scale;
3210                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
3211                            + off + " normTime=" + normTime + " delta=" + delta
3212                            + " scale=" + scale + " acc=" + acc);
3213                    acceleration = acc > 1 ? acc : 1;
3214                }
3215            }
3216            position += off;
3217            return (absPosition = Math.abs(position));
3218        }
3219
3220        /**
3221         * Generate the number of discrete movement events appropriate for
3222         * the currently collected trackball movement.
3223         *
3224         * @param precision The minimum movement required to generate the
3225         * first discrete movement.
3226         *
3227         * @return Returns the number of discrete movements, either positive
3228         * or negative, or 0 if there is not enough trackball movement yet
3229         * for a discrete movement.
3230         */
3231        int generate(float precision) {
3232            int movement = 0;
3233            nonAccelMovement = 0;
3234            do {
3235                final int dir = position >= 0 ? 1 : -1;
3236                switch (step) {
3237                    // If we are going to execute the first step, then we want
3238                    // to do this as soon as possible instead of waiting for
3239                    // a full movement, in order to make things look responsive.
3240                    case 0:
3241                        if (absPosition < precision) {
3242                            return movement;
3243                        }
3244                        movement += dir;
3245                        nonAccelMovement += dir;
3246                        step = 1;
3247                        break;
3248                    // If we have generated the first movement, then we need
3249                    // to wait for the second complete trackball motion before
3250                    // generating the second discrete movement.
3251                    case 1:
3252                        if (absPosition < 2) {
3253                            return movement;
3254                        }
3255                        movement += dir;
3256                        nonAccelMovement += dir;
3257                        position += dir > 0 ? -2 : 2;
3258                        absPosition = Math.abs(position);
3259                        step = 2;
3260                        break;
3261                    // After the first two, we generate discrete movements
3262                    // consistently with the trackball, applying an acceleration
3263                    // if the trackball is moving quickly.  This is a simple
3264                    // acceleration on top of what we already compute based
3265                    // on how quickly the wheel is being turned, to apply
3266                    // a longer increasing acceleration to continuous movement
3267                    // in one direction.
3268                    default:
3269                        if (absPosition < 1) {
3270                            return movement;
3271                        }
3272                        movement += dir;
3273                        position += dir >= 0 ? -1 : 1;
3274                        absPosition = Math.abs(position);
3275                        float acc = acceleration;
3276                        acc *= 1.1f;
3277                        acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
3278                        break;
3279                }
3280            } while (true);
3281        }
3282    }
3283
3284    public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
3285        public CalledFromWrongThreadException(String msg) {
3286            super(msg);
3287        }
3288    }
3289
3290    private SurfaceHolder mHolder = new SurfaceHolder() {
3291        // we only need a SurfaceHolder for opengl. it would be nice
3292        // to implement everything else though, especially the callback
3293        // support (opengl doesn't make use of it right now, but eventually
3294        // will).
3295        public Surface getSurface() {
3296            return mSurface;
3297        }
3298
3299        public boolean isCreating() {
3300            return false;
3301        }
3302
3303        public void addCallback(Callback callback) {
3304        }
3305
3306        public void removeCallback(Callback callback) {
3307        }
3308
3309        public void setFixedSize(int width, int height) {
3310        }
3311
3312        public void setSizeFromLayout() {
3313        }
3314
3315        public void setFormat(int format) {
3316        }
3317
3318        public void setType(int type) {
3319        }
3320
3321        public void setKeepScreenOn(boolean screenOn) {
3322        }
3323
3324        public Canvas lockCanvas() {
3325            return null;
3326        }
3327
3328        public Canvas lockCanvas(Rect dirty) {
3329            return null;
3330        }
3331
3332        public void unlockCanvasAndPost(Canvas canvas) {
3333        }
3334        public Rect getSurfaceFrame() {
3335            return null;
3336        }
3337    };
3338
3339    static RunQueue getRunQueue() {
3340        RunQueue rq = sRunQueues.get();
3341        if (rq != null) {
3342            return rq;
3343        }
3344        rq = new RunQueue();
3345        sRunQueues.set(rq);
3346        return rq;
3347    }
3348
3349    /**
3350     * @hide
3351     */
3352    static final class RunQueue {
3353        private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
3354
3355        void post(Runnable action) {
3356            postDelayed(action, 0);
3357        }
3358
3359        void postDelayed(Runnable action, long delayMillis) {
3360            HandlerAction handlerAction = new HandlerAction();
3361            handlerAction.action = action;
3362            handlerAction.delay = delayMillis;
3363
3364            synchronized (mActions) {
3365                mActions.add(handlerAction);
3366            }
3367        }
3368
3369        void removeCallbacks(Runnable action) {
3370            final HandlerAction handlerAction = new HandlerAction();
3371            handlerAction.action = action;
3372
3373            synchronized (mActions) {
3374                final ArrayList<HandlerAction> actions = mActions;
3375
3376                while (actions.remove(handlerAction)) {
3377                    // Keep going
3378                }
3379            }
3380        }
3381
3382        void executeActions(Handler handler) {
3383            synchronized (mActions) {
3384                final ArrayList<HandlerAction> actions = mActions;
3385                final int count = actions.size();
3386
3387                for (int i = 0; i < count; i++) {
3388                    final HandlerAction handlerAction = actions.get(i);
3389                    handler.postDelayed(handlerAction.action, handlerAction.delay);
3390                }
3391
3392                actions.clear();
3393            }
3394        }
3395
3396        private static class HandlerAction {
3397            Runnable action;
3398            long delay;
3399
3400            @Override
3401            public boolean equals(Object o) {
3402                if (this == o) return true;
3403                if (o == null || getClass() != o.getClass()) return false;
3404
3405                HandlerAction that = (HandlerAction) o;
3406                return !(action != null ? !action.equals(that.action) : that.action != null);
3407
3408            }
3409
3410            @Override
3411            public int hashCode() {
3412                int result = action != null ? action.hashCode() : 0;
3413                result = 31 * result + (int) (delay ^ (delay >>> 32));
3414                return result;
3415            }
3416        }
3417    }
3418
3419    private static native void nativeShowFPS(Canvas canvas, int durationMillis);
3420
3421    // inform skia to just abandon its texture cache IDs
3422    // doesn't call glDeleteTextures
3423    private static native void nativeAbandonGlCaches();
3424}
3425