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