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