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 android.Manifest;
     20 import android.animation.LayoutTransition;
     21 import android.app.ActivityManagerNative;
     22 import android.content.ClipDescription;
     23 import android.content.ComponentCallbacks;
     24 import android.content.ComponentCallbacks2;
     25 import android.content.Context;
     26 import android.content.pm.ApplicationInfo;
     27 import android.content.pm.PackageManager;
     28 import android.content.res.CompatibilityInfo;
     29 import android.content.res.Configuration;
     30 import android.content.res.Resources;
     31 import android.graphics.Canvas;
     32 import android.graphics.Paint;
     33 import android.graphics.PixelFormat;
     34 import android.graphics.Point;
     35 import android.graphics.PointF;
     36 import android.graphics.PorterDuff;
     37 import android.graphics.Rect;
     38 import android.graphics.Region;
     39 import android.graphics.drawable.Drawable;
     40 import android.media.AudioManager;
     41 import android.os.Binder;
     42 import android.os.Bundle;
     43 import android.os.Debug;
     44 import android.os.Handler;
     45 import android.os.Looper;
     46 import android.os.Message;
     47 import android.os.ParcelFileDescriptor;
     48 import android.os.PowerManager;
     49 import android.os.Process;
     50 import android.os.RemoteException;
     51 import android.os.SystemClock;
     52 import android.os.SystemProperties;
     53 import android.os.Trace;
     54 import android.util.AndroidRuntimeException;
     55 import android.util.DisplayMetrics;
     56 import android.util.Log;
     57 import android.util.Slog;
     58 import android.util.TypedValue;
     59 import android.view.View.AttachInfo;
     60 import android.view.View.MeasureSpec;
     61 import android.view.accessibility.AccessibilityEvent;
     62 import android.view.accessibility.AccessibilityManager;
     63 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
     64 import android.view.accessibility.AccessibilityNodeInfo;
     65 import android.view.accessibility.AccessibilityNodeProvider;
     66 import android.view.accessibility.IAccessibilityInteractionConnection;
     67 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
     68 import android.view.animation.AccelerateDecelerateInterpolator;
     69 import android.view.animation.Interpolator;
     70 import android.view.inputmethod.InputConnection;
     71 import android.view.inputmethod.InputMethodManager;
     72 import android.widget.Scroller;
     73 
     74 import com.android.internal.R;
     75 import com.android.internal.os.SomeArgs;
     76 import com.android.internal.policy.PolicyManager;
     77 import com.android.internal.view.BaseSurfaceHolder;
     78 import com.android.internal.view.RootViewSurfaceTaker;
     79 
     80 import java.io.IOException;
     81 import java.io.OutputStream;
     82 import java.lang.ref.WeakReference;
     83 import java.util.ArrayList;
     84 import java.util.HashSet;
     85 
     86 /**
     87  * The top of a view hierarchy, implementing the needed protocol between View
     88  * and the WindowManager.  This is for the most part an internal implementation
     89  * detail of {@link WindowManagerGlobal}.
     90  *
     91  * {@hide}
     92  */
     93 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
     94 public final class ViewRootImpl implements ViewParent,
     95         View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
     96     private static final String TAG = "ViewRootImpl";
     97     private static final boolean DBG = false;
     98     private static final boolean LOCAL_LOGV = false;
     99     /** @noinspection PointlessBooleanExpression*/
    100     private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
    101     private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
    102     private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV;
    103     private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;
    104     private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
    105     private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
    106     private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
    107     private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
    108     private static final boolean DEBUG_FPS = false;
    109     private static final boolean DEBUG_INPUT_PROCESSING = false || LOCAL_LOGV;
    110 
    111     private static final boolean USE_RENDER_THREAD = false;
    112 
    113     /**
    114      * Set this system property to true to force the view hierarchy to render
    115      * at 60 Hz. This can be used to measure the potential framerate.
    116      */
    117     private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
    118 
    119     /**
    120      * Maximum time we allow the user to roll the trackball enough to generate
    121      * a key event, before resetting the counters.
    122      */
    123     static final int MAX_TRACKBALL_DELAY = 250;
    124 
    125     static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
    126 
    127     static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
    128     static boolean sFirstDrawComplete = false;
    129 
    130     static final ArrayList<ComponentCallbacks> sConfigCallbacks
    131             = new ArrayList<ComponentCallbacks>();
    132 
    133     private static boolean sUseRenderThread = false;
    134     private static boolean sRenderThreadQueried = false;
    135     private static final Object[] sRenderThreadQueryLock = new Object[0];
    136 
    137     final Context mContext;
    138     final IWindowSession mWindowSession;
    139     final Display mDisplay;
    140     final String mBasePackageName;
    141 
    142     final int[] mTmpLocation = new int[2];
    143 
    144     final TypedValue mTmpValue = new TypedValue();
    145 
    146     final Thread mThread;
    147 
    148     final WindowLeaked mLocation;
    149 
    150     final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
    151 
    152     final W mWindow;
    153 
    154     final int mTargetSdkVersion;
    155 
    156     int mSeq;
    157 
    158     View mView;
    159 
    160     View mAccessibilityFocusedHost;
    161     AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
    162 
    163     int mViewVisibility;
    164     boolean mAppVisible = true;
    165     int mOrigWindowType = -1;
    166 
    167     // Set to true if the owner of this window is in the stopped state,
    168     // so the window should no longer be active.
    169     boolean mStopped = false;
    170 
    171     boolean mLastInCompatMode = false;
    172 
    173     SurfaceHolder.Callback2 mSurfaceHolderCallback;
    174     BaseSurfaceHolder mSurfaceHolder;
    175     boolean mIsCreating;
    176     boolean mDrawingAllowed;
    177 
    178     final Region mTransparentRegion;
    179     final Region mPreviousTransparentRegion;
    180 
    181     int mWidth;
    182     int mHeight;
    183     Rect mDirty;
    184     final Rect mCurrentDirty = new Rect();
    185     boolean mIsAnimating;
    186 
    187     CompatibilityInfo.Translator mTranslator;
    188 
    189     final View.AttachInfo mAttachInfo;
    190     InputChannel mInputChannel;
    191     InputQueue.Callback mInputQueueCallback;
    192     InputQueue mInputQueue;
    193     FallbackEventHandler mFallbackEventHandler;
    194     Choreographer mChoreographer;
    195 
    196     final Rect mTempRect; // used in the transaction to not thrash the heap.
    197     final Rect mVisRect; // used to retrieve visible rect of focused view.
    198 
    199     boolean mTraversalScheduled;
    200     int mTraversalBarrier;
    201     boolean mWillDrawSoon;
    202     /** Set to true while in performTraversals for detecting when die(true) is called from internal
    203      * callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */
    204     boolean mIsInTraversal;
    205     boolean mFitSystemWindowsRequested;
    206     boolean mLayoutRequested;
    207     boolean mFirst;
    208     boolean mReportNextDraw;
    209     boolean mFullRedrawNeeded;
    210     boolean mNewSurfaceNeeded;
    211     boolean mHasHadWindowFocus;
    212     boolean mLastWasImTarget;
    213     boolean mWindowsAnimating;
    214     boolean mDrawDuringWindowsAnimating;
    215     boolean mIsDrawing;
    216     int mLastSystemUiVisibility;
    217     int mClientWindowLayoutFlags;
    218     boolean mLastOverscanRequested;
    219 
    220     // Pool of queued input events.
    221     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
    222     private QueuedInputEvent mQueuedInputEventPool;
    223     private int mQueuedInputEventPoolSize;
    224 
    225     /* Input event queue.
    226      * Pending input events are input events waiting to be delivered to the input stages
    227      * and handled by the application.
    228      */
    229     QueuedInputEvent mPendingInputEventHead;
    230     QueuedInputEvent mPendingInputEventTail;
    231     int mPendingInputEventCount;
    232     boolean mProcessInputEventsScheduled;
    233     String mPendingInputEventQueueLengthCounterName = "pq";
    234 
    235     InputStage mFirstInputStage;
    236     InputStage mFirstPostImeInputStage;
    237 
    238     boolean mWindowAttributesChanged = false;
    239     int mWindowAttributesChangesFlag = 0;
    240 
    241     // These can be accessed by any thread, must be protected with a lock.
    242     // Surface can never be reassigned or cleared (use Surface.clear()).
    243     private final Surface mSurface = new Surface();
    244 
    245     boolean mAdded;
    246     boolean mAddedTouchMode;
    247 
    248     final CompatibilityInfoHolder mCompatibilityInfo;
    249 
    250     // These are accessed by multiple threads.
    251     final Rect mWinFrame; // frame given by window manager.
    252 
    253     final Rect mPendingOverscanInsets = new Rect();
    254     final Rect mPendingVisibleInsets = new Rect();
    255     final Rect mPendingContentInsets = new Rect();
    256     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
    257             = new ViewTreeObserver.InternalInsetsInfo();
    258 
    259     final Rect mFitSystemWindowsInsets = new Rect();
    260 
    261     final Configuration mLastConfiguration = new Configuration();
    262     final Configuration mPendingConfiguration = new Configuration();
    263 
    264     boolean mScrollMayChange;
    265     int mSoftInputMode;
    266     WeakReference<View> mLastScrolledFocus;
    267     int mScrollY;
    268     int mCurScrollY;
    269     Scroller mScroller;
    270     HardwareLayer mResizeBuffer;
    271     long mResizeBufferStartTime;
    272     int mResizeBufferDuration;
    273     static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
    274     private ArrayList<LayoutTransition> mPendingTransitions;
    275 
    276     final ViewConfiguration mViewConfiguration;
    277 
    278     /* Drag/drop */
    279     ClipDescription mDragDescription;
    280     View mCurrentDragView;
    281     volatile Object mLocalDragState;
    282     final PointF mDragPoint = new PointF();
    283     final PointF mLastTouchPoint = new PointF();
    284 
    285     private boolean mProfileRendering;
    286     private Choreographer.FrameCallback mRenderProfiler;
    287     private boolean mRenderProfilingEnabled;
    288 
    289     // Variables to track frames per second, enabled via DEBUG_FPS flag
    290     private long mFpsStartTime = -1;
    291     private long mFpsPrevTime = -1;
    292     private int mFpsNumFrames;
    293 
    294     private final ArrayList<DisplayList> mDisplayLists = new ArrayList<DisplayList>();
    295 
    296     /**
    297      * see {@link #playSoundEffect(int)}
    298      */
    299     AudioManager mAudioManager;
    300 
    301     final AccessibilityManager mAccessibilityManager;
    302 
    303     AccessibilityInteractionController mAccessibilityInteractionController;
    304 
    305     AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager;
    306 
    307     SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
    308 
    309     HashSet<View> mTempHashSet;
    310 
    311     private final int mDensity;
    312     private final int mNoncompatDensity;
    313 
    314     private boolean mInLayout = false;
    315     ArrayList<View> mLayoutRequesters = new ArrayList<View>();
    316     boolean mHandlingLayoutInLayoutRequest = false;
    317 
    318     private int mViewLayoutDirectionInitial;
    319 
    320     /**
    321      * Consistency verifier for debugging purposes.
    322      */
    323     protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
    324             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
    325                     new InputEventConsistencyVerifier(this, 0) : null;
    326 
    327     static final class SystemUiVisibilityInfo {
    328         int seq;
    329         int globalVisibility;
    330         int localValue;
    331         int localChanges;
    332     }
    333 
    334     public ViewRootImpl(Context context, Display display) {
    335         mContext = context;
    336         mWindowSession = WindowManagerGlobal.getWindowSession();
    337         mDisplay = display;
    338         mBasePackageName = context.getBasePackageName();
    339 
    340         CompatibilityInfoHolder cih = display.getCompatibilityInfo();
    341         mCompatibilityInfo = cih != null ? cih : new CompatibilityInfoHolder();
    342 
    343         mThread = Thread.currentThread();
    344         mLocation = new WindowLeaked(null);
    345         mLocation.fillInStackTrace();
    346         mWidth = -1;
    347         mHeight = -1;
    348         mDirty = new Rect();
    349         mTempRect = new Rect();
    350         mVisRect = new Rect();
    351         mWinFrame = new Rect();
    352         mWindow = new W(this);
    353         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
    354         mViewVisibility = View.GONE;
    355         mTransparentRegion = new Region();
    356         mPreviousTransparentRegion = new Region();
    357         mFirst = true; // true for the first time the view is added
    358         mAdded = false;
    359         mAccessibilityManager = AccessibilityManager.getInstance(context);
    360         mAccessibilityInteractionConnectionManager =
    361             new AccessibilityInteractionConnectionManager();
    362         mAccessibilityManager.addAccessibilityStateChangeListener(
    363                 mAccessibilityInteractionConnectionManager);
    364         mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
    365         mViewConfiguration = ViewConfiguration.get(context);
    366         mDensity = context.getResources().getDisplayMetrics().densityDpi;
    367         mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
    368         mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
    369         mChoreographer = Choreographer.getInstance();
    370 
    371         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    372         mAttachInfo.mScreenOn = powerManager.isScreenOn();
    373         loadSystemProperties();
    374     }
    375 
    376     /**
    377      * @return True if the application requests the use of a separate render thread,
    378      *         false otherwise
    379      */
    380     private static boolean isRenderThreadRequested(Context context) {
    381         if (USE_RENDER_THREAD) {
    382             synchronized (sRenderThreadQueryLock) {
    383                 if (!sRenderThreadQueried) {
    384                     final PackageManager packageManager = context.getPackageManager();
    385                     final String packageName = context.getApplicationInfo().packageName;
    386                     try {
    387                         ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
    388                                 PackageManager.GET_META_DATA);
    389                         if (applicationInfo.metaData != null) {
    390                             sUseRenderThread = applicationInfo.metaData.getBoolean(
    391                                     "android.graphics.renderThread", false);
    392                         }
    393                     } catch (PackageManager.NameNotFoundException e) {
    394                     } finally {
    395                         sRenderThreadQueried = true;
    396                     }
    397                 }
    398                 return sUseRenderThread;
    399             }
    400         } else {
    401             return false;
    402         }
    403     }
    404 
    405     public static void addFirstDrawHandler(Runnable callback) {
    406         synchronized (sFirstDrawHandlers) {
    407             if (!sFirstDrawComplete) {
    408                 sFirstDrawHandlers.add(callback);
    409             }
    410         }
    411     }
    412 
    413     public static void addConfigCallback(ComponentCallbacks callback) {
    414         synchronized (sConfigCallbacks) {
    415             sConfigCallbacks.add(callback);
    416         }
    417     }
    418 
    419     // FIXME for perf testing only
    420     private boolean mProfile = false;
    421 
    422     /**
    423      * Call this to profile the next traversal call.
    424      * FIXME for perf testing only. Remove eventually
    425      */
    426     public void profile() {
    427         mProfile = true;
    428     }
    429 
    430     /**
    431      * Indicates whether we are in touch mode. Calling this method triggers an IPC
    432      * call and should be avoided whenever possible.
    433      *
    434      * @return True, if the device is in touch mode, false otherwise.
    435      *
    436      * @hide
    437      */
    438     static boolean isInTouchMode() {
    439         IWindowSession windowSession = WindowManagerGlobal.peekWindowSession();
    440         if (windowSession != null) {
    441             try {
    442                 return windowSession.getInTouchMode();
    443             } catch (RemoteException e) {
    444             }
    445         }
    446         return false;
    447     }
    448 
    449     /**
    450      * We have one child
    451      */
    452     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    453         synchronized (this) {
    454             if (mView == null) {
    455                 mView = view;
    456                 mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
    457                 mFallbackEventHandler.setView(view);
    458                 mWindowAttributes.copyFrom(attrs);
    459                 if (mWindowAttributes.packageName == null) {
    460                     mWindowAttributes.packageName = mBasePackageName;
    461                 }
    462                 attrs = mWindowAttributes;
    463                 // Keep track of the actual window flags supplied by the client.
    464                 mClientWindowLayoutFlags = attrs.flags;
    465 
    466                 setAccessibilityFocus(null, null);
    467 
    468                 if (view instanceof RootViewSurfaceTaker) {
    469                     mSurfaceHolderCallback =
    470                             ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
    471                     if (mSurfaceHolderCallback != null) {
    472                         mSurfaceHolder = new TakenSurfaceHolder();
    473                         mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
    474                     }
    475                 }
    476 
    477                 CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
    478                 mTranslator = compatibilityInfo.getTranslator();
    479 
    480                 // If the application owns the surface, don't enable hardware acceleration
    481                 if (mSurfaceHolder == null) {
    482                     enableHardwareAcceleration(mView.getContext(), attrs);
    483                 }
    484 
    485                 boolean restore = false;
    486                 if (mTranslator != null) {
    487                     mSurface.setCompatibilityTranslator(mTranslator);
    488                     restore = true;
    489                     attrs.backup();
    490                     mTranslator.translateWindowLayout(attrs);
    491                 }
    492                 if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
    493 
    494                 if (!compatibilityInfo.supportsScreen()) {
    495                     attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
    496                     mLastInCompatMode = true;
    497                 }
    498 
    499                 mSoftInputMode = attrs.softInputMode;
    500                 mWindowAttributesChanged = true;
    501                 mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
    502                 mAttachInfo.mRootView = view;
    503                 mAttachInfo.mScalingRequired = mTranslator != null;
    504                 mAttachInfo.mApplicationScale =
    505                         mTranslator == null ? 1.0f : mTranslator.applicationScale;
    506                 if (panelParentView != null) {
    507                     mAttachInfo.mPanelParentWindowToken
    508                             = panelParentView.getApplicationWindowToken();
    509                 }
    510                 mAdded = true;
    511                 int res; /* = WindowManagerImpl.ADD_OKAY; */
    512 
    513                 // Schedule the first layout -before- adding to the window
    514                 // manager, to make sure we do the relayout before receiving
    515                 // any other events from the system.
    516                 requestLayout();
    517                 if ((mWindowAttributes.inputFeatures
    518                         & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
    519                     mInputChannel = new InputChannel();
    520                 }
    521                 try {
    522                     mOrigWindowType = mWindowAttributes.type;
    523                     mAttachInfo.mRecomputeGlobalAttributes = true;
    524                     collectViewAttributes();
    525                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
    526                             getHostVisibility(), mDisplay.getDisplayId(),
    527                             mAttachInfo.mContentInsets, mInputChannel);
    528                 } catch (RemoteException e) {
    529                     mAdded = false;
    530                     mView = null;
    531                     mAttachInfo.mRootView = null;
    532                     mInputChannel = null;
    533                     mFallbackEventHandler.setView(null);
    534                     unscheduleTraversals();
    535                     setAccessibilityFocus(null, null);
    536                     throw new RuntimeException("Adding window failed", e);
    537                 } finally {
    538                     if (restore) {
    539                         attrs.restore();
    540                     }
    541                 }
    542 
    543                 if (mTranslator != null) {
    544                     mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
    545                 }
    546                 mPendingOverscanInsets.set(0, 0, 0, 0);
    547                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
    548                 mPendingVisibleInsets.set(0, 0, 0, 0);
    549                 if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
    550                 if (res < WindowManagerGlobal.ADD_OKAY) {
    551                     mAttachInfo.mRootView = null;
    552                     mAdded = false;
    553                     mFallbackEventHandler.setView(null);
    554                     unscheduleTraversals();
    555                     setAccessibilityFocus(null, null);
    556                     switch (res) {
    557                         case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
    558                         case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
    559                             throw new WindowManager.BadTokenException(
    560                                 "Unable to add window -- token " + attrs.token
    561                                 + " is not valid; is your activity running?");
    562                         case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
    563                             throw new WindowManager.BadTokenException(
    564                                 "Unable to add window -- token " + attrs.token
    565                                 + " is not for an application");
    566                         case WindowManagerGlobal.ADD_APP_EXITING:
    567                             throw new WindowManager.BadTokenException(
    568                                 "Unable to add window -- app for token " + attrs.token
    569                                 + " is exiting");
    570                         case WindowManagerGlobal.ADD_DUPLICATE_ADD:
    571                             throw new WindowManager.BadTokenException(
    572                                 "Unable to add window -- window " + mWindow
    573                                 + " has already been added");
    574                         case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
    575                             // Silently ignore -- we would have just removed it
    576                             // right away, anyway.
    577                             return;
    578                         case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
    579                             throw new WindowManager.BadTokenException(
    580                                 "Unable to add window " + mWindow +
    581                                 " -- another window of this type already exists");
    582                         case WindowManagerGlobal.ADD_PERMISSION_DENIED:
    583                             throw new WindowManager.BadTokenException(
    584                                 "Unable to add window " + mWindow +
    585                                 " -- permission denied for this window type");
    586                         case WindowManagerGlobal.ADD_INVALID_DISPLAY:
    587                             throw new WindowManager.InvalidDisplayException(
    588                                 "Unable to add window " + mWindow +
    589                                 " -- the specified display can not be found");
    590                     }
    591                     throw new RuntimeException(
    592                         "Unable to add window -- unknown error code " + res);
    593                 }
    594 
    595                 if (view instanceof RootViewSurfaceTaker) {
    596                     mInputQueueCallback =
    597                         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
    598                 }
    599                 if (mInputChannel != null) {
    600                     if (mInputQueueCallback != null) {
    601                         mInputQueue = new InputQueue();
    602                         mInputQueueCallback.onInputQueueCreated(mInputQueue);
    603                     }
    604                     mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
    605                             Looper.myLooper());
    606                 }
    607 
    608                 view.assignParent(this);
    609                 mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
    610                 mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
    611 
    612                 if (mAccessibilityManager.isEnabled()) {
    613                     mAccessibilityInteractionConnectionManager.ensureConnection();
    614                 }
    615 
    616                 if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    617                     view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    618                 }
    619 
    620                 // Set up the input pipeline.
    621                 CharSequence counterSuffix = attrs.getTitle();
    622                 InputStage syntheticStage = new SyntheticInputStage();
    623                 InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage);
    624                 InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
    625                         "aq:native-post-ime:" + counterSuffix);
    626                 InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
    627                 InputStage imeStage = new ImeInputStage(earlyPostImeStage,
    628                         "aq:ime:" + counterSuffix);
    629                 InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
    630                 InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
    631                         "aq:native-pre-ime:" + counterSuffix);
    632 
    633                 mFirstInputStage = nativePreImeStage;
    634                 mFirstPostImeInputStage = earlyPostImeStage;
    635                 mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
    636             }
    637         }
    638     }
    639 
    640     void destroyHardwareResources() {
    641         if (mAttachInfo.mHardwareRenderer != null) {
    642             if (mAttachInfo.mHardwareRenderer.isEnabled()) {
    643                 mAttachInfo.mHardwareRenderer.destroyLayers(mView);
    644             }
    645             mAttachInfo.mHardwareRenderer.destroy(false);
    646         }
    647     }
    648 
    649     void terminateHardwareResources() {
    650         if (mAttachInfo.mHardwareRenderer != null) {
    651             mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
    652             mAttachInfo.mHardwareRenderer.destroy(false);
    653         }
    654     }
    655 
    656     void destroyHardwareLayers() {
    657         if (mThread != Thread.currentThread()) {
    658             if (mAttachInfo.mHardwareRenderer != null &&
    659                     mAttachInfo.mHardwareRenderer.isEnabled()) {
    660                 HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE);
    661             }
    662         } else {
    663             if (mAttachInfo.mHardwareRenderer != null &&
    664                     mAttachInfo.mHardwareRenderer.isEnabled()) {
    665                 mAttachInfo.mHardwareRenderer.destroyLayers(mView);
    666             }
    667         }
    668     }
    669 
    670     void pushHardwareLayerUpdate(HardwareLayer layer) {
    671         if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
    672             mAttachInfo.mHardwareRenderer.pushLayerUpdate(layer);
    673         }
    674     }
    675 
    676     public boolean attachFunctor(int functor) {
    677         //noinspection SimplifiableIfStatement
    678         if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
    679             return mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
    680         }
    681         return false;
    682     }
    683 
    684     public void detachFunctor(int functor) {
    685         if (mAttachInfo.mHardwareRenderer != null) {
    686             mAttachInfo.mHardwareRenderer.detachFunctor(functor);
    687         }
    688     }
    689 
    690     private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
    691         mAttachInfo.mHardwareAccelerated = false;
    692         mAttachInfo.mHardwareAccelerationRequested = false;
    693 
    694         // Don't enable hardware acceleration when the application is in compatibility mode
    695         if (mTranslator != null) return;
    696 
    697         // Try to enable hardware acceleration if requested
    698         final boolean hardwareAccelerated =
    699                 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
    700 
    701         if (hardwareAccelerated) {
    702             if (!HardwareRenderer.isAvailable()) {
    703                 return;
    704             }
    705 
    706             // Persistent processes (including the system) should not do
    707             // accelerated rendering on low-end devices.  In that case,
    708             // sRendererDisabled will be set.  In addition, the system process
    709             // itself should never do accelerated rendering.  In that case, both
    710             // sRendererDisabled and sSystemRendererDisabled are set.  When
    711             // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
    712             // can be used by code on the system process to escape that and enable
    713             // HW accelerated drawing.  (This is basically for the lock screen.)
    714 
    715             final boolean fakeHwAccelerated = (attrs.privateFlags &
    716                     WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
    717             final boolean forceHwAccelerated = (attrs.privateFlags &
    718                     WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
    719 
    720             if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
    721                     && forceHwAccelerated)) {
    722                 // Don't enable hardware acceleration when we're not on the main thread
    723                 if (!HardwareRenderer.sSystemRendererDisabled &&
    724                         Looper.getMainLooper() != Looper.myLooper()) {
    725                     Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
    726                             + "acceleration outside of the main thread, aborting");
    727                     return;
    728                 }
    729 
    730                 final boolean renderThread = isRenderThreadRequested(context);
    731                 if (renderThread) {
    732                     Log.i(HardwareRenderer.LOG_TAG, "Render threat initiated");
    733                 }
    734 
    735                 if (mAttachInfo.mHardwareRenderer != null) {
    736                     mAttachInfo.mHardwareRenderer.destroy(true);
    737                 }
    738 
    739                 final boolean translucent = attrs.format != PixelFormat.OPAQUE;
    740                 mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
    741                 if (mAttachInfo.mHardwareRenderer != null) {
    742                     mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
    743                     mAttachInfo.mHardwareAccelerated =
    744                             mAttachInfo.mHardwareAccelerationRequested = true;
    745                 }
    746             } else if (fakeHwAccelerated) {
    747                 // The window had wanted to use hardware acceleration, but this
    748                 // is not allowed in its process.  By setting this flag, it can
    749                 // still render as if it was accelerated.  This is basically for
    750                 // the preview windows the window manager shows for launching
    751                 // applications, so they will look more like the app being launched.
    752                 mAttachInfo.mHardwareAccelerationRequested = true;
    753             }
    754         }
    755     }
    756 
    757     public View getView() {
    758         return mView;
    759     }
    760 
    761     final WindowLeaked getLocation() {
    762         return mLocation;
    763     }
    764 
    765     void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
    766         synchronized (this) {
    767             int oldSoftInputMode = mWindowAttributes.softInputMode;
    768             // Keep track of the actual window flags supplied by the client.
    769             mClientWindowLayoutFlags = attrs.flags;
    770             // preserve compatible window flag if exists.
    771             int compatibleWindowFlag =
    772                 mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
    773             // transfer over system UI visibility values as they carry current state.
    774             attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility;
    775             attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
    776             mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs);
    777             if (mWindowAttributes.packageName == null) {
    778                 mWindowAttributes.packageName = mBasePackageName;
    779             }
    780             mWindowAttributes.flags |= compatibleWindowFlag;
    781 
    782             applyKeepScreenOnFlag(mWindowAttributes);
    783 
    784             if (newView) {
    785                 mSoftInputMode = attrs.softInputMode;
    786                 requestLayout();
    787             }
    788             // Don't lose the mode we last auto-computed.
    789             if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
    790                     == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
    791                 mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
    792                         & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
    793                         | (oldSoftInputMode
    794                                 & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
    795             }
    796             mWindowAttributesChanged = true;
    797             scheduleTraversals();
    798         }
    799     }
    800 
    801     void handleAppVisibility(boolean visible) {
    802         if (mAppVisible != visible) {
    803             mAppVisible = visible;
    804             scheduleTraversals();
    805         }
    806     }
    807 
    808     void handleGetNewSurface() {
    809         mNewSurfaceNeeded = true;
    810         mFullRedrawNeeded = true;
    811         scheduleTraversals();
    812     }
    813 
    814     void handleScreenStateChange(boolean on) {
    815         if (on != mAttachInfo.mScreenOn) {
    816             mAttachInfo.mScreenOn = on;
    817             if (mView != null) {
    818                 mView.dispatchScreenStateChanged(on ? View.SCREEN_STATE_ON : View.SCREEN_STATE_OFF);
    819             }
    820             if (on) {
    821                 mFullRedrawNeeded = true;
    822                 scheduleTraversals();
    823             }
    824         }
    825     }
    826 
    827     @Override
    828     public void requestFitSystemWindows() {
    829         checkThread();
    830         mFitSystemWindowsRequested = true;
    831         scheduleTraversals();
    832     }
    833 
    834     @Override
    835     public void requestLayout() {
    836         if (!mHandlingLayoutInLayoutRequest) {
    837             checkThread();
    838             mLayoutRequested = true;
    839             scheduleTraversals();
    840         }
    841     }
    842 
    843     @Override
    844     public boolean isLayoutRequested() {
    845         return mLayoutRequested;
    846     }
    847 
    848     void invalidate() {
    849         mDirty.set(0, 0, mWidth, mHeight);
    850         scheduleTraversals();
    851     }
    852 
    853     void invalidateWorld(View view) {
    854         view.invalidate();
    855         if (view instanceof ViewGroup) {
    856             ViewGroup parent = (ViewGroup) view;
    857             for (int i = 0; i < parent.getChildCount(); i++) {
    858                 invalidateWorld(parent.getChildAt(i));
    859             }
    860         }
    861     }
    862 
    863     @Override
    864     public void invalidateChild(View child, Rect dirty) {
    865         invalidateChildInParent(null, dirty);
    866     }
    867 
    868     public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    869         checkThread();
    870         if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
    871 
    872         if (dirty == null) {
    873             invalidate();
    874             return null;
    875         } else if (dirty.isEmpty() && !mIsAnimating) {
    876             return null;
    877         }
    878 
    879         if (mCurScrollY != 0 || mTranslator != null) {
    880             mTempRect.set(dirty);
    881             dirty = mTempRect;
    882             if (mCurScrollY != 0) {
    883                 dirty.offset(0, -mCurScrollY);
    884             }
    885             if (mTranslator != null) {
    886                 mTranslator.translateRectInAppWindowToScreen(dirty);
    887             }
    888             if (mAttachInfo.mScalingRequired) {
    889                 dirty.inset(-1, -1);
    890             }
    891         }
    892 
    893         final Rect localDirty = mDirty;
    894         if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
    895             mAttachInfo.mSetIgnoreDirtyState = true;
    896             mAttachInfo.mIgnoreDirtyState = true;
    897         }
    898 
    899         // Add the new dirty rect to the current one
    900         localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
    901         // Intersect with the bounds of the window to skip
    902         // updates that lie outside of the visible region
    903         final float appScale = mAttachInfo.mApplicationScale;
    904         final boolean intersected = localDirty.intersect(0, 0,
    905                 (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
    906         if (!intersected) {
    907             localDirty.setEmpty();
    908         }
    909         if (!mWillDrawSoon && (intersected || mIsAnimating)) {
    910             scheduleTraversals();
    911         }
    912 
    913         return null;
    914     }
    915 
    916     void setStopped(boolean stopped) {
    917         if (mStopped != stopped) {
    918             mStopped = stopped;
    919             if (!stopped) {
    920                 scheduleTraversals();
    921             }
    922         }
    923     }
    924 
    925     public ViewParent getParent() {
    926         return null;
    927     }
    928 
    929     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
    930         if (child != mView) {
    931             throw new RuntimeException("child is not mine, honest!");
    932         }
    933         // Note: don't apply scroll offset, because we want to know its
    934         // visibility in the virtual canvas being given to the view hierarchy.
    935         return r.intersect(0, 0, mWidth, mHeight);
    936     }
    937 
    938     public void bringChildToFront(View child) {
    939     }
    940 
    941     int getHostVisibility() {
    942         return mAppVisible ? mView.getVisibility() : View.GONE;
    943     }
    944 
    945     void disposeResizeBuffer() {
    946         if (mResizeBuffer != null) {
    947             mResizeBuffer.destroy();
    948             mResizeBuffer = null;
    949         }
    950     }
    951 
    952     /**
    953      * Add LayoutTransition to the list of transitions to be started in the next traversal.
    954      * This list will be cleared after the transitions on the list are start()'ed. These
    955      * transitionsa re added by LayoutTransition itself when it sets up animations. The setup
    956      * happens during the layout phase of traversal, which we want to complete before any of the
    957      * animations are started (because those animations may side-effect properties that layout
    958      * depends upon, like the bounding rectangles of the affected views). So we add the transition
    959      * to the list and it is started just prior to starting the drawing phase of traversal.
    960      *
    961      * @param transition The LayoutTransition to be started on the next traversal.
    962      *
    963      * @hide
    964      */
    965     public void requestTransitionStart(LayoutTransition transition) {
    966         if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) {
    967             if (mPendingTransitions == null) {
    968                  mPendingTransitions = new ArrayList<LayoutTransition>();
    969             }
    970             mPendingTransitions.add(transition);
    971         }
    972     }
    973 
    974     void scheduleTraversals() {
    975         if (!mTraversalScheduled) {
    976             mTraversalScheduled = true;
    977             mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
    978             mChoreographer.postCallback(
    979                     Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    980             scheduleConsumeBatchedInput();
    981         }
    982     }
    983 
    984     void unscheduleTraversals() {
    985         if (mTraversalScheduled) {
    986             mTraversalScheduled = false;
    987             mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
    988             mChoreographer.removeCallbacks(
    989                     Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    990         }
    991     }
    992 
    993     void doTraversal() {
    994         if (mTraversalScheduled) {
    995             mTraversalScheduled = false;
    996             mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
    997 
    998             if (mProfile) {
    999                 Debug.startMethodTracing("ViewAncestor");
   1000             }
   1001 
   1002             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
   1003             try {
   1004                 performTraversals();
   1005             } finally {
   1006                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
   1007             }
   1008 
   1009             if (mProfile) {
   1010                 Debug.stopMethodTracing();
   1011                 mProfile = false;
   1012             }
   1013         }
   1014     }
   1015 
   1016     private void applyKeepScreenOnFlag(WindowManager.LayoutParams params) {
   1017         // Update window's global keep screen on flag: if a view has requested
   1018         // that the screen be kept on, then it is always set; otherwise, it is
   1019         // set to whatever the client last requested for the global state.
   1020         if (mAttachInfo.mKeepScreenOn) {
   1021             params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
   1022         } else {
   1023             params.flags = (params.flags&~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
   1024                     | (mClientWindowLayoutFlags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   1025         }
   1026     }
   1027 
   1028     private boolean collectViewAttributes() {
   1029         final View.AttachInfo attachInfo = mAttachInfo;
   1030         if (attachInfo.mRecomputeGlobalAttributes) {
   1031             //Log.i(TAG, "Computing view hierarchy attributes!");
   1032             attachInfo.mRecomputeGlobalAttributes = false;
   1033             boolean oldScreenOn = attachInfo.mKeepScreenOn;
   1034             attachInfo.mKeepScreenOn = false;
   1035             attachInfo.mSystemUiVisibility = 0;
   1036             attachInfo.mHasSystemUiListeners = false;
   1037             mView.dispatchCollectViewAttributes(attachInfo, 0);
   1038             attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility;
   1039             WindowManager.LayoutParams params = mWindowAttributes;
   1040             if (attachInfo.mKeepScreenOn != oldScreenOn
   1041                     || attachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
   1042                     || attachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
   1043                 applyKeepScreenOnFlag(params);
   1044                 params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility;
   1045                 params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners;
   1046                 mView.dispatchWindowSystemUiVisiblityChanged(attachInfo.mSystemUiVisibility);
   1047                 return true;
   1048             }
   1049         }
   1050         return false;
   1051     }
   1052 
   1053     private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
   1054             final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
   1055         int childWidthMeasureSpec;
   1056         int childHeightMeasureSpec;
   1057         boolean windowSizeMayChange = false;
   1058 
   1059         if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
   1060                 "Measuring " + host + " in display " + desiredWindowWidth
   1061                 + "x" + desiredWindowHeight + "...");
   1062 
   1063         boolean goodMeasure = false;
   1064         if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
   1065             // On large screens, we don't want to allow dialogs to just
   1066             // stretch to fill the entire width of the screen to display
   1067             // one line of text.  First try doing the layout at a smaller
   1068             // size to see if it will fit.
   1069             final DisplayMetrics packageMetrics = res.getDisplayMetrics();
   1070             res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
   1071             int baseSize = 0;
   1072             if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
   1073                 baseSize = (int)mTmpValue.getDimension(packageMetrics);
   1074             }
   1075             if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
   1076             if (baseSize != 0 && desiredWindowWidth > baseSize) {
   1077                 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
   1078                 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
   1079                 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   1080                 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
   1081                         + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
   1082                 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
   1083                     goodMeasure = true;
   1084                 } else {
   1085                     // Didn't fit in that size... try expanding a bit.
   1086                     baseSize = (baseSize+desiredWindowWidth)/2;
   1087                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
   1088                             + baseSize);
   1089                     childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
   1090                     performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   1091                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
   1092                             + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
   1093                     if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
   1094                         if (DEBUG_DIALOG) Log.v(TAG, "Good!");
   1095                         goodMeasure = true;
   1096                     }
   1097                 }
   1098             }
   1099         }
   1100 
   1101         if (!goodMeasure) {
   1102             childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
   1103             childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
   1104             performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   1105             if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
   1106                 windowSizeMayChange = true;
   1107             }
   1108         }
   1109 
   1110         if (DBG) {
   1111             System.out.println("======================================");
   1112             System.out.println("performTraversals -- after measure");
   1113             host.debug();
   1114         }
   1115 
   1116         return windowSizeMayChange;
   1117     }
   1118 
   1119     private void performTraversals() {
   1120         // cache mView since it is used so much below...
   1121         final View host = mView;
   1122 
   1123         if (DBG) {
   1124             System.out.println("======================================");
   1125             System.out.println("performTraversals");
   1126             host.debug();
   1127         }
   1128 
   1129         if (host == null || !mAdded)
   1130             return;
   1131 
   1132         mIsInTraversal = true;
   1133         mWillDrawSoon = true;
   1134         boolean windowSizeMayChange = false;
   1135         boolean newSurface = false;
   1136         boolean surfaceChanged = false;
   1137         WindowManager.LayoutParams lp = mWindowAttributes;
   1138 
   1139         int desiredWindowWidth;
   1140         int desiredWindowHeight;
   1141 
   1142         final View.AttachInfo attachInfo = mAttachInfo;
   1143 
   1144         final int viewVisibility = getHostVisibility();
   1145         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
   1146                 || mNewSurfaceNeeded;
   1147 
   1148         WindowManager.LayoutParams params = null;
   1149         if (mWindowAttributesChanged) {
   1150             mWindowAttributesChanged = false;
   1151             surfaceChanged = true;
   1152             params = lp;
   1153         }
   1154         CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
   1155         if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
   1156             params = lp;
   1157             mFullRedrawNeeded = true;
   1158             mLayoutRequested = true;
   1159             if (mLastInCompatMode) {
   1160                 params.flags &= ~WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
   1161                 mLastInCompatMode = false;
   1162             } else {
   1163                 params.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
   1164                 mLastInCompatMode = true;
   1165             }
   1166         }
   1167 
   1168         mWindowAttributesChangesFlag = 0;
   1169 
   1170         Rect frame = mWinFrame;
   1171         if (mFirst) {
   1172             mFullRedrawNeeded = true;
   1173             mLayoutRequested = true;
   1174 
   1175             if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) {
   1176                 // NOTE -- system code, won't try to do compat mode.
   1177                 Point size = new Point();
   1178                 mDisplay.getRealSize(size);
   1179                 desiredWindowWidth = size.x;
   1180                 desiredWindowHeight = size.y;
   1181             } else {
   1182                 DisplayMetrics packageMetrics =
   1183                     mView.getContext().getResources().getDisplayMetrics();
   1184                 desiredWindowWidth = packageMetrics.widthPixels;
   1185                 desiredWindowHeight = packageMetrics.heightPixels;
   1186             }
   1187 
   1188             // For the very first time, tell the view hierarchy that it
   1189             // is attached to the window.  Note that at this point the surface
   1190             // object is not initialized to its backing store, but soon it
   1191             // will be (assuming the window is visible).
   1192             attachInfo.mSurface = mSurface;
   1193             // We used to use the following condition to choose 32 bits drawing caches:
   1194             // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
   1195             // However, windows are now always 32 bits by default, so choose 32 bits
   1196             attachInfo.mUse32BitDrawingCache = true;
   1197             attachInfo.mHasWindowFocus = false;
   1198             attachInfo.mWindowVisibility = viewVisibility;
   1199             attachInfo.mRecomputeGlobalAttributes = false;
   1200             viewVisibilityChanged = false;
   1201             mLastConfiguration.setTo(host.getResources().getConfiguration());
   1202             mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
   1203             // Set the layout direction if it has not been set before (inherit is the default)
   1204             if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
   1205                 host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
   1206             }
   1207             host.dispatchAttachedToWindow(attachInfo, 0);
   1208             attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
   1209             mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
   1210             host.fitSystemWindows(mFitSystemWindowsInsets);
   1211             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
   1212 
   1213         } else {
   1214             desiredWindowWidth = frame.width();
   1215             desiredWindowHeight = frame.height();
   1216             if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
   1217                 if (DEBUG_ORIENTATION) Log.v(TAG,
   1218                         "View " + host + " resized to: " + frame);
   1219                 mFullRedrawNeeded = true;
   1220                 mLayoutRequested = true;
   1221                 windowSizeMayChange = true;
   1222             }
   1223         }
   1224 
   1225         if (viewVisibilityChanged) {
   1226             attachInfo.mWindowVisibility = viewVisibility;
   1227             host.dispatchWindowVisibilityChanged(viewVisibility);
   1228             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
   1229                 destroyHardwareResources();
   1230             }
   1231             if (viewVisibility == View.GONE) {
   1232                 // After making a window gone, we will count it as being
   1233                 // shown for the first time the next time it gets focus.
   1234                 mHasHadWindowFocus = false;
   1235             }
   1236         }
   1237 
   1238         // Execute enqueued actions on every traversal in case a detached view enqueued an action
   1239         getRunQueue().executeActions(attachInfo.mHandler);
   1240 
   1241         boolean insetsChanged = false;
   1242 
   1243         boolean layoutRequested = mLayoutRequested && !mStopped;
   1244         if (layoutRequested) {
   1245 
   1246             final Resources res = mView.getContext().getResources();
   1247 
   1248             if (mFirst) {
   1249                 // make sure touch mode code executes by setting cached value
   1250                 // to opposite of the added touch mode.
   1251                 mAttachInfo.mInTouchMode = !mAddedTouchMode;
   1252                 ensureTouchModeLocally(mAddedTouchMode);
   1253             } else {
   1254                 if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
   1255                     insetsChanged = true;
   1256                 }
   1257                 if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
   1258                     insetsChanged = true;
   1259                 }
   1260                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
   1261                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
   1262                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
   1263                             + mAttachInfo.mVisibleInsets);
   1264                 }
   1265                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
   1266                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
   1267                     windowSizeMayChange = true;
   1268 
   1269                     if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) {
   1270                         // NOTE -- system code, won't try to do compat mode.
   1271                         Point size = new Point();
   1272                         mDisplay.getRealSize(size);
   1273                         desiredWindowWidth = size.x;
   1274                         desiredWindowHeight = size.y;
   1275                     } else {
   1276                         DisplayMetrics packageMetrics = res.getDisplayMetrics();
   1277                         desiredWindowWidth = packageMetrics.widthPixels;
   1278                         desiredWindowHeight = packageMetrics.heightPixels;
   1279                     }
   1280                 }
   1281             }
   1282 
   1283             // Ask host how big it wants to be
   1284             windowSizeMayChange |= measureHierarchy(host, lp, res,
   1285                     desiredWindowWidth, desiredWindowHeight);
   1286         }
   1287 
   1288         if (collectViewAttributes()) {
   1289             params = lp;
   1290         }
   1291         if (attachInfo.mForceReportNewAttributes) {
   1292             attachInfo.mForceReportNewAttributes = false;
   1293             params = lp;
   1294         }
   1295 
   1296         if (mFirst || attachInfo.mViewVisibilityChanged) {
   1297             attachInfo.mViewVisibilityChanged = false;
   1298             int resizeMode = mSoftInputMode &
   1299                     WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
   1300             // If we are in auto resize mode, then we need to determine
   1301             // what mode to use now.
   1302             if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
   1303                 final int N = attachInfo.mScrollContainers.size();
   1304                 for (int i=0; i<N; i++) {
   1305                     if (attachInfo.mScrollContainers.get(i).isShown()) {
   1306                         resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
   1307                     }
   1308                 }
   1309                 if (resizeMode == 0) {
   1310                     resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
   1311                 }
   1312                 if ((lp.softInputMode &
   1313                         WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
   1314                     lp.softInputMode = (lp.softInputMode &
   1315                             ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
   1316                             resizeMode;
   1317                     params = lp;
   1318                 }
   1319             }
   1320         }
   1321 
   1322         if (params != null) {
   1323             if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
   1324                 if (!PixelFormat.formatHasAlpha(params.format)) {
   1325                     params.format = PixelFormat.TRANSLUCENT;
   1326                 }
   1327             }
   1328             mAttachInfo.mOverscanRequested = (params.flags
   1329                     & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
   1330         }
   1331 
   1332         if (mFitSystemWindowsRequested) {
   1333             mFitSystemWindowsRequested = false;
   1334             mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
   1335             mLastOverscanRequested = mAttachInfo.mOverscanRequested;
   1336             host.fitSystemWindows(mFitSystemWindowsInsets);
   1337             if (mLayoutRequested) {
   1338                 // Short-circuit catching a new layout request here, so
   1339                 // we don't need to go through two layout passes when things
   1340                 // change due to fitting system windows, which can happen a lot.
   1341                 windowSizeMayChange |= measureHierarchy(host, lp,
   1342                         mView.getContext().getResources(),
   1343                         desiredWindowWidth, desiredWindowHeight);
   1344             }
   1345         }
   1346 
   1347         if (layoutRequested) {
   1348             // Clear this now, so that if anything requests a layout in the
   1349             // rest of this function we will catch it and re-run a full
   1350             // layout pass.
   1351             mLayoutRequested = false;
   1352         }
   1353 
   1354         boolean windowShouldResize = layoutRequested && windowSizeMayChange
   1355             && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
   1356                 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
   1357                         frame.width() < desiredWindowWidth && frame.width() != mWidth)
   1358                 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
   1359                         frame.height() < desiredWindowHeight && frame.height() != mHeight));
   1360 
   1361         final boolean computesInternalInsets =
   1362                 attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
   1363 
   1364         boolean insetsPending = false;
   1365         int relayoutResult = 0;
   1366 
   1367         if (mFirst || windowShouldResize || insetsChanged ||
   1368                 viewVisibilityChanged || params != null) {
   1369 
   1370             if (viewVisibility == View.VISIBLE) {
   1371                 // If this window is giving internal insets to the window
   1372                 // manager, and it is being added or changing its visibility,
   1373                 // then we want to first give the window manager "fake"
   1374                 // insets to cause it to effectively ignore the content of
   1375                 // the window during layout.  This avoids it briefly causing
   1376                 // other windows to resize/move based on the raw frame of the
   1377                 // window, waiting until we can finish laying out this window
   1378                 // and get back to the window manager with the ultimately
   1379                 // computed insets.
   1380                 insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged);
   1381             }
   1382 
   1383             if (mSurfaceHolder != null) {
   1384                 mSurfaceHolder.mSurfaceLock.lock();
   1385                 mDrawingAllowed = true;
   1386             }
   1387 
   1388             boolean hwInitialized = false;
   1389             boolean contentInsetsChanged = false;
   1390             boolean hadSurface = mSurface.isValid();
   1391 
   1392             try {
   1393                 if (DEBUG_LAYOUT) {
   1394                     Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" +
   1395                             host.getMeasuredHeight() + ", params=" + params);
   1396                 }
   1397 
   1398                 final int surfaceGenerationId = mSurface.getGenerationId();
   1399                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
   1400                 if (!mDrawDuringWindowsAnimating) {
   1401                     mWindowsAnimating |=
   1402                             (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0;
   1403                 }
   1404 
   1405                 if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
   1406                         + " overscan=" + mPendingOverscanInsets.toShortString()
   1407                         + " content=" + mPendingContentInsets.toShortString()
   1408                         + " visible=" + mPendingVisibleInsets.toShortString()
   1409                         + " surface=" + mSurface);
   1410 
   1411                 if (mPendingConfiguration.seq != 0) {
   1412                     if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
   1413                             + mPendingConfiguration);
   1414                     updateConfiguration(mPendingConfiguration, !mFirst);
   1415                     mPendingConfiguration.seq = 0;
   1416                 }
   1417 
   1418                 final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals(
   1419                         mAttachInfo.mOverscanInsets);
   1420                 contentInsetsChanged = !mPendingContentInsets.equals(
   1421                         mAttachInfo.mContentInsets);
   1422                 final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
   1423                         mAttachInfo.mVisibleInsets);
   1424                 if (contentInsetsChanged) {
   1425                     if (mWidth > 0 && mHeight > 0 && lp != null &&
   1426                             ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility)
   1427                                     & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 &&
   1428                             mSurface != null && mSurface.isValid() &&
   1429                             !mAttachInfo.mTurnOffWindowResizeAnim &&
   1430                             mAttachInfo.mHardwareRenderer != null &&
   1431                             mAttachInfo.mHardwareRenderer.isEnabled() &&
   1432                             mAttachInfo.mHardwareRenderer.validate() &&
   1433                             lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
   1434 
   1435                         disposeResizeBuffer();
   1436 
   1437                         boolean completed = false;
   1438                         HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas();
   1439                         HardwareCanvas layerCanvas = null;
   1440                         try {
   1441                             if (mResizeBuffer == null) {
   1442                                 mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
   1443                                         mWidth, mHeight, false);
   1444                             } else if (mResizeBuffer.getWidth() != mWidth ||
   1445                                     mResizeBuffer.getHeight() != mHeight) {
   1446                                 mResizeBuffer.resize(mWidth, mHeight);
   1447                             }
   1448                             // TODO: should handle create/resize failure
   1449                             layerCanvas = mResizeBuffer.start(hwRendererCanvas);
   1450                             final int restoreCount = layerCanvas.save();
   1451 
   1452                             int yoff;
   1453                             final boolean scrolling = mScroller != null
   1454                                     && mScroller.computeScrollOffset();
   1455                             if (scrolling) {
   1456                                 yoff = mScroller.getCurrY();
   1457                                 mScroller.abortAnimation();
   1458                             } else {
   1459                                 yoff = mScrollY;
   1460                             }
   1461 
   1462                             layerCanvas.translate(0, -yoff);
   1463                             if (mTranslator != null) {
   1464                                 mTranslator.translateCanvas(layerCanvas);
   1465                             }
   1466 
   1467                             DisplayList displayList = mView.mDisplayList;
   1468                             if (displayList != null) {
   1469                                 layerCanvas.drawDisplayList(displayList, null,
   1470                                         DisplayList.FLAG_CLIP_CHILDREN);
   1471                             } else {
   1472                                 mView.draw(layerCanvas);
   1473                             }
   1474 
   1475                             drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
   1476 
   1477                             mResizeBufferStartTime = SystemClock.uptimeMillis();
   1478                             mResizeBufferDuration = mView.getResources().getInteger(
   1479                                     com.android.internal.R.integer.config_mediumAnimTime);
   1480                             completed = true;
   1481 
   1482                             layerCanvas.restoreToCount(restoreCount);
   1483                         } catch (OutOfMemoryError e) {
   1484                             Log.w(TAG, "Not enough memory for content change anim buffer", e);
   1485                         } finally {
   1486                             if (mResizeBuffer != null) {
   1487                                 mResizeBuffer.end(hwRendererCanvas);
   1488                                 if (!completed) {
   1489                                     mResizeBuffer.destroy();
   1490                                     mResizeBuffer = null;
   1491                                 }
   1492                             }
   1493                         }
   1494                     }
   1495                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
   1496                     if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
   1497                             + mAttachInfo.mContentInsets);
   1498                 }
   1499                 if (overscanInsetsChanged) {
   1500                     mAttachInfo.mOverscanInsets.set(mPendingOverscanInsets);
   1501                     if (DEBUG_LAYOUT) Log.v(TAG, "Overscan insets changing to: "
   1502                             + mAttachInfo.mOverscanInsets);
   1503                     // Need to relayout with content insets.
   1504                     contentInsetsChanged = true;
   1505                 }
   1506                 if (contentInsetsChanged || mLastSystemUiVisibility !=
   1507                         mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested
   1508                         || mLastOverscanRequested != mAttachInfo.mOverscanRequested) {
   1509                     mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
   1510                     mLastOverscanRequested = mAttachInfo.mOverscanRequested;
   1511                     mFitSystemWindowsRequested = false;
   1512                     mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
   1513                     host.fitSystemWindows(mFitSystemWindowsInsets);
   1514                 }
   1515                 if (visibleInsetsChanged) {
   1516                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
   1517                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
   1518                             + mAttachInfo.mVisibleInsets);
   1519                 }
   1520 
   1521                 if (!hadSurface) {
   1522                     if (mSurface.isValid()) {
   1523                         // If we are creating a new surface, then we need to
   1524                         // completely redraw it.  Also, when we get to the
   1525                         // point of drawing it we will hold off and schedule
   1526                         // a new traversal instead.  This is so we can tell the
   1527                         // window manager about all of the windows being displayed
   1528                         // before actually drawing them, so it can display then
   1529                         // all at once.
   1530                         newSurface = true;
   1531                         mFullRedrawNeeded = true;
   1532                         mPreviousTransparentRegion.setEmpty();
   1533 
   1534                         if (mAttachInfo.mHardwareRenderer != null) {
   1535                             try {
   1536                                 hwInitialized = mAttachInfo.mHardwareRenderer.initialize(
   1537                                         mHolder.getSurface());
   1538                             } catch (Surface.OutOfResourcesException e) {
   1539                                 handleOutOfResourcesException(e);
   1540                                 return;
   1541                             }
   1542                         }
   1543                     }
   1544                 } else if (!mSurface.isValid()) {
   1545                     // If the surface has been removed, then reset the scroll
   1546                     // positions.
   1547                     if (mLastScrolledFocus != null) {
   1548                         mLastScrolledFocus.clear();
   1549                     }
   1550                     mScrollY = mCurScrollY = 0;
   1551                     if (mScroller != null) {
   1552                         mScroller.abortAnimation();
   1553                     }
   1554                     disposeResizeBuffer();
   1555                     // Our surface is gone
   1556                     if (mAttachInfo.mHardwareRenderer != null &&
   1557                             mAttachInfo.mHardwareRenderer.isEnabled()) {
   1558                         mAttachInfo.mHardwareRenderer.destroy(true);
   1559                     }
   1560                 } else if (surfaceGenerationId != mSurface.getGenerationId() &&
   1561                         mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) {
   1562                     mFullRedrawNeeded = true;
   1563                     try {
   1564                         mAttachInfo.mHardwareRenderer.updateSurface(mHolder.getSurface());
   1565                     } catch (Surface.OutOfResourcesException e) {
   1566                         handleOutOfResourcesException(e);
   1567                         return;
   1568                     }
   1569                 }
   1570             } catch (RemoteException e) {
   1571             }
   1572 
   1573             if (DEBUG_ORIENTATION) Log.v(
   1574                     TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
   1575 
   1576             attachInfo.mWindowLeft = frame.left;
   1577             attachInfo.mWindowTop = frame.top;
   1578 
   1579             // !!FIXME!! This next section handles the case where we did not get the
   1580             // window size we asked for. We should avoid this by getting a maximum size from
   1581             // the window session beforehand.
   1582             if (mWidth != frame.width() || mHeight != frame.height()) {
   1583                 mWidth = frame.width();
   1584                 mHeight = frame.height();
   1585             }
   1586 
   1587             if (mSurfaceHolder != null) {
   1588                 // The app owns the surface; tell it about what is going on.
   1589                 if (mSurface.isValid()) {
   1590                     // XXX .copyFrom() doesn't work!
   1591                     //mSurfaceHolder.mSurface.copyFrom(mSurface);
   1592                     mSurfaceHolder.mSurface = mSurface;
   1593                 }
   1594                 mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
   1595                 mSurfaceHolder.mSurfaceLock.unlock();
   1596                 if (mSurface.isValid()) {
   1597                     if (!hadSurface) {
   1598                         mSurfaceHolder.ungetCallbacks();
   1599 
   1600                         mIsCreating = true;
   1601                         mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
   1602                         SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   1603                         if (callbacks != null) {
   1604                             for (SurfaceHolder.Callback c : callbacks) {
   1605                                 c.surfaceCreated(mSurfaceHolder);
   1606                             }
   1607                         }
   1608                         surfaceChanged = true;
   1609                     }
   1610                     if (surfaceChanged) {
   1611                         mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
   1612                                 lp.format, mWidth, mHeight);
   1613                         SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   1614                         if (callbacks != null) {
   1615                             for (SurfaceHolder.Callback c : callbacks) {
   1616                                 c.surfaceChanged(mSurfaceHolder, lp.format,
   1617                                         mWidth, mHeight);
   1618                             }
   1619                         }
   1620                     }
   1621                     mIsCreating = false;
   1622                 } else if (hadSurface) {
   1623                     mSurfaceHolder.ungetCallbacks();
   1624                     SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   1625                     mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
   1626                     if (callbacks != null) {
   1627                         for (SurfaceHolder.Callback c : callbacks) {
   1628                             c.surfaceDestroyed(mSurfaceHolder);
   1629                         }
   1630                     }
   1631                     mSurfaceHolder.mSurfaceLock.lock();
   1632                     try {
   1633                         mSurfaceHolder.mSurface = new Surface();
   1634                     } finally {
   1635                         mSurfaceHolder.mSurfaceLock.unlock();
   1636                     }
   1637                 }
   1638             }
   1639 
   1640             if (mAttachInfo.mHardwareRenderer != null &&
   1641                     mAttachInfo.mHardwareRenderer.isEnabled()) {
   1642                 if (hwInitialized ||
   1643                         mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
   1644                         mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
   1645                     mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
   1646                     if (!hwInitialized) {
   1647                         mAttachInfo.mHardwareRenderer.invalidate(mHolder.getSurface());
   1648                         mFullRedrawNeeded = true;
   1649                     }
   1650                 }
   1651             }
   1652 
   1653             if (!mStopped) {
   1654                 boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
   1655                         (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
   1656                 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
   1657                         || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
   1658                     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
   1659                     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
   1660 
   1661                     if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="
   1662                             + mWidth + " measuredWidth=" + host.getMeasuredWidth()
   1663                             + " mHeight=" + mHeight
   1664                             + " measuredHeight=" + host.getMeasuredHeight()
   1665                             + " coveredInsetsChanged=" + contentInsetsChanged);
   1666 
   1667                      // Ask host how big it wants to be
   1668                     performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   1669 
   1670                     // Implementation of weights from WindowManager.LayoutParams
   1671                     // We just grow the dimensions as needed and re-measure if
   1672                     // needs be
   1673                     int width = host.getMeasuredWidth();
   1674                     int height = host.getMeasuredHeight();
   1675                     boolean measureAgain = false;
   1676 
   1677                     if (lp.horizontalWeight > 0.0f) {
   1678                         width += (int) ((mWidth - width) * lp.horizontalWeight);
   1679                         childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
   1680                                 MeasureSpec.EXACTLY);
   1681                         measureAgain = true;
   1682                     }
   1683                     if (lp.verticalWeight > 0.0f) {
   1684                         height += (int) ((mHeight - height) * lp.verticalWeight);
   1685                         childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
   1686                                 MeasureSpec.EXACTLY);
   1687                         measureAgain = true;
   1688                     }
   1689 
   1690                     if (measureAgain) {
   1691                         if (DEBUG_LAYOUT) Log.v(TAG,
   1692                                 "And hey let's measure once more: width=" + width
   1693                                 + " height=" + height);
   1694                         performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   1695                     }
   1696 
   1697                     layoutRequested = true;
   1698                 }
   1699             }
   1700         } else {
   1701             // Not the first pass and no window/insets/visibility change but the window
   1702             // may have moved and we need check that and if so to update the left and right
   1703             // in the attach info. We translate only the window frame since on window move
   1704             // the window manager tells us only for the new frame but the insets are the
   1705             // same and we do not want to translate them more than once.
   1706 
   1707             // TODO: Well, we are checking whether the frame has changed similarly
   1708             // to how this is done for the insets. This is however incorrect since
   1709             // the insets and the frame are translated. For example, the old frame
   1710             // was (1, 1 - 1, 1) and was translated to say (2, 2 - 2, 2), now the new
   1711             // reported frame is (2, 2 - 2, 2) which implies no change but this is not
   1712             // true since we are comparing a not translated value to a translated one.
   1713             // This scenario is rare but we may want to fix that.
   1714 
   1715             final boolean windowMoved = (attachInfo.mWindowLeft != frame.left
   1716                     || attachInfo.mWindowTop != frame.top);
   1717             if (windowMoved) {
   1718                 if (mTranslator != null) {
   1719                     mTranslator.translateRectInScreenToAppWinFrame(frame);
   1720                 }
   1721                 attachInfo.mWindowLeft = frame.left;
   1722                 attachInfo.mWindowTop = frame.top;
   1723             }
   1724         }
   1725 
   1726         final boolean didLayout = layoutRequested && !mStopped;
   1727         boolean triggerGlobalLayoutListener = didLayout
   1728                 || attachInfo.mRecomputeGlobalAttributes;
   1729         if (didLayout) {
   1730             performLayout(lp, desiredWindowWidth, desiredWindowHeight);
   1731 
   1732             // By this point all views have been sized and positioned
   1733             // We can compute the transparent area
   1734 
   1735             if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
   1736                 // start out transparent
   1737                 // TODO: AVOID THAT CALL BY CACHING THE RESULT?
   1738                 host.getLocationInWindow(mTmpLocation);
   1739                 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
   1740                         mTmpLocation[0] + host.mRight - host.mLeft,
   1741                         mTmpLocation[1] + host.mBottom - host.mTop);
   1742 
   1743                 host.gatherTransparentRegion(mTransparentRegion);
   1744                 if (mTranslator != null) {
   1745                     mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
   1746                 }
   1747 
   1748                 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
   1749                     mPreviousTransparentRegion.set(mTransparentRegion);
   1750                     mFullRedrawNeeded = true;
   1751                     // reconfigure window manager
   1752                     try {
   1753                         mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
   1754                     } catch (RemoteException e) {
   1755                     }
   1756                 }
   1757             }
   1758 
   1759             if (DBG) {
   1760                 System.out.println("======================================");
   1761                 System.out.println("performTraversals -- after setFrame");
   1762                 host.debug();
   1763             }
   1764         }
   1765 
   1766         if (triggerGlobalLayoutListener) {
   1767             attachInfo.mRecomputeGlobalAttributes = false;
   1768             attachInfo.mTreeObserver.dispatchOnGlobalLayout();
   1769 
   1770             if (AccessibilityManager.getInstance(host.mContext).isEnabled()) {
   1771                 postSendWindowContentChangedCallback(mView);
   1772             }
   1773         }
   1774 
   1775         if (computesInternalInsets) {
   1776             // Clear the original insets.
   1777             final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
   1778             insets.reset();
   1779 
   1780             // Compute new insets in place.
   1781             attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
   1782 
   1783             // Tell the window manager.
   1784             if (insetsPending || !mLastGivenInsets.equals(insets)) {
   1785                 mLastGivenInsets.set(insets);
   1786 
   1787                 // Translate insets to screen coordinates if needed.
   1788                 final Rect contentInsets;
   1789                 final Rect visibleInsets;
   1790                 final Region touchableRegion;
   1791                 if (mTranslator != null) {
   1792                     contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
   1793                     visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
   1794                     touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion);
   1795                 } else {
   1796                     contentInsets = insets.contentInsets;
   1797                     visibleInsets = insets.visibleInsets;
   1798                     touchableRegion = insets.touchableRegion;
   1799                 }
   1800 
   1801                 try {
   1802                     mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
   1803                             contentInsets, visibleInsets, touchableRegion);
   1804                 } catch (RemoteException e) {
   1805                 }
   1806             }
   1807         }
   1808 
   1809         boolean skipDraw = false;
   1810 
   1811         if (mFirst) {
   1812             // handle first focus request
   1813             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
   1814                     + mView.hasFocus());
   1815             if (mView != null) {
   1816                 if (!mView.hasFocus()) {
   1817                     mView.requestFocus(View.FOCUS_FORWARD);
   1818                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
   1819                             + mView.findFocus());
   1820                 } else {
   1821                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
   1822                             + mView.findFocus());
   1823                 }
   1824             }
   1825             if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) {
   1826                 // The first time we relayout the window, if the system is
   1827                 // doing window animations, we want to hold of on any future
   1828                 // draws until the animation is done.
   1829                 mWindowsAnimating = true;
   1830             }
   1831         } else if (mWindowsAnimating) {
   1832             skipDraw = true;
   1833         }
   1834 
   1835         mFirst = false;
   1836         mWillDrawSoon = false;
   1837         mNewSurfaceNeeded = false;
   1838         mViewVisibility = viewVisibility;
   1839 
   1840         if (mAttachInfo.mHasWindowFocus) {
   1841             final boolean imTarget = WindowManager.LayoutParams
   1842                     .mayUseInputMethod(mWindowAttributes.flags);
   1843             if (imTarget != mLastWasImTarget) {
   1844                 mLastWasImTarget = imTarget;
   1845                 InputMethodManager imm = InputMethodManager.peekInstance();
   1846                 if (imm != null && imTarget) {
   1847                     imm.startGettingWindowFocus(mView);
   1848                     imm.onWindowFocus(mView, mView.findFocus(),
   1849                             mWindowAttributes.softInputMode,
   1850                             !mHasHadWindowFocus, mWindowAttributes.flags);
   1851                 }
   1852             }
   1853         }
   1854 
   1855         // Remember if we must report the next draw.
   1856         if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
   1857             mReportNextDraw = true;
   1858         }
   1859 
   1860         boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
   1861                 viewVisibility != View.VISIBLE;
   1862 
   1863         if (!cancelDraw && !newSurface) {
   1864             if (!skipDraw || mReportNextDraw) {
   1865                 if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
   1866                     for (int i = 0; i < mPendingTransitions.size(); ++i) {
   1867                         mPendingTransitions.get(i).startChangingAnimations();
   1868                     }
   1869                     mPendingTransitions.clear();
   1870                 }
   1871 
   1872                 performDraw();
   1873             }
   1874         } else {
   1875             if (viewVisibility == View.VISIBLE) {
   1876                 // Try again
   1877                 scheduleTraversals();
   1878             } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
   1879                 for (int i = 0; i < mPendingTransitions.size(); ++i) {
   1880                     mPendingTransitions.get(i).endChangingAnimations();
   1881                 }
   1882                 mPendingTransitions.clear();
   1883             }
   1884         }
   1885 
   1886         mIsInTraversal = false;
   1887     }
   1888 
   1889     private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
   1890         Log.e(TAG, "OutOfResourcesException initializing HW surface", e);
   1891         try {
   1892             if (!mWindowSession.outOfMemory(mWindow) &&
   1893                     Process.myUid() != Process.SYSTEM_UID) {
   1894                 Slog.w(TAG, "No processes killed for memory; killing self");
   1895                 Process.killProcess(Process.myPid());
   1896             }
   1897         } catch (RemoteException ex) {
   1898         }
   1899         mLayoutRequested = true;    // ask wm for a new surface next time.
   1900     }
   1901 
   1902     private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
   1903         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
   1904         try {
   1905             mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
   1906         } finally {
   1907             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
   1908         }
   1909     }
   1910 
   1911     /**
   1912      * Called by {@link android.view.View#isInLayout()} to determine whether the view hierarchy
   1913      * is currently undergoing a layout pass.
   1914      *
   1915      * @return whether the view hierarchy is currently undergoing a layout pass
   1916      */
   1917     boolean isInLayout() {
   1918         return mInLayout;
   1919     }
   1920 
   1921     /**
   1922      * Called by {@link android.view.View#requestLayout()} if the view hierarchy is currently
   1923      * undergoing a layout pass. requestLayout() should not generally be called during layout,
   1924      * unless the container hierarchy knows what it is doing (i.e., it is fine as long as
   1925      * all children in that container hierarchy are measured and laid out at the end of the layout
   1926      * pass for that container). If requestLayout() is called anyway, we handle it correctly
   1927      * by registering all requesters during a frame as it proceeds. At the end of the frame,
   1928      * we check all of those views to see if any still have pending layout requests, which
   1929      * indicates that they were not correctly handled by their container hierarchy. If that is
   1930      * the case, we clear all such flags in the tree, to remove the buggy flag state that leads
   1931      * to blank containers, and force a second request/measure/layout pass in this frame. If
   1932      * more requestLayout() calls are received during that second layout pass, we post those
   1933      * requests to the next frame to avoid possible infinite loops.
   1934      *
   1935      * <p>The return value from this method indicates whether the request should proceed
   1936      * (if it is a request during the first layout pass) or should be skipped and posted to the
   1937      * next frame (if it is a request during the second layout pass).</p>
   1938      *
   1939      * @param view the view that requested the layout.
   1940      *
   1941      * @return true if request should proceed, false otherwise.
   1942      */
   1943     boolean requestLayoutDuringLayout(final View view) {
   1944         if (view.mParent == null || view.mAttachInfo == null) {
   1945             // Would not normally trigger another layout, so just let it pass through as usual
   1946             return true;
   1947         }
   1948         if (!mLayoutRequesters.contains(view)) {
   1949             mLayoutRequesters.add(view);
   1950         }
   1951         if (!mHandlingLayoutInLayoutRequest) {
   1952             // Let the request proceed normally; it will be processed in a second layout pass
   1953             // if necessary
   1954             return true;
   1955         } else {
   1956             // Don't let the request proceed during the second layout pass.
   1957             // It will post to the next frame instead.
   1958             return false;
   1959         }
   1960     }
   1961 
   1962     private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
   1963             int desiredWindowHeight) {
   1964         mLayoutRequested = false;
   1965         mScrollMayChange = true;
   1966         mInLayout = true;
   1967 
   1968         final View host = mView;
   1969         if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
   1970             Log.v(TAG, "Laying out " + host + " to (" +
   1971                     host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
   1972         }
   1973 
   1974         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
   1975         try {
   1976             host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
   1977 
   1978             mInLayout = false;
   1979             int numViewsRequestingLayout = mLayoutRequesters.size();
   1980             if (numViewsRequestingLayout > 0) {
   1981                 // requestLayout() was called during layout.
   1982                 // If no layout-request flags are set on the requesting views, there is no problem.
   1983                 // If some requests are still pending, then we need to clear those flags and do
   1984                 // a full request/measure/layout pass to handle this situation.
   1985                 ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
   1986                         false);
   1987                 if (validLayoutRequesters != null) {
   1988                     // Set this flag to indicate that any further requests are happening during
   1989                     // the second pass, which may result in posting those requests to the next
   1990                     // frame instead
   1991                     mHandlingLayoutInLayoutRequest = true;
   1992 
   1993                     // Process fresh layout requests, then measure and layout
   1994                     int numValidRequests = validLayoutRequesters.size();
   1995                     for (int i = 0; i < numValidRequests; ++i) {
   1996                         final View view = validLayoutRequesters.get(i);
   1997                         Log.w("View", "requestLayout() improperly called by " + view +
   1998                                 " during layout: running second layout pass");
   1999                         view.requestLayout();
   2000                     }
   2001                     measureHierarchy(host, lp, mView.getContext().getResources(),
   2002                             desiredWindowWidth, desiredWindowHeight);
   2003                     mInLayout = true;
   2004                     host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
   2005 
   2006                     mHandlingLayoutInLayoutRequest = false;
   2007 
   2008                     // Check the valid requests again, this time without checking/clearing the
   2009                     // layout flags, since requests happening during the second pass get noop'd
   2010                     validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
   2011                     if (validLayoutRequesters != null) {
   2012                         final ArrayList<View> finalRequesters = validLayoutRequesters;
   2013                         // Post second-pass requests to the next frame
   2014                         getRunQueue().post(new Runnable() {
   2015                             @Override
   2016                             public void run() {
   2017                                 int numValidRequests = finalRequesters.size();
   2018                                 for (int i = 0; i < numValidRequests; ++i) {
   2019                                     final View view = finalRequesters.get(i);
   2020                                     Log.w("View", "requestLayout() improperly called by " + view +
   2021                                             " during second layout pass: posting in next frame");
   2022                                     view.requestLayout();
   2023                                 }
   2024                             }
   2025                         });
   2026                     }
   2027                 }
   2028 
   2029             }
   2030         } finally {
   2031             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
   2032         }
   2033         mInLayout = false;
   2034     }
   2035 
   2036     /**
   2037      * This method is called during layout when there have been calls to requestLayout() during
   2038      * layout. It walks through the list of views that requested layout to determine which ones
   2039      * still need it, based on visibility in the hierarchy and whether they have already been
   2040      * handled (as is usually the case with ListView children).
   2041      *
   2042      * @param layoutRequesters The list of views that requested layout during layout
   2043      * @param secondLayoutRequests Whether the requests were issued during the second layout pass.
   2044      * If so, the FORCE_LAYOUT flag was not set on requesters.
   2045      * @return A list of the actual views that still need to be laid out.
   2046      */
   2047     private ArrayList<View> getValidLayoutRequesters(ArrayList<View> layoutRequesters,
   2048             boolean secondLayoutRequests) {
   2049 
   2050         int numViewsRequestingLayout = layoutRequesters.size();
   2051         ArrayList<View> validLayoutRequesters = null;
   2052         for (int i = 0; i < numViewsRequestingLayout; ++i) {
   2053             View view = layoutRequesters.get(i);
   2054             if (view != null && view.mAttachInfo != null && view.mParent != null &&
   2055                     (secondLayoutRequests || (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) ==
   2056                             View.PFLAG_FORCE_LAYOUT)) {
   2057                 boolean gone = false;
   2058                 View parent = view;
   2059                 // Only trigger new requests for views in a non-GONE hierarchy
   2060                 while (parent != null) {
   2061                     if ((parent.mViewFlags & View.VISIBILITY_MASK) == View.GONE) {
   2062                         gone = true;
   2063                         break;
   2064                     }
   2065                     if (parent.mParent instanceof View) {
   2066                         parent = (View) parent.mParent;
   2067                     } else {
   2068                         parent = null;
   2069                     }
   2070                 }
   2071                 if (!gone) {
   2072                     if (validLayoutRequesters == null) {
   2073                         validLayoutRequesters = new ArrayList<View>();
   2074                     }
   2075                     validLayoutRequesters.add(view);
   2076                 }
   2077             }
   2078         }
   2079         if (!secondLayoutRequests) {
   2080             // If we're checking the layout flags, then we need to clean them up also
   2081             for (int i = 0; i < numViewsRequestingLayout; ++i) {
   2082                 View view = layoutRequesters.get(i);
   2083                 while (view != null &&
   2084                         (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) {
   2085                     view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
   2086                     if (view.mParent instanceof View) {
   2087                         view = (View) view.mParent;
   2088                     } else {
   2089                         view = null;
   2090                     }
   2091                 }
   2092             }
   2093         }
   2094         layoutRequesters.clear();
   2095         return validLayoutRequesters;
   2096     }
   2097 
   2098     public void requestTransparentRegion(View child) {
   2099         // the test below should not fail unless someone is messing with us
   2100         checkThread();
   2101         if (mView == child) {
   2102             mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
   2103             // Need to make sure we re-evaluate the window attributes next
   2104             // time around, to ensure the window has the correct format.
   2105             mWindowAttributesChanged = true;
   2106             mWindowAttributesChangesFlag = 0;
   2107             requestLayout();
   2108         }
   2109     }
   2110 
   2111     /**
   2112      * Figures out the measure spec for the root view in a window based on it's
   2113      * layout params.
   2114      *
   2115      * @param windowSize
   2116      *            The available width or height of the window
   2117      *
   2118      * @param rootDimension
   2119      *            The layout params for one dimension (width or height) of the
   2120      *            window.
   2121      *
   2122      * @return The measure spec to use to measure the root view.
   2123      */
   2124     private static int getRootMeasureSpec(int windowSize, int rootDimension) {
   2125         int measureSpec;
   2126         switch (rootDimension) {
   2127 
   2128         case ViewGroup.LayoutParams.MATCH_PARENT:
   2129             // Window can't resize. Force root view to be windowSize.
   2130             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
   2131             break;
   2132         case ViewGroup.LayoutParams.WRAP_CONTENT:
   2133             // Window can resize. Set max size for root view.
   2134             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
   2135             break;
   2136         default:
   2137             // Window wants to be an exact size. Force root view to be that size.
   2138             measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
   2139             break;
   2140         }
   2141         return measureSpec;
   2142     }
   2143 
   2144     int mHardwareYOffset;
   2145     int mResizeAlpha;
   2146     final Paint mResizePaint = new Paint();
   2147 
   2148     public void onHardwarePreDraw(HardwareCanvas canvas) {
   2149         canvas.translate(0, -mHardwareYOffset);
   2150     }
   2151 
   2152     public void onHardwarePostDraw(HardwareCanvas canvas) {
   2153         if (mResizeBuffer != null) {
   2154             mResizePaint.setAlpha(mResizeAlpha);
   2155             canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
   2156         }
   2157         drawAccessibilityFocusedDrawableIfNeeded(canvas);
   2158     }
   2159 
   2160     /**
   2161      * @hide
   2162      */
   2163     void outputDisplayList(View view) {
   2164         if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) {
   2165             DisplayList displayList = view.getDisplayList();
   2166             if (displayList != null) {
   2167                 mAttachInfo.mHardwareCanvas.outputDisplayList(displayList);
   2168             }
   2169         }
   2170     }
   2171 
   2172     /**
   2173      * @see #PROPERTY_PROFILE_RENDERING
   2174      */
   2175     private void profileRendering(boolean enabled) {
   2176         if (mProfileRendering) {
   2177             mRenderProfilingEnabled = enabled;
   2178 
   2179             if (mRenderProfiler != null) {
   2180                 mChoreographer.removeFrameCallback(mRenderProfiler);
   2181             }
   2182             if (mRenderProfilingEnabled) {
   2183                 if (mRenderProfiler == null) {
   2184                     mRenderProfiler = new Choreographer.FrameCallback() {
   2185                         @Override
   2186                         public void doFrame(long frameTimeNanos) {
   2187                             mDirty.set(0, 0, mWidth, mHeight);
   2188                             scheduleTraversals();
   2189                             if (mRenderProfilingEnabled) {
   2190                                 mChoreographer.postFrameCallback(mRenderProfiler);
   2191                             }
   2192                         }
   2193                     };
   2194                 }
   2195                 mChoreographer.postFrameCallback(mRenderProfiler);
   2196             } else {
   2197                 mRenderProfiler = null;
   2198             }
   2199         }
   2200     }
   2201 
   2202     /**
   2203      * Called from draw() when DEBUG_FPS is enabled
   2204      */
   2205     private void trackFPS() {
   2206         // Tracks frames per second drawn. First value in a series of draws may be bogus
   2207         // because it down not account for the intervening idle time
   2208         long nowTime = System.currentTimeMillis();
   2209         if (mFpsStartTime < 0) {
   2210             mFpsStartTime = mFpsPrevTime = nowTime;
   2211             mFpsNumFrames = 0;
   2212         } else {
   2213             ++mFpsNumFrames;
   2214             String thisHash = Integer.toHexString(System.identityHashCode(this));
   2215             long frameTime = nowTime - mFpsPrevTime;
   2216             long totalTime = nowTime - mFpsStartTime;
   2217             Log.v(TAG, "0x" + thisHash + "\tFrame time:\t" + frameTime);
   2218             mFpsPrevTime = nowTime;
   2219             if (totalTime > 1000) {
   2220                 float fps = (float) mFpsNumFrames * 1000 / totalTime;
   2221                 Log.v(TAG, "0x" + thisHash + "\tFPS:\t" + fps);
   2222                 mFpsStartTime = nowTime;
   2223                 mFpsNumFrames = 0;
   2224             }
   2225         }
   2226     }
   2227 
   2228     private void performDraw() {
   2229         if (!mAttachInfo.mScreenOn && !mReportNextDraw) {
   2230             return;
   2231         }
   2232 
   2233         final boolean fullRedrawNeeded = mFullRedrawNeeded;
   2234         mFullRedrawNeeded = false;
   2235 
   2236         mIsDrawing = true;
   2237         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
   2238         try {
   2239             draw(fullRedrawNeeded);
   2240         } finally {
   2241             mIsDrawing = false;
   2242             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
   2243         }
   2244 
   2245         if (mReportNextDraw) {
   2246             mReportNextDraw = false;
   2247 
   2248             if (LOCAL_LOGV) {
   2249                 Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
   2250             }
   2251             if (mSurfaceHolder != null && mSurface.isValid()) {
   2252                 mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
   2253                 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   2254                 if (callbacks != null) {
   2255                     for (SurfaceHolder.Callback c : callbacks) {
   2256                         if (c instanceof SurfaceHolder.Callback2) {
   2257                             ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
   2258                                     mSurfaceHolder);
   2259                         }
   2260                     }
   2261                 }
   2262             }
   2263             try {
   2264                 mWindowSession.finishDrawing(mWindow);
   2265             } catch (RemoteException e) {
   2266             }
   2267         }
   2268     }
   2269 
   2270     private void draw(boolean fullRedrawNeeded) {
   2271         Surface surface = mSurface;
   2272         if (!surface.isValid()) {
   2273             return;
   2274         }
   2275 
   2276         if (DEBUG_FPS) {
   2277             trackFPS();
   2278         }
   2279 
   2280         if (!sFirstDrawComplete) {
   2281             synchronized (sFirstDrawHandlers) {
   2282                 sFirstDrawComplete = true;
   2283                 final int count = sFirstDrawHandlers.size();
   2284                 for (int i = 0; i< count; i++) {
   2285                     mHandler.post(sFirstDrawHandlers.get(i));
   2286                 }
   2287             }
   2288         }
   2289 
   2290         scrollToRectOrFocus(null, false);
   2291 
   2292         final AttachInfo attachInfo = mAttachInfo;
   2293         if (attachInfo.mViewScrollChanged) {
   2294             attachInfo.mViewScrollChanged = false;
   2295             attachInfo.mTreeObserver.dispatchOnScrollChanged();
   2296         }
   2297 
   2298         int yoff;
   2299         boolean animating = mScroller != null && mScroller.computeScrollOffset();
   2300         if (animating) {
   2301             yoff = mScroller.getCurrY();
   2302         } else {
   2303             yoff = mScrollY;
   2304         }
   2305         if (mCurScrollY != yoff) {
   2306             mCurScrollY = yoff;
   2307             fullRedrawNeeded = true;
   2308         }
   2309 
   2310         final float appScale = attachInfo.mApplicationScale;
   2311         final boolean scalingRequired = attachInfo.mScalingRequired;
   2312 
   2313         int resizeAlpha = 0;
   2314         if (mResizeBuffer != null) {
   2315             long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime;
   2316             if (deltaTime < mResizeBufferDuration) {
   2317                 float amt = deltaTime/(float) mResizeBufferDuration;
   2318                 amt = mResizeInterpolator.getInterpolation(amt);
   2319                 animating = true;
   2320                 resizeAlpha = 255 - (int)(amt*255);
   2321             } else {
   2322                 disposeResizeBuffer();
   2323             }
   2324         }
   2325 
   2326         final Rect dirty = mDirty;
   2327         if (mSurfaceHolder != null) {
   2328             // The app owns the surface, we won't draw.
   2329             dirty.setEmpty();
   2330             if (animating) {
   2331                 if (mScroller != null) {
   2332                     mScroller.abortAnimation();
   2333                 }
   2334                 disposeResizeBuffer();
   2335             }
   2336             return;
   2337         }
   2338 
   2339         if (fullRedrawNeeded) {
   2340             attachInfo.mIgnoreDirtyState = true;
   2341             dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
   2342         }
   2343 
   2344         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
   2345             Log.v(TAG, "Draw " + mView + "/"
   2346                     + mWindowAttributes.getTitle()
   2347                     + ": dirty={" + dirty.left + "," + dirty.top
   2348                     + "," + dirty.right + "," + dirty.bottom + "} surface="
   2349                     + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
   2350                     appScale + ", width=" + mWidth + ", height=" + mHeight);
   2351         }
   2352 
   2353         invalidateDisplayLists();
   2354 
   2355         attachInfo.mTreeObserver.dispatchOnDraw();
   2356 
   2357         if (!dirty.isEmpty() || mIsAnimating) {
   2358             if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
   2359                 // Draw with hardware renderer.
   2360                 mIsAnimating = false;
   2361                 mHardwareYOffset = yoff;
   2362                 mResizeAlpha = resizeAlpha;
   2363 
   2364                 mCurrentDirty.set(dirty);
   2365                 dirty.setEmpty();
   2366 
   2367                 attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
   2368                         animating ? null : mCurrentDirty);
   2369             } else {
   2370                 // If we get here with a disabled & requested hardware renderer, something went
   2371                 // wrong (an invalidate posted right before we destroyed the hardware surface
   2372                 // for instance) so we should just bail out. Locking the surface with software
   2373                 // rendering at this point would lock it forever and prevent hardware renderer
   2374                 // from doing its job when it comes back.
   2375                 // Before we request a new frame we must however attempt to reinitiliaze the
   2376                 // hardware renderer if it's in requested state. This would happen after an
   2377                 // eglTerminate() for instance.
   2378                 if (attachInfo.mHardwareRenderer != null &&
   2379                         !attachInfo.mHardwareRenderer.isEnabled() &&
   2380                         attachInfo.mHardwareRenderer.isRequested()) {
   2381 
   2382                     try {
   2383                         attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
   2384                                 mHolder.getSurface());
   2385                     } catch (Surface.OutOfResourcesException e) {
   2386                         handleOutOfResourcesException(e);
   2387                         return;
   2388                     }
   2389 
   2390                     mFullRedrawNeeded = true;
   2391                     scheduleTraversals();
   2392                     return;
   2393                 }
   2394 
   2395                 if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
   2396                     return;
   2397                 }
   2398             }
   2399         }
   2400 
   2401         if (animating) {
   2402             mFullRedrawNeeded = true;
   2403             scheduleTraversals();
   2404         }
   2405     }
   2406 
   2407     /**
   2408      * @return true if drawing was succesfull, false if an error occurred
   2409      */
   2410     private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
   2411             boolean scalingRequired, Rect dirty) {
   2412 
   2413         // Draw with software renderer.
   2414         Canvas canvas;
   2415         try {
   2416             int left = dirty.left;
   2417             int top = dirty.top;
   2418             int right = dirty.right;
   2419             int bottom = dirty.bottom;
   2420 
   2421             canvas = mSurface.lockCanvas(dirty);
   2422 
   2423             // The dirty rectangle can be modified by Surface.lockCanvas()
   2424             //noinspection ConstantConditions
   2425             if (left != dirty.left || top != dirty.top || right != dirty.right ||
   2426                     bottom != dirty.bottom) {
   2427                 attachInfo.mIgnoreDirtyState = true;
   2428             }
   2429 
   2430             // TODO: Do this in native
   2431             canvas.setDensity(mDensity);
   2432         } catch (Surface.OutOfResourcesException e) {
   2433             handleOutOfResourcesException(e);
   2434             return false;
   2435         } catch (IllegalArgumentException e) {
   2436             Log.e(TAG, "Could not lock surface", e);
   2437             // Don't assume this is due to out of memory, it could be
   2438             // something else, and if it is something else then we could
   2439             // kill stuff (or ourself) for no reason.
   2440             mLayoutRequested = true;    // ask wm for a new surface next time.
   2441             return false;
   2442         }
   2443 
   2444         try {
   2445             if (DEBUG_ORIENTATION || DEBUG_DRAW) {
   2446                 Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
   2447                         + canvas.getWidth() + ", h=" + canvas.getHeight());
   2448                 //canvas.drawARGB(255, 255, 0, 0);
   2449             }
   2450 
   2451             // If this bitmap's format includes an alpha channel, we
   2452             // need to clear it before drawing so that the child will
   2453             // properly re-composite its drawing on a transparent
   2454             // background. This automatically respects the clip/dirty region
   2455             // or
   2456             // If we are applying an offset, we need to clear the area
   2457             // where the offset doesn't appear to avoid having garbage
   2458             // left in the blank areas.
   2459             if (!canvas.isOpaque() || yoff != 0) {
   2460                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
   2461             }
   2462 
   2463             dirty.setEmpty();
   2464             mIsAnimating = false;
   2465             attachInfo.mDrawingTime = SystemClock.uptimeMillis();
   2466             mView.mPrivateFlags |= View.PFLAG_DRAWN;
   2467 
   2468             if (DEBUG_DRAW) {
   2469                 Context cxt = mView.getContext();
   2470                 Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
   2471                         ", metrics=" + cxt.getResources().getDisplayMetrics() +
   2472                         ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
   2473             }
   2474             try {
   2475                 canvas.translate(0, -yoff);
   2476                 if (mTranslator != null) {
   2477                     mTranslator.translateCanvas(canvas);
   2478                 }
   2479                 canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
   2480                 attachInfo.mSetIgnoreDirtyState = false;
   2481 
   2482                 mView.draw(canvas);
   2483 
   2484                 drawAccessibilityFocusedDrawableIfNeeded(canvas);
   2485             } finally {
   2486                 if (!attachInfo.mSetIgnoreDirtyState) {
   2487                     // Only clear the flag if it was not set during the mView.draw() call
   2488                     attachInfo.mIgnoreDirtyState = false;
   2489                 }
   2490             }
   2491         } finally {
   2492             try {
   2493                 surface.unlockCanvasAndPost(canvas);
   2494             } catch (IllegalArgumentException e) {
   2495                 Log.e(TAG, "Could not unlock surface", e);
   2496                 mLayoutRequested = true;    // ask wm for a new surface next time.
   2497                 //noinspection ReturnInsideFinallyBlock
   2498                 return false;
   2499             }
   2500 
   2501             if (LOCAL_LOGV) {
   2502                 Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
   2503             }
   2504         }
   2505         return true;
   2506     }
   2507 
   2508     /**
   2509      * We want to draw a highlight around the current accessibility focused.
   2510      * Since adding a style for all possible view is not a viable option we
   2511      * have this specialized drawing method.
   2512      *
   2513      * Note: We are doing this here to be able to draw the highlight for
   2514      *       virtual views in addition to real ones.
   2515      *
   2516      * @param canvas The canvas on which to draw.
   2517      */
   2518     private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) {
   2519         AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext);
   2520         if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
   2521             return;
   2522         }
   2523         if (mAccessibilityFocusedHost == null || mAccessibilityFocusedHost.mAttachInfo == null) {
   2524             return;
   2525         }
   2526         Drawable drawable = getAccessibilityFocusedDrawable();
   2527         if (drawable == null) {
   2528             return;
   2529         }
   2530         AccessibilityNodeProvider provider =
   2531             mAccessibilityFocusedHost.getAccessibilityNodeProvider();
   2532         Rect bounds = mView.mAttachInfo.mTmpInvalRect;
   2533         if (provider == null) {
   2534             mAccessibilityFocusedHost.getBoundsOnScreen(bounds);
   2535         } else {
   2536             if (mAccessibilityFocusedVirtualView == null) {
   2537                 return;
   2538             }
   2539             mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
   2540         }
   2541         bounds.offset(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
   2542         bounds.intersect(0, 0, mAttachInfo.mViewRootImpl.mWidth, mAttachInfo.mViewRootImpl.mHeight);
   2543         drawable.setBounds(bounds);
   2544         drawable.draw(canvas);
   2545     }
   2546 
   2547     private Drawable getAccessibilityFocusedDrawable() {
   2548         if (mAttachInfo != null) {
   2549             // Lazily load the accessibility focus drawable.
   2550             if (mAttachInfo.mAccessibilityFocusDrawable == null) {
   2551                 TypedValue value = new TypedValue();
   2552                 final boolean resolved = mView.mContext.getTheme().resolveAttribute(
   2553                         R.attr.accessibilityFocusedDrawable, value, true);
   2554                 if (resolved) {
   2555                     mAttachInfo.mAccessibilityFocusDrawable =
   2556                         mView.mContext.getResources().getDrawable(value.resourceId);
   2557                 }
   2558             }
   2559             return mAttachInfo.mAccessibilityFocusDrawable;
   2560         }
   2561         return null;
   2562     }
   2563 
   2564     void invalidateDisplayLists() {
   2565         final ArrayList<DisplayList> displayLists = mDisplayLists;
   2566         final int count = displayLists.size();
   2567 
   2568         for (int i = 0; i < count; i++) {
   2569             final DisplayList displayList = displayLists.get(i);
   2570             if (displayList.isDirty()) {
   2571                 displayList.clear();
   2572             }
   2573         }
   2574 
   2575         displayLists.clear();
   2576     }
   2577 
   2578     /**
   2579      * @hide
   2580      */
   2581     public void setDrawDuringWindowsAnimating(boolean value) {
   2582         mDrawDuringWindowsAnimating = value;
   2583         if (value) {
   2584             handleDispatchDoneAnimating();
   2585         }
   2586     }
   2587 
   2588     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
   2589         final View.AttachInfo attachInfo = mAttachInfo;
   2590         final Rect ci = attachInfo.mContentInsets;
   2591         final Rect vi = attachInfo.mVisibleInsets;
   2592         int scrollY = 0;
   2593         boolean handled = false;
   2594 
   2595         if (vi.left > ci.left || vi.top > ci.top
   2596                 || vi.right > ci.right || vi.bottom > ci.bottom) {
   2597             // We'll assume that we aren't going to change the scroll
   2598             // offset, since we want to avoid that unless it is actually
   2599             // going to make the focus visible...  otherwise we scroll
   2600             // all over the place.
   2601             scrollY = mScrollY;
   2602             // We can be called for two different situations: during a draw,
   2603             // to update the scroll position if the focus has changed (in which
   2604             // case 'rectangle' is null), or in response to a
   2605             // requestChildRectangleOnScreen() call (in which case 'rectangle'
   2606             // is non-null and we just want to scroll to whatever that
   2607             // rectangle is).
   2608             final View focus = mView.findFocus();
   2609             if (focus == null) {
   2610                 return false;
   2611             }
   2612             View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;
   2613             if (focus != lastScrolledFocus) {
   2614                 // If the focus has changed, then ignore any requests to scroll
   2615                 // to a rectangle; first we want to make sure the entire focus
   2616                 // view is visible.
   2617                 rectangle = null;
   2618             }
   2619             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
   2620                     + " rectangle=" + rectangle + " ci=" + ci
   2621                     + " vi=" + vi);
   2622             if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
   2623                 // Optimization: if the focus hasn't changed since last
   2624                 // time, and no layout has happened, then just leave things
   2625                 // as they are.
   2626                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
   2627                         + mScrollY + " vi=" + vi.toShortString());
   2628             } else {
   2629                 // We need to determine if the currently focused view is
   2630                 // within the visible part of the window and, if not, apply
   2631                 // a pan so it can be seen.
   2632                 mLastScrolledFocus = new WeakReference<View>(focus);
   2633                 mScrollMayChange = false;
   2634                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
   2635                 // Try to find the rectangle from the focus view.
   2636                 if (focus.getGlobalVisibleRect(mVisRect, null)) {
   2637                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w="
   2638                             + mView.getWidth() + " h=" + mView.getHeight()
   2639                             + " ci=" + ci.toShortString()
   2640                             + " vi=" + vi.toShortString());
   2641                     if (rectangle == null) {
   2642                         focus.getFocusedRect(mTempRect);
   2643                         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
   2644                                 + ": focusRect=" + mTempRect.toShortString());
   2645                         if (mView instanceof ViewGroup) {
   2646                             ((ViewGroup) mView).offsetDescendantRectToMyCoords(
   2647                                     focus, mTempRect);
   2648                         }
   2649                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2650                                 "Focus in window: focusRect="
   2651                                 + mTempRect.toShortString()
   2652                                 + " visRect=" + mVisRect.toShortString());
   2653                     } else {
   2654                         mTempRect.set(rectangle);
   2655                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2656                                 "Request scroll to rect: "
   2657                                 + mTempRect.toShortString()
   2658                                 + " visRect=" + mVisRect.toShortString());
   2659                     }
   2660                     if (mTempRect.intersect(mVisRect)) {
   2661                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2662                                 "Focus window visible rect: "
   2663                                 + mTempRect.toShortString());
   2664                         if (mTempRect.height() >
   2665                                 (mView.getHeight()-vi.top-vi.bottom)) {
   2666                             // If the focus simply is not going to fit, then
   2667                             // best is probably just to leave things as-is.
   2668                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2669                                     "Too tall; leaving scrollY=" + scrollY);
   2670                         } else if ((mTempRect.top-scrollY) < vi.top) {
   2671                             scrollY -= vi.top - (mTempRect.top-scrollY);
   2672                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2673                                     "Top covered; scrollY=" + scrollY);
   2674                         } else if ((mTempRect.bottom-scrollY)
   2675                                 > (mView.getHeight()-vi.bottom)) {
   2676                             scrollY += (mTempRect.bottom-scrollY)
   2677                                     - (mView.getHeight()-vi.bottom);
   2678                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2679                                     "Bottom covered; scrollY=" + scrollY);
   2680                         }
   2681                         handled = true;
   2682                     }
   2683                 }
   2684             }
   2685         }
   2686 
   2687         if (scrollY != mScrollY) {
   2688             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
   2689                     + mScrollY + " , new=" + scrollY);
   2690             if (!immediate && mResizeBuffer == null) {
   2691                 if (mScroller == null) {
   2692                     mScroller = new Scroller(mView.getContext());
   2693                 }
   2694                 mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);
   2695             } else if (mScroller != null) {
   2696                 mScroller.abortAnimation();
   2697             }
   2698             mScrollY = scrollY;
   2699         }
   2700 
   2701         return handled;
   2702     }
   2703 
   2704     /**
   2705      * @hide
   2706      */
   2707     public View getAccessibilityFocusedHost() {
   2708         return mAccessibilityFocusedHost;
   2709     }
   2710 
   2711     /**
   2712      * @hide
   2713      */
   2714     public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() {
   2715         return mAccessibilityFocusedVirtualView;
   2716     }
   2717 
   2718     void setAccessibilityFocus(View view, AccessibilityNodeInfo node) {
   2719         // If we have a virtual view with accessibility focus we need
   2720         // to clear the focus and invalidate the virtual view bounds.
   2721         if (mAccessibilityFocusedVirtualView != null) {
   2722 
   2723             AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView;
   2724             View focusHost = mAccessibilityFocusedHost;
   2725 
   2726             // Wipe the state of the current accessibility focus since
   2727             // the call into the provider to clear accessibility focus
   2728             // will fire an accessibility event which will end up calling
   2729             // this method and we want to have clean state when this
   2730             // invocation happens.
   2731             mAccessibilityFocusedHost = null;
   2732             mAccessibilityFocusedVirtualView = null;
   2733 
   2734             // Clear accessibility focus on the host after clearing state since
   2735             // this method may be reentrant.
   2736             focusHost.clearAccessibilityFocusNoCallbacks();
   2737 
   2738             AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider();
   2739             if (provider != null) {
   2740                 // Invalidate the area of the cleared accessibility focus.
   2741                 focusNode.getBoundsInParent(mTempRect);
   2742                 focusHost.invalidate(mTempRect);
   2743                 // Clear accessibility focus in the virtual node.
   2744                 final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
   2745                         focusNode.getSourceNodeId());
   2746                 provider.performAction(virtualNodeId,
   2747                         AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
   2748             }
   2749             focusNode.recycle();
   2750         }
   2751         if (mAccessibilityFocusedHost != null) {
   2752             // Clear accessibility focus in the view.
   2753             mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
   2754         }
   2755 
   2756         // Set the new focus host and node.
   2757         mAccessibilityFocusedHost = view;
   2758         mAccessibilityFocusedVirtualView = node;
   2759     }
   2760 
   2761     public void requestChildFocus(View child, View focused) {
   2762         if (DEBUG_INPUT_RESIZE) {
   2763             Log.v(TAG, "Request child focus: focus now " + focused);
   2764         }
   2765         checkThread();
   2766         scheduleTraversals();
   2767     }
   2768 
   2769     public void clearChildFocus(View child) {
   2770         if (DEBUG_INPUT_RESIZE) {
   2771             Log.v(TAG, "Clearing child focus");
   2772         }
   2773         checkThread();
   2774         scheduleTraversals();
   2775     }
   2776 
   2777     @Override
   2778     public ViewParent getParentForAccessibility() {
   2779         return null;
   2780     }
   2781 
   2782     public void focusableViewAvailable(View v) {
   2783         checkThread();
   2784         if (mView != null) {
   2785             if (!mView.hasFocus()) {
   2786                 v.requestFocus();
   2787             } else {
   2788                 // the one case where will transfer focus away from the current one
   2789                 // is if the current view is a view group that prefers to give focus
   2790                 // to its children first AND the view is a descendant of it.
   2791                 View focused = mView.findFocus();
   2792                 if (focused instanceof ViewGroup) {
   2793                     ViewGroup group = (ViewGroup) focused;
   2794                     if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
   2795                             && isViewDescendantOf(v, focused)) {
   2796                         v.requestFocus();
   2797                     }
   2798                 }
   2799             }
   2800         }
   2801     }
   2802 
   2803     public void recomputeViewAttributes(View child) {
   2804         checkThread();
   2805         if (mView == child) {
   2806             mAttachInfo.mRecomputeGlobalAttributes = true;
   2807             if (!mWillDrawSoon) {
   2808                 scheduleTraversals();
   2809             }
   2810         }
   2811     }
   2812 
   2813     void dispatchDetachedFromWindow() {
   2814         if (mView != null && mView.mAttachInfo != null) {
   2815             if (mAttachInfo.mHardwareRenderer != null &&
   2816                     mAttachInfo.mHardwareRenderer.isEnabled()) {
   2817                 mAttachInfo.mHardwareRenderer.validate();
   2818             }
   2819             mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
   2820             mView.dispatchDetachedFromWindow();
   2821         }
   2822 
   2823         mAccessibilityInteractionConnectionManager.ensureNoConnection();
   2824         mAccessibilityManager.removeAccessibilityStateChangeListener(
   2825                 mAccessibilityInteractionConnectionManager);
   2826         removeSendWindowContentChangedCallback();
   2827 
   2828         destroyHardwareRenderer();
   2829 
   2830         setAccessibilityFocus(null, null);
   2831 
   2832         mView.assignParent(null);
   2833         mView = null;
   2834         mAttachInfo.mRootView = null;
   2835         mAttachInfo.mSurface = null;
   2836 
   2837         mSurface.release();
   2838 
   2839         if (mInputQueueCallback != null && mInputQueue != null) {
   2840             mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
   2841             mInputQueue.dispose();
   2842             mInputQueueCallback = null;
   2843             mInputQueue = null;
   2844         }
   2845         if (mInputEventReceiver != null) {
   2846             mInputEventReceiver.dispose();
   2847             mInputEventReceiver = null;
   2848         }
   2849         try {
   2850             mWindowSession.remove(mWindow);
   2851         } catch (RemoteException e) {
   2852         }
   2853 
   2854         // Dispose the input channel after removing the window so the Window Manager
   2855         // doesn't interpret the input channel being closed as an abnormal termination.
   2856         if (mInputChannel != null) {
   2857             mInputChannel.dispose();
   2858             mInputChannel = null;
   2859         }
   2860 
   2861         unscheduleTraversals();
   2862     }
   2863 
   2864     void updateConfiguration(Configuration config, boolean force) {
   2865         if (DEBUG_CONFIGURATION) Log.v(TAG,
   2866                 "Applying new config to window "
   2867                 + mWindowAttributes.getTitle()
   2868                 + ": " + config);
   2869 
   2870         CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded();
   2871         if (ci != null) {
   2872             config = new Configuration(config);
   2873             ci.applyToConfiguration(mNoncompatDensity, config);
   2874         }
   2875 
   2876         synchronized (sConfigCallbacks) {
   2877             for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
   2878                 sConfigCallbacks.get(i).onConfigurationChanged(config);
   2879             }
   2880         }
   2881         if (mView != null) {
   2882             // At this point the resources have been updated to
   2883             // have the most recent config, whatever that is.  Use
   2884             // the one in them which may be newer.
   2885             config = mView.getResources().getConfiguration();
   2886             if (force || mLastConfiguration.diff(config) != 0) {
   2887                 final int lastLayoutDirection = mLastConfiguration.getLayoutDirection();
   2888                 final int currentLayoutDirection = config.getLayoutDirection();
   2889                 mLastConfiguration.setTo(config);
   2890                 if (lastLayoutDirection != currentLayoutDirection &&
   2891                         mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
   2892                     mView.setLayoutDirection(currentLayoutDirection);
   2893                 }
   2894                 mView.dispatchConfigurationChanged(config);
   2895             }
   2896         }
   2897     }
   2898 
   2899     /**
   2900      * Return true if child is an ancestor of parent, (or equal to the parent).
   2901      */
   2902     public static boolean isViewDescendantOf(View child, View parent) {
   2903         if (child == parent) {
   2904             return true;
   2905         }
   2906 
   2907         final ViewParent theParent = child.getParent();
   2908         return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
   2909     }
   2910 
   2911     private static void forceLayout(View view) {
   2912         view.forceLayout();
   2913         if (view instanceof ViewGroup) {
   2914             ViewGroup group = (ViewGroup) view;
   2915             final int count = group.getChildCount();
   2916             for (int i = 0; i < count; i++) {
   2917                 forceLayout(group.getChildAt(i));
   2918             }
   2919         }
   2920     }
   2921 
   2922     private final static int MSG_INVALIDATE = 1;
   2923     private final static int MSG_INVALIDATE_RECT = 2;
   2924     private final static int MSG_DIE = 3;
   2925     private final static int MSG_RESIZED = 4;
   2926     private final static int MSG_RESIZED_REPORT = 5;
   2927     private final static int MSG_WINDOW_FOCUS_CHANGED = 6;
   2928     private final static int MSG_DISPATCH_KEY = 7;
   2929     private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
   2930     private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
   2931     private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
   2932     private final static int MSG_FINISH_INPUT_CONNECTION = 12;
   2933     private final static int MSG_CHECK_FOCUS = 13;
   2934     private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14;
   2935     private final static int MSG_DISPATCH_DRAG_EVENT = 15;
   2936     private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16;
   2937     private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17;
   2938     private final static int MSG_UPDATE_CONFIGURATION = 18;
   2939     private final static int MSG_PROCESS_INPUT_EVENTS = 19;
   2940     private final static int MSG_DISPATCH_SCREEN_STATE = 20;
   2941     private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21;
   2942     private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
   2943     private final static int MSG_INVALIDATE_WORLD = 23;
   2944     private final static int MSG_WINDOW_MOVED = 24;
   2945 
   2946     final class ViewRootHandler extends Handler {
   2947         @Override
   2948         public String getMessageName(Message message) {
   2949             switch (message.what) {
   2950                 case MSG_INVALIDATE:
   2951                     return "MSG_INVALIDATE";
   2952                 case MSG_INVALIDATE_RECT:
   2953                     return "MSG_INVALIDATE_RECT";
   2954                 case MSG_DIE:
   2955                     return "MSG_DIE";
   2956                 case MSG_RESIZED:
   2957                     return "MSG_RESIZED";
   2958                 case MSG_RESIZED_REPORT:
   2959                     return "MSG_RESIZED_REPORT";
   2960                 case MSG_WINDOW_FOCUS_CHANGED:
   2961                     return "MSG_WINDOW_FOCUS_CHANGED";
   2962                 case MSG_DISPATCH_KEY:
   2963                     return "MSG_DISPATCH_KEY";
   2964                 case MSG_DISPATCH_APP_VISIBILITY:
   2965                     return "MSG_DISPATCH_APP_VISIBILITY";
   2966                 case MSG_DISPATCH_GET_NEW_SURFACE:
   2967                     return "MSG_DISPATCH_GET_NEW_SURFACE";
   2968                 case MSG_DISPATCH_KEY_FROM_IME:
   2969                     return "MSG_DISPATCH_KEY_FROM_IME";
   2970                 case MSG_FINISH_INPUT_CONNECTION:
   2971                     return "MSG_FINISH_INPUT_CONNECTION";
   2972                 case MSG_CHECK_FOCUS:
   2973                     return "MSG_CHECK_FOCUS";
   2974                 case MSG_CLOSE_SYSTEM_DIALOGS:
   2975                     return "MSG_CLOSE_SYSTEM_DIALOGS";
   2976                 case MSG_DISPATCH_DRAG_EVENT:
   2977                     return "MSG_DISPATCH_DRAG_EVENT";
   2978                 case MSG_DISPATCH_DRAG_LOCATION_EVENT:
   2979                     return "MSG_DISPATCH_DRAG_LOCATION_EVENT";
   2980                 case MSG_DISPATCH_SYSTEM_UI_VISIBILITY:
   2981                     return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY";
   2982                 case MSG_UPDATE_CONFIGURATION:
   2983                     return "MSG_UPDATE_CONFIGURATION";
   2984                 case MSG_PROCESS_INPUT_EVENTS:
   2985                     return "MSG_PROCESS_INPUT_EVENTS";
   2986                 case MSG_DISPATCH_SCREEN_STATE:
   2987                     return "MSG_DISPATCH_SCREEN_STATE";
   2988                 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST:
   2989                     return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST";
   2990                 case MSG_DISPATCH_DONE_ANIMATING:
   2991                     return "MSG_DISPATCH_DONE_ANIMATING";
   2992                 case MSG_WINDOW_MOVED:
   2993                     return "MSG_WINDOW_MOVED";
   2994             }
   2995             return super.getMessageName(message);
   2996         }
   2997 
   2998         @Override
   2999         public void handleMessage(Message msg) {
   3000             switch (msg.what) {
   3001             case MSG_INVALIDATE:
   3002                 ((View) msg.obj).invalidate();
   3003                 break;
   3004             case MSG_INVALIDATE_RECT:
   3005                 final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
   3006                 info.target.invalidate(info.left, info.top, info.right, info.bottom);
   3007                 info.recycle();
   3008                 break;
   3009             case MSG_PROCESS_INPUT_EVENTS:
   3010                 mProcessInputEventsScheduled = false;
   3011                 doProcessInputEvents();
   3012                 break;
   3013             case MSG_DISPATCH_APP_VISIBILITY:
   3014                 handleAppVisibility(msg.arg1 != 0);
   3015                 break;
   3016             case MSG_DISPATCH_GET_NEW_SURFACE:
   3017                 handleGetNewSurface();
   3018                 break;
   3019             case MSG_RESIZED: {
   3020                 // Recycled in the fall through...
   3021                 SomeArgs args = (SomeArgs) msg.obj;
   3022                 if (mWinFrame.equals(args.arg1)
   3023                         && mPendingOverscanInsets.equals(args.arg5)
   3024                         && mPendingContentInsets.equals(args.arg2)
   3025                         && mPendingVisibleInsets.equals(args.arg3)
   3026                         && args.arg4 == null) {
   3027                     break;
   3028                 }
   3029                 } // fall through...
   3030             case MSG_RESIZED_REPORT:
   3031                 if (mAdded) {
   3032                     SomeArgs args = (SomeArgs) msg.obj;
   3033 
   3034                     Configuration config = (Configuration) args.arg4;
   3035                     if (config != null) {
   3036                         updateConfiguration(config, false);
   3037                     }
   3038 
   3039                     mWinFrame.set((Rect) args.arg1);
   3040                     mPendingOverscanInsets.set((Rect) args.arg5);
   3041                     mPendingContentInsets.set((Rect) args.arg2);
   3042                     mPendingVisibleInsets.set((Rect) args.arg3);
   3043 
   3044                     args.recycle();
   3045 
   3046                     if (msg.what == MSG_RESIZED_REPORT) {
   3047                         mReportNextDraw = true;
   3048                     }
   3049 
   3050                     if (mView != null) {
   3051                         forceLayout(mView);
   3052                     }
   3053 
   3054                     requestLayout();
   3055                 }
   3056                 break;
   3057             case MSG_WINDOW_MOVED:
   3058                 if (mAdded) {
   3059                     final int w = mWinFrame.width();
   3060                     final int h = mWinFrame.height();
   3061                     final int l = msg.arg1;
   3062                     final int t = msg.arg2;
   3063                     mWinFrame.left = l;
   3064                     mWinFrame.right = l + w;
   3065                     mWinFrame.top = t;
   3066                     mWinFrame.bottom = t + h;
   3067 
   3068                     if (mView != null) {
   3069                         forceLayout(mView);
   3070                     }
   3071                     requestLayout();
   3072                 }
   3073                 break;
   3074             case MSG_WINDOW_FOCUS_CHANGED: {
   3075                 if (mAdded) {
   3076                     boolean hasWindowFocus = msg.arg1 != 0;
   3077                     mAttachInfo.mHasWindowFocus = hasWindowFocus;
   3078 
   3079                     profileRendering(hasWindowFocus);
   3080 
   3081                     if (hasWindowFocus) {
   3082                         boolean inTouchMode = msg.arg2 != 0;
   3083                         ensureTouchModeLocally(inTouchMode);
   3084 
   3085                         if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){
   3086                             mFullRedrawNeeded = true;
   3087                             try {
   3088                                 mAttachInfo.mHardwareRenderer.initializeIfNeeded(
   3089                                         mWidth, mHeight, mHolder.getSurface());
   3090                             } catch (Surface.OutOfResourcesException e) {
   3091                                 Log.e(TAG, "OutOfResourcesException locking surface", e);
   3092                                 try {
   3093                                     if (!mWindowSession.outOfMemory(mWindow)) {
   3094                                         Slog.w(TAG, "No processes killed for memory; killing self");
   3095                                         Process.killProcess(Process.myPid());
   3096                                     }
   3097                                 } catch (RemoteException ex) {
   3098                                 }
   3099                                 // Retry in a bit.
   3100                                 sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500);
   3101                                 return;
   3102                             }
   3103                         }
   3104                     }
   3105 
   3106                     mLastWasImTarget = WindowManager.LayoutParams
   3107                             .mayUseInputMethod(mWindowAttributes.flags);
   3108 
   3109                     InputMethodManager imm = InputMethodManager.peekInstance();
   3110                     if (mView != null) {
   3111                         if (hasWindowFocus && imm != null && mLastWasImTarget) {
   3112                             imm.startGettingWindowFocus(mView);
   3113                         }
   3114                         mAttachInfo.mKeyDispatchState.reset();
   3115                         mView.dispatchWindowFocusChanged(hasWindowFocus);
   3116                         mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
   3117                     }
   3118 
   3119                     // Note: must be done after the focus change callbacks,
   3120                     // so all of the view state is set up correctly.
   3121                     if (hasWindowFocus) {
   3122                         if (imm != null && mLastWasImTarget) {
   3123                             imm.onWindowFocus(mView, mView.findFocus(),
   3124                                     mWindowAttributes.softInputMode,
   3125                                     !mHasHadWindowFocus, mWindowAttributes.flags);
   3126                         }
   3127                         // Clear the forward bit.  We can just do this directly, since
   3128                         // the window manager doesn't care about it.
   3129                         mWindowAttributes.softInputMode &=
   3130                                 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
   3131                         ((WindowManager.LayoutParams)mView.getLayoutParams())
   3132                                 .softInputMode &=
   3133                                     ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
   3134                         mHasHadWindowFocus = true;
   3135                     }
   3136 
   3137                     setAccessibilityFocus(null, null);
   3138 
   3139                     if (mView != null && mAccessibilityManager.isEnabled()) {
   3140                         if (hasWindowFocus) {
   3141                             mView.sendAccessibilityEvent(
   3142                                     AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3143                         }
   3144                     }
   3145                 }
   3146             } break;
   3147             case MSG_DIE:
   3148                 doDie();
   3149                 break;
   3150             case MSG_DISPATCH_KEY: {
   3151                 KeyEvent event = (KeyEvent)msg.obj;
   3152                 enqueueInputEvent(event, null, 0, true);
   3153             } break;
   3154             case MSG_DISPATCH_KEY_FROM_IME: {
   3155                 if (LOCAL_LOGV) Log.v(
   3156                     TAG, "Dispatching key "
   3157                     + msg.obj + " from IME to " + mView);
   3158                 KeyEvent event = (KeyEvent)msg.obj;
   3159                 if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
   3160                     // The IME is trying to say this event is from the
   3161                     // system!  Bad bad bad!
   3162                     //noinspection UnusedAssignment
   3163                     event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
   3164                 }
   3165                 enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
   3166             } break;
   3167             case MSG_FINISH_INPUT_CONNECTION: {
   3168                 InputMethodManager imm = InputMethodManager.peekInstance();
   3169                 if (imm != null) {
   3170                     imm.reportFinishInputConnection((InputConnection)msg.obj);
   3171                 }
   3172             } break;
   3173             case MSG_CHECK_FOCUS: {
   3174                 InputMethodManager imm = InputMethodManager.peekInstance();
   3175                 if (imm != null) {
   3176                     imm.checkFocus();
   3177                 }
   3178             } break;
   3179             case MSG_CLOSE_SYSTEM_DIALOGS: {
   3180                 if (mView != null) {
   3181                     mView.onCloseSystemDialogs((String)msg.obj);
   3182                 }
   3183             } break;
   3184             case MSG_DISPATCH_DRAG_EVENT:
   3185             case MSG_DISPATCH_DRAG_LOCATION_EVENT: {
   3186                 DragEvent event = (DragEvent)msg.obj;
   3187                 event.mLocalState = mLocalDragState;    // only present when this app called startDrag()
   3188                 handleDragEvent(event);
   3189             } break;
   3190             case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
   3191                 handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
   3192             } break;
   3193             case MSG_UPDATE_CONFIGURATION: {
   3194                 Configuration config = (Configuration)msg.obj;
   3195                 if (config.isOtherSeqNewer(mLastConfiguration)) {
   3196                     config = mLastConfiguration;
   3197                 }
   3198                 updateConfiguration(config, false);
   3199             } break;
   3200             case MSG_DISPATCH_SCREEN_STATE: {
   3201                 if (mView != null) {
   3202                     handleScreenStateChange(msg.arg1 == 1);
   3203                 }
   3204             } break;
   3205             case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
   3206                 setAccessibilityFocus(null, null);
   3207             } break;
   3208             case MSG_DISPATCH_DONE_ANIMATING: {
   3209                 handleDispatchDoneAnimating();
   3210             } break;
   3211             case MSG_INVALIDATE_WORLD: {
   3212                 if (mView != null) {
   3213                     invalidateWorld(mView);
   3214                 }
   3215             } break;
   3216             }
   3217         }
   3218     }
   3219 
   3220     final ViewRootHandler mHandler = new ViewRootHandler();
   3221 
   3222     /**
   3223      * Something in the current window tells us we need to change the touch mode.  For
   3224      * example, we are not in touch mode, and the user touches the screen.
   3225      *
   3226      * If the touch mode has changed, tell the window manager, and handle it locally.
   3227      *
   3228      * @param inTouchMode Whether we want to be in touch mode.
   3229      * @return True if the touch mode changed and focus changed was changed as a result
   3230      */
   3231     boolean ensureTouchMode(boolean inTouchMode) {
   3232         if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
   3233                 + "touch mode is " + mAttachInfo.mInTouchMode);
   3234         if (mAttachInfo.mInTouchMode == inTouchMode) return false;
   3235 
   3236         // tell the window manager
   3237         try {
   3238             mWindowSession.setInTouchMode(inTouchMode);
   3239         } catch (RemoteException e) {
   3240             throw new RuntimeException(e);
   3241         }
   3242 
   3243         // handle the change
   3244         return ensureTouchModeLocally(inTouchMode);
   3245     }
   3246 
   3247     /**
   3248      * Ensure that the touch mode for this window is set, and if it is changing,
   3249      * take the appropriate action.
   3250      * @param inTouchMode Whether we want to be in touch mode.
   3251      * @return True if the touch mode changed and focus changed was changed as a result
   3252      */
   3253     private boolean ensureTouchModeLocally(boolean inTouchMode) {
   3254         if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current "
   3255                 + "touch mode is " + mAttachInfo.mInTouchMode);
   3256 
   3257         if (mAttachInfo.mInTouchMode == inTouchMode) return false;
   3258 
   3259         mAttachInfo.mInTouchMode = inTouchMode;
   3260         mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);
   3261 
   3262         return (inTouchMode) ? enterTouchMode() : leaveTouchMode();
   3263     }
   3264 
   3265     private boolean enterTouchMode() {
   3266         if (mView != null) {
   3267             if (mView.hasFocus()) {
   3268                 // note: not relying on mFocusedView here because this could
   3269                 // be when the window is first being added, and mFocused isn't
   3270                 // set yet.
   3271                 final View focused = mView.findFocus();
   3272                 if (focused != null && !focused.isFocusableInTouchMode()) {
   3273                     final ViewGroup ancestorToTakeFocus =
   3274                             findAncestorToTakeFocusInTouchMode(focused);
   3275                     if (ancestorToTakeFocus != null) {
   3276                         // there is an ancestor that wants focus after its descendants that
   3277                         // is focusable in touch mode.. give it focus
   3278                         return ancestorToTakeFocus.requestFocus();
   3279                     } else {
   3280                         // nothing appropriate to have focus in touch mode, clear it out
   3281                         focused.unFocus();
   3282                         return true;
   3283                     }
   3284                 }
   3285             }
   3286         }
   3287         return false;
   3288     }
   3289 
   3290     /**
   3291      * Find an ancestor of focused that wants focus after its descendants and is
   3292      * focusable in touch mode.
   3293      * @param focused The currently focused view.
   3294      * @return An appropriate view, or null if no such view exists.
   3295      */
   3296     private static ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
   3297         ViewParent parent = focused.getParent();
   3298         while (parent instanceof ViewGroup) {
   3299             final ViewGroup vgParent = (ViewGroup) parent;
   3300             if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
   3301                     && vgParent.isFocusableInTouchMode()) {
   3302                 return vgParent;
   3303             }
   3304             if (vgParent.isRootNamespace()) {
   3305                 return null;
   3306             } else {
   3307                 parent = vgParent.getParent();
   3308             }
   3309         }
   3310         return null;
   3311     }
   3312 
   3313     private boolean leaveTouchMode() {
   3314         if (mView != null) {
   3315             if (mView.hasFocus()) {
   3316                 View focusedView = mView.findFocus();
   3317                 if (!(focusedView instanceof ViewGroup)) {
   3318                     // some view has focus, let it keep it
   3319                     return false;
   3320                 } else if (((ViewGroup) focusedView).getDescendantFocusability() !=
   3321                         ViewGroup.FOCUS_AFTER_DESCENDANTS) {
   3322                     // some view group has focus, and doesn't prefer its children
   3323                     // over itself for focus, so let them keep it.
   3324                     return false;
   3325                 }
   3326             }
   3327 
   3328             // find the best view to give focus to in this brave new non-touch-mode
   3329             // world
   3330             final View focused = focusSearch(null, View.FOCUS_DOWN);
   3331             if (focused != null) {
   3332                 return focused.requestFocus(View.FOCUS_DOWN);
   3333             }
   3334         }
   3335         return false;
   3336     }
   3337 
   3338     /**
   3339      * Base class for implementing a stage in the chain of responsibility
   3340      * for processing input events.
   3341      * <p>
   3342      * Events are delivered to the stage by the {@link #deliver} method.  The stage
   3343      * then has the choice of finishing the event or forwarding it to the next stage.
   3344      * </p>
   3345      */
   3346     abstract class InputStage {
   3347         private final InputStage mNext;
   3348 
   3349         protected static final int FORWARD = 0;
   3350         protected static final int FINISH_HANDLED = 1;
   3351         protected static final int FINISH_NOT_HANDLED = 2;
   3352 
   3353         /**
   3354          * Creates an input stage.
   3355          * @param next The next stage to which events should be forwarded.
   3356          */
   3357         public InputStage(InputStage next) {
   3358             mNext = next;
   3359         }
   3360 
   3361         /**
   3362          * Delivers an event to be processed.
   3363          */
   3364         public final void deliver(QueuedInputEvent q) {
   3365             if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
   3366                 forward(q);
   3367             } else if (mView == null || !mAdded) {
   3368                 Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
   3369                 finish(q, false);
   3370             } else if (!mAttachInfo.mHasWindowFocus &&
   3371                   !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
   3372                   !isTerminalInputEvent(q.mEvent)) {
   3373                 // If this is a focused event and the window doesn't currently have input focus,
   3374                 // then drop this event.  This could be an event that came back from the previous
   3375                 // stage but the window has lost focus in the meantime.
   3376                 Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
   3377                 finish(q, false);
   3378             } else {
   3379                 apply(q, onProcess(q));
   3380             }
   3381         }
   3382 
   3383         /**
   3384          * Marks the the input event as finished then forwards it to the next stage.
   3385          */
   3386         protected void finish(QueuedInputEvent q, boolean handled) {
   3387             q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
   3388             if (handled) {
   3389                 q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
   3390             }
   3391             forward(q);
   3392         }
   3393 
   3394         /**
   3395          * Forwards the event to the next stage.
   3396          */
   3397         protected void forward(QueuedInputEvent q) {
   3398             onDeliverToNext(q);
   3399         }
   3400 
   3401         /**
   3402          * Applies a result code from {@link #onProcess} to the specified event.
   3403          */
   3404         protected void apply(QueuedInputEvent q, int result) {
   3405             if (result == FORWARD) {
   3406                 forward(q);
   3407             } else if (result == FINISH_HANDLED) {
   3408                 finish(q, true);
   3409             } else if (result == FINISH_NOT_HANDLED) {
   3410                 finish(q, false);
   3411             } else {
   3412                 throw new IllegalArgumentException("Invalid result: " + result);
   3413             }
   3414         }
   3415 
   3416         /**
   3417          * Called when an event is ready to be processed.
   3418          * @return A result code indicating how the event was handled.
   3419          */
   3420         protected int onProcess(QueuedInputEvent q) {
   3421             return FORWARD;
   3422         }
   3423 
   3424         /**
   3425          * Called when an event is being delivered to the next stage.
   3426          */
   3427         protected void onDeliverToNext(QueuedInputEvent q) {
   3428             if (mNext != null) {
   3429                 mNext.deliver(q);
   3430             } else {
   3431                 finishInputEvent(q);
   3432             }
   3433         }
   3434     }
   3435 
   3436     /**
   3437      * Base class for implementing an input pipeline stage that supports
   3438      * asynchronous and out-of-order processing of input events.
   3439      * <p>
   3440      * In addition to what a normal input stage can do, an asynchronous
   3441      * input stage may also defer an input event that has been delivered to it
   3442      * and finish or forward it later.
   3443      * </p>
   3444      */
   3445     abstract class AsyncInputStage extends InputStage {
   3446         private final String mTraceCounter;
   3447 
   3448         private QueuedInputEvent mQueueHead;
   3449         private QueuedInputEvent mQueueTail;
   3450         private int mQueueLength;
   3451 
   3452         protected static final int DEFER = 3;
   3453 
   3454         /**
   3455          * Creates an asynchronous input stage.
   3456          * @param next The next stage to which events should be forwarded.
   3457          * @param traceCounter The name of a counter to record the size of
   3458          * the queue of pending events.
   3459          */
   3460         public AsyncInputStage(InputStage next, String traceCounter) {
   3461             super(next);
   3462             mTraceCounter = traceCounter;
   3463         }
   3464 
   3465         /**
   3466          * Marks the event as deferred, which is to say that it will be handled
   3467          * asynchronously.  The caller is responsible for calling {@link #forward}
   3468          * or {@link #finish} later when it is done handling the event.
   3469          */
   3470         protected void defer(QueuedInputEvent q) {
   3471             q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
   3472             enqueue(q);
   3473         }
   3474 
   3475         @Override
   3476         protected void forward(QueuedInputEvent q) {
   3477             // Clear the deferred flag.
   3478             q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;
   3479 
   3480             // Fast path if the queue is empty.
   3481             QueuedInputEvent curr = mQueueHead;
   3482             if (curr == null) {
   3483                 super.forward(q);
   3484                 return;
   3485             }
   3486 
   3487             // Determine whether the event must be serialized behind any others
   3488             // before it can be delivered to the next stage.  This is done because
   3489             // deferred events might be handled out of order by the stage.
   3490             final int deviceId = q.mEvent.getDeviceId();
   3491             QueuedInputEvent prev = null;
   3492             boolean blocked = false;
   3493             while (curr != null && curr != q) {
   3494                 if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
   3495                     blocked = true;
   3496                 }
   3497                 prev = curr;
   3498                 curr = curr.mNext;
   3499             }
   3500 
   3501             // If the event is blocked, then leave it in the queue to be delivered later.
   3502             // Note that the event might not yet be in the queue if it was not previously
   3503             // deferred so we will enqueue it if needed.
   3504             if (blocked) {
   3505                 if (curr == null) {
   3506                     enqueue(q);
   3507                 }
   3508                 return;
   3509             }
   3510 
   3511             // The event is not blocked.  Deliver it immediately.
   3512             if (curr != null) {
   3513                 curr = curr.mNext;
   3514                 dequeue(q, prev);
   3515             }
   3516             super.forward(q);
   3517 
   3518             // Dequeuing this event may have unblocked successors.  Deliver them.
   3519             while (curr != null) {
   3520                 if (deviceId == curr.mEvent.getDeviceId()) {
   3521                     if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
   3522                         break;
   3523                     }
   3524                     QueuedInputEvent next = curr.mNext;
   3525                     dequeue(curr, prev);
   3526                     super.forward(curr);
   3527                     curr = next;
   3528                 } else {
   3529                     prev = curr;
   3530                     curr = curr.mNext;
   3531                 }
   3532             }
   3533         }
   3534 
   3535         @Override
   3536         protected void apply(QueuedInputEvent q, int result) {
   3537             if (result == DEFER) {
   3538                 defer(q);
   3539             } else {
   3540                 super.apply(q, result);
   3541             }
   3542         }
   3543 
   3544         private void enqueue(QueuedInputEvent q) {
   3545             if (mQueueTail == null) {
   3546                 mQueueHead = q;
   3547                 mQueueTail = q;
   3548             } else {
   3549                 mQueueTail.mNext = q;
   3550                 mQueueTail = q;
   3551             }
   3552 
   3553             mQueueLength += 1;
   3554             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
   3555         }
   3556 
   3557         private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
   3558             if (prev == null) {
   3559                 mQueueHead = q.mNext;
   3560             } else {
   3561                 prev.mNext = q.mNext;
   3562             }
   3563             if (mQueueTail == q) {
   3564                 mQueueTail = prev;
   3565             }
   3566             q.mNext = null;
   3567 
   3568             mQueueLength -= 1;
   3569             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
   3570         }
   3571     }
   3572 
   3573     /**
   3574      * Delivers pre-ime input events to a native activity.
   3575      * Does not support pointer events.
   3576      */
   3577     final class NativePreImeInputStage extends AsyncInputStage
   3578             implements InputQueue.FinishedInputEventCallback {
   3579         public NativePreImeInputStage(InputStage next, String traceCounter) {
   3580             super(next, traceCounter);
   3581         }
   3582 
   3583         @Override
   3584         protected int onProcess(QueuedInputEvent q) {
   3585             if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
   3586                 mInputQueue.sendInputEvent(q.mEvent, q, true, this);
   3587                 return DEFER;
   3588             }
   3589             return FORWARD;
   3590         }
   3591 
   3592         @Override
   3593         public void onFinishedInputEvent(Object token, boolean handled) {
   3594             QueuedInputEvent q = (QueuedInputEvent)token;
   3595             if (handled) {
   3596                 finish(q, true);
   3597                 return;
   3598             }
   3599             forward(q);
   3600         }
   3601     }
   3602 
   3603     /**
   3604      * Delivers pre-ime input events to the view hierarchy.
   3605      * Does not support pointer events.
   3606      */
   3607     final class ViewPreImeInputStage extends InputStage {
   3608         public ViewPreImeInputStage(InputStage next) {
   3609             super(next);
   3610         }
   3611 
   3612         @Override
   3613         protected int onProcess(QueuedInputEvent q) {
   3614             if (q.mEvent instanceof KeyEvent) {
   3615                 return processKeyEvent(q);
   3616             }
   3617             return FORWARD;
   3618         }
   3619 
   3620         private int processKeyEvent(QueuedInputEvent q) {
   3621             final KeyEvent event = (KeyEvent)q.mEvent;
   3622             if (mView.dispatchKeyEventPreIme(event)) {
   3623                 return FINISH_HANDLED;
   3624             }
   3625             return FORWARD;
   3626         }
   3627     }
   3628 
   3629     /**
   3630      * Delivers input events to the ime.
   3631      * Does not support pointer events.
   3632      */
   3633     final class ImeInputStage extends AsyncInputStage
   3634             implements InputMethodManager.FinishedInputEventCallback {
   3635         public ImeInputStage(InputStage next, String traceCounter) {
   3636             super(next, traceCounter);
   3637         }
   3638 
   3639         @Override
   3640         protected int onProcess(QueuedInputEvent q) {
   3641             if (mLastWasImTarget) {
   3642                 InputMethodManager imm = InputMethodManager.peekInstance();
   3643                 if (imm != null) {
   3644                     final InputEvent event = q.mEvent;
   3645                     if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
   3646                     int result = imm.dispatchInputEvent(event, q, this, mHandler);
   3647                     if (result == InputMethodManager.DISPATCH_HANDLED) {
   3648                         return FINISH_HANDLED;
   3649                     } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
   3650                         return FINISH_NOT_HANDLED;
   3651                     } else {
   3652                         return DEFER; // callback will be invoked later
   3653                     }
   3654                 }
   3655             }
   3656             return FORWARD;
   3657         }
   3658 
   3659         @Override
   3660         public void onFinishedInputEvent(Object token, boolean handled) {
   3661             QueuedInputEvent q = (QueuedInputEvent)token;
   3662             if (handled) {
   3663                 finish(q, true);
   3664                 return;
   3665             }
   3666             forward(q);
   3667         }
   3668     }
   3669 
   3670     /**
   3671      * Performs early processing of post-ime input events.
   3672      */
   3673     final class EarlyPostImeInputStage extends InputStage {
   3674         public EarlyPostImeInputStage(InputStage next) {
   3675             super(next);
   3676         }
   3677 
   3678         @Override
   3679         protected int onProcess(QueuedInputEvent q) {
   3680             if (q.mEvent instanceof KeyEvent) {
   3681                 return processKeyEvent(q);
   3682             } else {
   3683                 final int source = q.mEvent.getSource();
   3684                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
   3685                     return processPointerEvent(q);
   3686                 }
   3687             }
   3688             return FORWARD;
   3689         }
   3690 
   3691         private int processKeyEvent(QueuedInputEvent q) {
   3692             final KeyEvent event = (KeyEvent)q.mEvent;
   3693 
   3694             // If the key's purpose is to exit touch mode then we consume it
   3695             // and consider it handled.
   3696             if (checkForLeavingTouchModeAndConsume(event)) {
   3697                 return FINISH_HANDLED;
   3698             }
   3699 
   3700             // Make sure the fallback event policy sees all keys that will be
   3701             // delivered to the view hierarchy.
   3702             mFallbackEventHandler.preDispatchKeyEvent(event);
   3703             return FORWARD;
   3704         }
   3705 
   3706         private int processPointerEvent(QueuedInputEvent q) {
   3707             final MotionEvent event = (MotionEvent)q.mEvent;
   3708 
   3709             // Translate the pointer event for compatibility, if needed.
   3710             if (mTranslator != null) {
   3711                 mTranslator.translateEventInScreenToAppWindow(event);
   3712             }
   3713 
   3714             // Enter touch mode on down or scroll.
   3715             final int action = event.getAction();
   3716             if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
   3717                 ensureTouchMode(true);
   3718             }
   3719 
   3720             // Offset the scroll position.
   3721             if (mCurScrollY != 0) {
   3722                 event.offsetLocation(0, mCurScrollY);
   3723             }
   3724 
   3725             // Remember the touch position for possible drag-initiation.
   3726             if (event.isTouchEvent()) {
   3727                 mLastTouchPoint.x = event.getRawX();
   3728                 mLastTouchPoint.y = event.getRawY();
   3729             }
   3730             return FORWARD;
   3731         }
   3732     }
   3733 
   3734     /**
   3735      * Delivers post-ime input events to a native activity.
   3736      */
   3737     final class NativePostImeInputStage extends AsyncInputStage
   3738             implements InputQueue.FinishedInputEventCallback {
   3739         public NativePostImeInputStage(InputStage next, String traceCounter) {
   3740             super(next, traceCounter);
   3741         }
   3742 
   3743         @Override
   3744         protected int onProcess(QueuedInputEvent q) {
   3745             if (mInputQueue != null) {
   3746                 mInputQueue.sendInputEvent(q.mEvent, q, false, this);
   3747                 return DEFER;
   3748             }
   3749             return FORWARD;
   3750         }
   3751 
   3752         @Override
   3753         public void onFinishedInputEvent(Object token, boolean handled) {
   3754             QueuedInputEvent q = (QueuedInputEvent)token;
   3755             if (handled) {
   3756                 finish(q, true);
   3757                 return;
   3758             }
   3759             forward(q);
   3760         }
   3761     }
   3762 
   3763     /**
   3764      * Delivers post-ime input events to the view hierarchy.
   3765      */
   3766     final class ViewPostImeInputStage extends InputStage {
   3767         public ViewPostImeInputStage(InputStage next) {
   3768             super(next);
   3769         }
   3770 
   3771         @Override
   3772         protected int onProcess(QueuedInputEvent q) {
   3773             if (q.mEvent instanceof KeyEvent) {
   3774                 return processKeyEvent(q);
   3775             } else {
   3776                 final int source = q.mEvent.getSource();
   3777                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
   3778                     return processPointerEvent(q);
   3779                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
   3780                     return processTrackballEvent(q);
   3781                 } else {
   3782                     return processGenericMotionEvent(q);
   3783                 }
   3784             }
   3785         }
   3786 
   3787         private int processKeyEvent(QueuedInputEvent q) {
   3788             final KeyEvent event = (KeyEvent)q.mEvent;
   3789 
   3790             // Deliver the key to the view hierarchy.
   3791             if (mView.dispatchKeyEvent(event)) {
   3792                 return FINISH_HANDLED;
   3793             }
   3794 
   3795             // If the Control modifier is held, try to interpret the key as a shortcut.
   3796             if (event.getAction() == KeyEvent.ACTION_DOWN
   3797                     && event.isCtrlPressed()
   3798                     && event.getRepeatCount() == 0
   3799                     && !KeyEvent.isModifierKey(event.getKeyCode())) {
   3800                 if (mView.dispatchKeyShortcutEvent(event)) {
   3801                     return FINISH_HANDLED;
   3802                 }
   3803             }
   3804 
   3805             // Apply the fallback event policy.
   3806             if (mFallbackEventHandler.dispatchKeyEvent(event)) {
   3807                 return FINISH_HANDLED;
   3808             }
   3809 
   3810             // Handle automatic focus changes.
   3811             if (event.getAction() == KeyEvent.ACTION_DOWN) {
   3812                 int direction = 0;
   3813                 switch (event.getKeyCode()) {
   3814                     case KeyEvent.KEYCODE_DPAD_LEFT:
   3815                         if (event.hasNoModifiers()) {
   3816                             direction = View.FOCUS_LEFT;
   3817                         }
   3818                         break;
   3819                     case KeyEvent.KEYCODE_DPAD_RIGHT:
   3820                         if (event.hasNoModifiers()) {
   3821                             direction = View.FOCUS_RIGHT;
   3822                         }
   3823                         break;
   3824                     case KeyEvent.KEYCODE_DPAD_UP:
   3825                         if (event.hasNoModifiers()) {
   3826                             direction = View.FOCUS_UP;
   3827                         }
   3828                         break;
   3829                     case KeyEvent.KEYCODE_DPAD_DOWN:
   3830                         if (event.hasNoModifiers()) {
   3831                             direction = View.FOCUS_DOWN;
   3832                         }
   3833                         break;
   3834                     case KeyEvent.KEYCODE_TAB:
   3835                         if (event.hasNoModifiers()) {
   3836                             direction = View.FOCUS_FORWARD;
   3837                         } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
   3838                             direction = View.FOCUS_BACKWARD;
   3839                         }
   3840                         break;
   3841                 }
   3842                 if (direction != 0) {
   3843                     View focused = mView.findFocus();
   3844                     if (focused != null) {
   3845                         View v = focused.focusSearch(direction);
   3846                         if (v != null && v != focused) {
   3847                             // do the math the get the interesting rect
   3848                             // of previous focused into the coord system of
   3849                             // newly focused view
   3850                             focused.getFocusedRect(mTempRect);
   3851                             if (mView instanceof ViewGroup) {
   3852                                 ((ViewGroup) mView).offsetDescendantRectToMyCoords(
   3853                                         focused, mTempRect);
   3854                                 ((ViewGroup) mView).offsetRectIntoDescendantCoords(
   3855                                         v, mTempRect);
   3856                             }
   3857                             if (v.requestFocus(direction, mTempRect)) {
   3858                                 playSoundEffect(SoundEffectConstants
   3859                                         .getContantForFocusDirection(direction));
   3860                                 return FINISH_HANDLED;
   3861                             }
   3862                         }
   3863 
   3864                         // Give the focused view a last chance to handle the dpad key.
   3865                         if (mView.dispatchUnhandledMove(focused, direction)) {
   3866                             return FINISH_HANDLED;
   3867                         }
   3868                     } else {
   3869                         // find the best view to give focus to in this non-touch-mode with no-focus
   3870                         View v = focusSearch(null, direction);
   3871                         if (v != null && v.requestFocus(direction)) {
   3872                             return FINISH_HANDLED;
   3873                         }
   3874                     }
   3875                 }
   3876             }
   3877             return FORWARD;
   3878         }
   3879 
   3880         private int processPointerEvent(QueuedInputEvent q) {
   3881             final MotionEvent event = (MotionEvent)q.mEvent;
   3882 
   3883             if (mView.dispatchPointerEvent(event)) {
   3884                 return FINISH_HANDLED;
   3885             }
   3886             return FORWARD;
   3887         }
   3888 
   3889         private int processTrackballEvent(QueuedInputEvent q) {
   3890             final MotionEvent event = (MotionEvent)q.mEvent;
   3891 
   3892             if (mView.dispatchTrackballEvent(event)) {
   3893                 return FINISH_HANDLED;
   3894             }
   3895             return FORWARD;
   3896         }
   3897 
   3898         private int processGenericMotionEvent(QueuedInputEvent q) {
   3899             final MotionEvent event = (MotionEvent)q.mEvent;
   3900 
   3901             // Deliver the event to the view.
   3902             if (mView.dispatchGenericMotionEvent(event)) {
   3903                 return FINISH_HANDLED;
   3904             }
   3905             return FORWARD;
   3906         }
   3907     }
   3908 
   3909     /**
   3910      * Performs synthesis of new input events from unhandled input events.
   3911      */
   3912     final class SyntheticInputStage extends InputStage {
   3913         private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
   3914         private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
   3915         private final SyntheticTouchNavigationHandler mTouchNavigation =
   3916                 new SyntheticTouchNavigationHandler();
   3917 
   3918         public SyntheticInputStage() {
   3919             super(null);
   3920         }
   3921 
   3922         @Override
   3923         protected int onProcess(QueuedInputEvent q) {
   3924             q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
   3925             if (q.mEvent instanceof MotionEvent) {
   3926                 final MotionEvent event = (MotionEvent)q.mEvent;
   3927                 final int source = event.getSource();
   3928                 if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
   3929                     mTrackball.process(event);
   3930                     return FINISH_HANDLED;
   3931                 } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
   3932                     mJoystick.process(event);
   3933                     return FINISH_HANDLED;
   3934                 } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
   3935                         == InputDevice.SOURCE_TOUCH_NAVIGATION) {
   3936                     mTouchNavigation.process(event);
   3937                     return FINISH_HANDLED;
   3938                 }
   3939             }
   3940             return FORWARD;
   3941         }
   3942 
   3943         @Override
   3944         protected void onDeliverToNext(QueuedInputEvent q) {
   3945             if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
   3946                 // Cancel related synthetic events if any prior stage has handled the event.
   3947                 if (q.mEvent instanceof MotionEvent) {
   3948                     final MotionEvent event = (MotionEvent)q.mEvent;
   3949                     final int source = event.getSource();
   3950                     if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
   3951                         mTrackball.cancel(event);
   3952                     } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
   3953                         mJoystick.cancel(event);
   3954                     } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
   3955                             == InputDevice.SOURCE_TOUCH_NAVIGATION) {
   3956                         mTouchNavigation.cancel(event);
   3957                     }
   3958                 }
   3959             }
   3960             super.onDeliverToNext(q);
   3961         }
   3962     }
   3963 
   3964     /**
   3965      * Creates dpad events from unhandled trackball movements.
   3966      */
   3967     final class SyntheticTrackballHandler {
   3968         private final TrackballAxis mX = new TrackballAxis();
   3969         private final TrackballAxis mY = new TrackballAxis();
   3970         private long mLastTime;
   3971 
   3972         public void process(MotionEvent event) {
   3973             // Translate the trackball event into DPAD keys and try to deliver those.
   3974             long curTime = SystemClock.uptimeMillis();
   3975             if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) {
   3976                 // It has been too long since the last movement,
   3977                 // so restart at the beginning.
   3978                 mX.reset(0);
   3979                 mY.reset(0);
   3980                 mLastTime = curTime;
   3981             }
   3982 
   3983             final int action = event.getAction();
   3984             final int metaState = event.getMetaState();
   3985             switch (action) {
   3986                 case MotionEvent.ACTION_DOWN:
   3987                     mX.reset(2);
   3988                     mY.reset(2);
   3989                     enqueueInputEvent(new KeyEvent(curTime, curTime,
   3990                             KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
   3991                             KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   3992                             InputDevice.SOURCE_KEYBOARD));
   3993                     break;
   3994                 case MotionEvent.ACTION_UP:
   3995                     mX.reset(2);
   3996                     mY.reset(2);
   3997                     enqueueInputEvent(new KeyEvent(curTime, curTime,
   3998                             KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
   3999                             KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   4000                             InputDevice.SOURCE_KEYBOARD));
   4001                     break;
   4002             }
   4003 
   4004             if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + mX.position + " step="
   4005                     + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration
   4006                     + " move=" + event.getX()
   4007                     + " / Y=" + mY.position + " step="
   4008                     + mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration
   4009                     + " move=" + event.getY());
   4010             final float xOff = mX.collect(event.getX(), event.getEventTime(), "X");
   4011             final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y");
   4012 
   4013             // Generate DPAD events based on the trackball movement.
   4014             // We pick the axis that has moved the most as the direction of
   4015             // the DPAD.  When we generate DPAD events for one axis, then the
   4016             // other axis is reset -- we don't want to perform DPAD jumps due
   4017             // to slight movements in the trackball when making major movements
   4018             // along the other axis.
   4019             int keycode = 0;
   4020             int movement = 0;
   4021             float accel = 1;
   4022             if (xOff > yOff) {
   4023                 movement = mX.generate();
   4024                 if (movement != 0) {
   4025                     keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
   4026                             : KeyEvent.KEYCODE_DPAD_LEFT;
   4027                     accel = mX.acceleration;
   4028                     mY.reset(2);
   4029                 }
   4030             } else if (yOff > 0) {
   4031                 movement = mY.generate();
   4032                 if (movement != 0) {
   4033                     keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
   4034                             : KeyEvent.KEYCODE_DPAD_UP;
   4035                     accel = mY.acceleration;
   4036                     mX.reset(2);
   4037                 }
   4038             }
   4039 
   4040             if (keycode != 0) {
   4041                 if (movement < 0) movement = -movement;
   4042                 int accelMovement = (int)(movement * accel);
   4043                 if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
   4044                         + " accelMovement=" + accelMovement
   4045                         + " accel=" + accel);
   4046                 if (accelMovement > movement) {
   4047                     if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
   4048                             + keycode);
   4049                     movement--;
   4050                     int repeatCount = accelMovement - movement;
   4051                     enqueueInputEvent(new KeyEvent(curTime, curTime,
   4052                             KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
   4053                             KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   4054                             InputDevice.SOURCE_KEYBOARD));
   4055                 }
   4056                 while (movement > 0) {
   4057                     if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
   4058                             + keycode);
   4059                     movement--;
   4060                     curTime = SystemClock.uptimeMillis();
   4061                     enqueueInputEvent(new KeyEvent(curTime, curTime,
   4062                             KeyEvent.ACTION_DOWN, keycode, 0, metaState,
   4063                             KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   4064                             InputDevice.SOURCE_KEYBOARD));
   4065                     enqueueInputEvent(new KeyEvent(curTime, curTime,
   4066                             KeyEvent.ACTION_UP, keycode, 0, metaState,
   4067                             KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   4068                             InputDevice.SOURCE_KEYBOARD));
   4069                 }
   4070                 mLastTime = curTime;
   4071             }
   4072         }
   4073 
   4074         public void cancel(MotionEvent event) {
   4075             mLastTime = Integer.MIN_VALUE;
   4076 
   4077             // If we reach this, we consumed a trackball event.
   4078             // Because we will not translate the trackball event into a key event,
   4079             // touch mode will not exit, so we exit touch mode here.
   4080             if (mView != null && mAdded) {
   4081                 ensureTouchMode(false);
   4082             }
   4083         }
   4084     }
   4085 
   4086     /**
   4087      * Maintains state information for a single trackball axis, generating
   4088      * discrete (DPAD) movements based on raw trackball motion.
   4089      */
   4090     static final class TrackballAxis {
   4091         /**
   4092          * The maximum amount of acceleration we will apply.
   4093          */
   4094         static final float MAX_ACCELERATION = 20;
   4095 
   4096         /**
   4097          * The maximum amount of time (in milliseconds) between events in order
   4098          * for us to consider the user to be doing fast trackball movements,
   4099          * and thus apply an acceleration.
   4100          */
   4101         static final long FAST_MOVE_TIME = 150;
   4102 
   4103         /**
   4104          * Scaling factor to the time (in milliseconds) between events to how
   4105          * much to multiple/divide the current acceleration.  When movement
   4106          * is < FAST_MOVE_TIME this multiplies the acceleration; when >
   4107          * FAST_MOVE_TIME it divides it.
   4108          */
   4109         static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
   4110 
   4111         static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
   4112         static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
   4113         static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
   4114 
   4115         float position;
   4116         float acceleration = 1;
   4117         long lastMoveTime = 0;
   4118         int step;
   4119         int dir;
   4120         int nonAccelMovement;
   4121 
   4122         void reset(int _step) {
   4123             position = 0;
   4124             acceleration = 1;
   4125             lastMoveTime = 0;
   4126             step = _step;
   4127             dir = 0;
   4128         }
   4129 
   4130         /**
   4131          * Add trackball movement into the state.  If the direction of movement
   4132          * has been reversed, the state is reset before adding the
   4133          * movement (so that you don't have to compensate for any previously
   4134          * collected movement before see the result of the movement in the
   4135          * new direction).
   4136          *
   4137          * @return Returns the absolute value of the amount of movement
   4138          * collected so far.
   4139          */
   4140         float collect(float off, long time, String axis) {
   4141             long normTime;
   4142             if (off > 0) {
   4143                 normTime = (long)(off * FAST_MOVE_TIME);
   4144                 if (dir < 0) {
   4145                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
   4146                     position = 0;
   4147                     step = 0;
   4148                     acceleration = 1;
   4149                     lastMoveTime = 0;
   4150                 }
   4151                 dir = 1;
   4152             } else if (off < 0) {
   4153                 normTime = (long)((-off) * FAST_MOVE_TIME);
   4154                 if (dir > 0) {
   4155                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
   4156                     position = 0;
   4157                     step = 0;
   4158                     acceleration = 1;
   4159                     lastMoveTime = 0;
   4160                 }
   4161                 dir = -1;
   4162             } else {
   4163                 normTime = 0;
   4164             }
   4165 
   4166             // The number of milliseconds between each movement that is
   4167             // considered "normal" and will not result in any acceleration
   4168             // or deceleration, scaled by the offset we have here.
   4169             if (normTime > 0) {
   4170                 long delta = time - lastMoveTime;
   4171                 lastMoveTime = time;
   4172                 float acc = acceleration;
   4173                 if (delta < normTime) {
   4174                     // The user is scrolling rapidly, so increase acceleration.
   4175                     float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
   4176                     if (scale > 1) acc *= scale;
   4177                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
   4178                             + off + " normTime=" + normTime + " delta=" + delta
   4179                             + " scale=" + scale + " acc=" + acc);
   4180                     acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
   4181                 } else {
   4182                     // The user is scrolling slowly, so decrease acceleration.
   4183                     float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
   4184                     if (scale > 1) acc /= scale;
   4185                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
   4186                             + off + " normTime=" + normTime + " delta=" + delta
   4187                             + " scale=" + scale + " acc=" + acc);
   4188                     acceleration = acc > 1 ? acc : 1;
   4189                 }
   4190             }
   4191             position += off;
   4192             return Math.abs(position);
   4193         }
   4194 
   4195         /**
   4196          * Generate the number of discrete movement events appropriate for
   4197          * the currently collected trackball movement.
   4198          *
   4199          * @return Returns the number of discrete movements, either positive
   4200          * or negative, or 0 if there is not enough trackball movement yet
   4201          * for a discrete movement.
   4202          */
   4203         int generate() {
   4204             int movement = 0;
   4205             nonAccelMovement = 0;
   4206             do {
   4207                 final int dir = position >= 0 ? 1 : -1;
   4208                 switch (step) {
   4209                     // If we are going to execute the first step, then we want
   4210                     // to do this as soon as possible instead of waiting for
   4211                     // a full movement, in order to make things look responsive.
   4212                     case 0:
   4213                         if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
   4214                             return movement;
   4215                         }
   4216                         movement += dir;
   4217                         nonAccelMovement += dir;
   4218                         step = 1;
   4219                         break;
   4220                     // If we have generated the first movement, then we need
   4221                     // to wait for the second complete trackball motion before
   4222                     // generating the second discrete movement.
   4223                     case 1:
   4224                         if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
   4225                             return movement;
   4226                         }
   4227                         movement += dir;
   4228                         nonAccelMovement += dir;
   4229                         position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
   4230                         step = 2;
   4231                         break;
   4232                     // After the first two, we generate discrete movements
   4233                     // consistently with the trackball, applying an acceleration
   4234                     // if the trackball is moving quickly.  This is a simple
   4235                     // acceleration on top of what we already compute based
   4236                     // on how quickly the wheel is being turned, to apply
   4237                     // a longer increasing acceleration to continuous movement
   4238                     // in one direction.
   4239                     default:
   4240                         if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
   4241                             return movement;
   4242                         }
   4243                         movement += dir;
   4244                         position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
   4245                         float acc = acceleration;
   4246                         acc *= 1.1f;
   4247                         acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
   4248                         break;
   4249                 }
   4250             } while (true);
   4251         }
   4252     }
   4253 
   4254     /**
   4255      * Creates dpad events from unhandled joystick movements.
   4256      */
   4257     final class SyntheticJoystickHandler extends Handler {
   4258         private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1;
   4259         private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2;
   4260 
   4261         private int mLastXDirection;
   4262         private int mLastYDirection;
   4263         private int mLastXKeyCode;
   4264         private int mLastYKeyCode;
   4265 
   4266         public SyntheticJoystickHandler() {
   4267             super(true);
   4268         }
   4269 
   4270         @Override
   4271         public void handleMessage(Message msg) {
   4272             switch (msg.what) {
   4273                 case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
   4274                 case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
   4275                     KeyEvent oldEvent = (KeyEvent)msg.obj;
   4276                     KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
   4277                             SystemClock.uptimeMillis(),
   4278                             oldEvent.getRepeatCount() + 1);
   4279                     if (mAttachInfo.mHasWindowFocus) {
   4280                         enqueueInputEvent(e);
   4281                         Message m = obtainMessage(msg.what, e);
   4282                         m.setAsynchronous(true);
   4283                         sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay());
   4284                     }
   4285                 } break;
   4286             }
   4287         }
   4288 
   4289         public void process(MotionEvent event) {
   4290             update(event, true);
   4291         }
   4292 
   4293         public void cancel(MotionEvent event) {
   4294             update(event, false);
   4295         }
   4296 
   4297         private void update(MotionEvent event, boolean synthesizeNewKeys) {
   4298             final long time = event.getEventTime();
   4299             final int metaState = event.getMetaState();
   4300             final int deviceId = event.getDeviceId();
   4301             final int source = event.getSource();
   4302 
   4303             int xDirection = joystickAxisValueToDirection(
   4304                     event.getAxisValue(MotionEvent.AXIS_HAT_X));
   4305             if (xDirection == 0) {
   4306                 xDirection = joystickAxisValueToDirection(event.getX());
   4307             }
   4308 
   4309             int yDirection = joystickAxisValueToDirection(
   4310                     event.getAxisValue(MotionEvent.AXIS_HAT_Y));
   4311             if (yDirection == 0) {
   4312                 yDirection = joystickAxisValueToDirection(event.getY());
   4313             }
   4314 
   4315             if (xDirection != mLastXDirection) {
   4316                 if (mLastXKeyCode != 0) {
   4317                     removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
   4318                     enqueueInputEvent(new KeyEvent(time, time,
   4319                             KeyEvent.ACTION_UP, mLastXKeyCode, 0, metaState,
   4320                             deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
   4321                     mLastXKeyCode = 0;
   4322                 }
   4323 
   4324                 mLastXDirection = xDirection;
   4325 
   4326                 if (xDirection != 0 && synthesizeNewKeys) {
   4327                     mLastXKeyCode = xDirection > 0
   4328                             ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
   4329                     final KeyEvent e = new KeyEvent(time, time,
   4330                             KeyEvent.ACTION_DOWN, mLastXKeyCode, 0, metaState,
   4331                             deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
   4332                     enqueueInputEvent(e);
   4333                     Message m = obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
   4334                     m.setAsynchronous(true);
   4335                     sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
   4336                 }
   4337             }
   4338 
   4339             if (yDirection != mLastYDirection) {
   4340                 if (mLastYKeyCode != 0) {
   4341                     removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
   4342                     enqueueInputEvent(new KeyEvent(time, time,
   4343                             KeyEvent.ACTION_UP, mLastYKeyCode, 0, metaState,
   4344                             deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
   4345                     mLastYKeyCode = 0;
   4346                 }
   4347 
   4348                 mLastYDirection = yDirection;
   4349 
   4350                 if (yDirection != 0 && synthesizeNewKeys) {
   4351                     mLastYKeyCode = yDirection > 0
   4352                             ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
   4353                     final KeyEvent e = new KeyEvent(time, time,
   4354                             KeyEvent.ACTION_DOWN, mLastYKeyCode, 0, metaState,
   4355                             deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
   4356                     enqueueInputEvent(e);
   4357                     Message m = obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
   4358                     m.setAsynchronous(true);
   4359                     sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
   4360                 }
   4361             }
   4362         }
   4363 
   4364         private int joystickAxisValueToDirection(float value) {
   4365             if (value >= 0.5f) {
   4366                 return 1;
   4367             } else if (value <= -0.5f) {
   4368                 return -1;
   4369             } else {
   4370                 return 0;
   4371             }
   4372         }
   4373     }
   4374 
   4375     /**
   4376      * Creates dpad events from unhandled touch navigation movements.
   4377      */
   4378     final class SyntheticTouchNavigationHandler extends Handler {
   4379         private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler";
   4380         private static final boolean LOCAL_DEBUG = false;
   4381 
   4382         // Assumed nominal width and height in millimeters of a touch navigation pad,
   4383         // if no resolution information is available from the input system.
   4384         private static final float DEFAULT_WIDTH_MILLIMETERS = 48;
   4385         private static final float DEFAULT_HEIGHT_MILLIMETERS = 48;
   4386 
   4387         /* TODO: These constants should eventually be moved to ViewConfiguration. */
   4388 
   4389         // Tap timeout in milliseconds.
   4390         private static final int TAP_TIMEOUT = 250;
   4391 
   4392         // The maximum distance traveled for a gesture to be considered a tap in millimeters.
   4393         private static final int TAP_SLOP_MILLIMETERS = 5;
   4394 
   4395         // The nominal distance traveled to move by one unit.
   4396         private static final int TICK_DISTANCE_MILLIMETERS = 12;
   4397 
   4398         // Minimum and maximum fling velocity in ticks per second.
   4399         // The minimum velocity should be set such that we perform enough ticks per
   4400         // second that the fling appears to be fluid.  For example, if we set the minimum
   4401         // to 2 ticks per second, then there may be up to half a second delay between the next
   4402         // to last and last ticks which is noticeably discrete and jerky.  This value should
   4403         // probably not be set to anything less than about 4.
   4404         // If fling accuracy is a problem then consider tuning the tick distance instead.
   4405         private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f;
   4406         private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f;
   4407 
   4408         // Fling velocity decay factor applied after each new key is emitted.
   4409         // This parameter controls the deceleration and overall duration of the fling.
   4410         // The fling stops automatically when its velocity drops below the minimum
   4411         // fling velocity defined above.
   4412         private static final float FLING_TICK_DECAY = 0.8f;
   4413 
   4414         /* The input device that we are tracking. */
   4415 
   4416         private int mCurrentDeviceId = -1;
   4417         private int mCurrentSource;
   4418         private boolean mCurrentDeviceSupported;
   4419 
   4420         /* Configuration for the current input device. */
   4421 
   4422         // The tap timeout and scaled slop.
   4423         private int mConfigTapTimeout;
   4424         private float mConfigTapSlop;
   4425 
   4426         // The scaled tick distance.  A movement of this amount should generally translate
   4427         // into a single dpad event in a given direction.
   4428         private float mConfigTickDistance;
   4429 
   4430         // The minimum and maximum scaled fling velocity.
   4431         private float mConfigMinFlingVelocity;
   4432         private float mConfigMaxFlingVelocity;
   4433 
   4434         /* Tracking state. */
   4435 
   4436         // The velocity tracker for detecting flings.
   4437         private VelocityTracker mVelocityTracker;
   4438 
   4439         // The active pointer id, or -1 if none.
   4440         private int mActivePointerId = -1;
   4441 
   4442         // Time and location where tracking started.
   4443         private long mStartTime;
   4444         private float mStartX;
   4445         private float mStartY;
   4446 
   4447         // Most recently observed position.
   4448         private float mLastX;
   4449         private float mLastY;
   4450 
   4451         // Accumulated movement delta since the last direction key was sent.
   4452         private float mAccumulatedX;
   4453         private float mAccumulatedY;
   4454 
   4455         // Set to true if any movement was delivered to the app.
   4456         // Implies that tap slop was exceeded.
   4457         private boolean mConsumedMovement;
   4458 
   4459         // The most recently sent key down event.
   4460         // The keycode remains set until the direction changes or a fling ends
   4461         // so that repeated key events may be generated as required.
   4462         private long mPendingKeyDownTime;
   4463         private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
   4464         private int mPendingKeyRepeatCount;
   4465         private int mPendingKeyMetaState;
   4466 
   4467         // The current fling velocity while a fling is in progress.
   4468         private boolean mFlinging;
   4469         private float mFlingVelocity;
   4470 
   4471         public SyntheticTouchNavigationHandler() {
   4472             super(true);
   4473         }
   4474 
   4475         public void process(MotionEvent event) {
   4476             // Update the current device information.
   4477             final long time = event.getEventTime();
   4478             final int deviceId = event.getDeviceId();
   4479             final int source = event.getSource();
   4480             if (mCurrentDeviceId != deviceId || mCurrentSource != source) {
   4481                 finishKeys(time);
   4482                 finishTracking(time);
   4483                 mCurrentDeviceId = deviceId;
   4484                 mCurrentSource = source;
   4485                 mCurrentDeviceSupported = false;
   4486                 InputDevice device = event.getDevice();
   4487                 if (device != null) {
   4488                     // In order to support an input device, we must know certain
   4489                     // characteristics about it, such as its size and resolution.
   4490                     InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X);
   4491                     InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y);
   4492                     if (xRange != null && yRange != null) {
   4493                         mCurrentDeviceSupported = true;
   4494 
   4495                         // Infer the resolution if it not actually known.
   4496                         float xRes = xRange.getResolution();
   4497                         if (xRes <= 0) {
   4498                             xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS;
   4499                         }
   4500                         float yRes = yRange.getResolution();
   4501                         if (yRes <= 0) {
   4502                             yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS;
   4503                         }
   4504                         float nominalRes = (xRes + yRes) * 0.5f;
   4505 
   4506                         // Precompute all of the configuration thresholds we will need.
   4507                         mConfigTapTimeout = TAP_TIMEOUT;
   4508                         mConfigTapSlop = TAP_SLOP_MILLIMETERS * nominalRes;
   4509                         mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes;
   4510                         mConfigMinFlingVelocity =
   4511                                 MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
   4512                         mConfigMaxFlingVelocity =
   4513                                 MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
   4514 
   4515                         if (LOCAL_DEBUG) {
   4516                             Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
   4517                                     + " (" + Integer.toHexString(mCurrentSource) + "): "
   4518                                     + "mConfigTapTimeout=" + mConfigTapTimeout
   4519                                     + ", mConfigTapSlop=" + mConfigTapSlop
   4520                                     + ", mConfigTickDistance=" + mConfigTickDistance
   4521                                     + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity
   4522                                     + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity);
   4523                         }
   4524                     }
   4525                 }
   4526             }
   4527             if (!mCurrentDeviceSupported) {
   4528                 return;
   4529             }
   4530 
   4531             // Handle the event.
   4532             final int action = event.getActionMasked();
   4533             switch (action) {
   4534                 case MotionEvent.ACTION_DOWN: {
   4535                     boolean caughtFling = mFlinging;
   4536                     finishKeys(time);
   4537                     finishTracking(time);
   4538                     mActivePointerId = event.getPointerId(0);
   4539                     mVelocityTracker = VelocityTracker.obtain();
   4540                     mVelocityTracker.addMovement(event);
   4541                     mStartTime = time;
   4542                     mStartX = event.getX();
   4543                     mStartY = event.getY();
   4544                     mLastX = mStartX;
   4545                     mLastY = mStartY;
   4546                     mAccumulatedX = 0;
   4547                     mAccumulatedY = 0;
   4548 
   4549                     // If we caught a fling, then pretend that the tap slop has already
   4550                     // been exceeded to suppress taps whose only purpose is to stop the fling.
   4551                     mConsumedMovement = caughtFling;
   4552                     break;
   4553                 }
   4554 
   4555                 case MotionEvent.ACTION_MOVE:
   4556                 case MotionEvent.ACTION_UP: {
   4557                     if (mActivePointerId < 0) {
   4558                         break;
   4559                     }
   4560                     final int index = event.findPointerIndex(mActivePointerId);
   4561                     if (index < 0) {
   4562                         finishKeys(time);
   4563                         finishTracking(time);
   4564                         break;
   4565                     }
   4566 
   4567                     mVelocityTracker.addMovement(event);
   4568                     final float x = event.getX(index);
   4569                     final float y = event.getY(index);
   4570                     mAccumulatedX += x - mLastX;
   4571                     mAccumulatedY += y - mLastY;
   4572                     mLastX = x;
   4573                     mLastY = y;
   4574 
   4575                     // Consume any accumulated movement so far.
   4576                     final int metaState = event.getMetaState();
   4577                     consumeAccumulatedMovement(time, metaState);
   4578 
   4579                     // Detect taps and flings.
   4580                     if (action == MotionEvent.ACTION_UP) {
   4581                         if (!mConsumedMovement
   4582                                 && Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop
   4583                                 && time <= mStartTime + mConfigTapTimeout) {
   4584                             // It's a tap!
   4585                             finishKeys(time);
   4586                             sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
   4587                             sendKeyUp(time);
   4588                         } else if (mConsumedMovement
   4589                                 && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
   4590                             // It might be a fling.
   4591                             mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity);
   4592                             final float vx = mVelocityTracker.getXVelocity(mActivePointerId);
   4593                             final float vy = mVelocityTracker.getYVelocity(mActivePointerId);
   4594                             if (!startFling(time, vx, vy)) {
   4595                                 finishKeys(time);
   4596                             }
   4597                         }
   4598                         finishTracking(time);
   4599                     }
   4600                     break;
   4601                 }
   4602 
   4603                 case MotionEvent.ACTION_CANCEL: {
   4604                     finishKeys(time);
   4605                     finishTracking(time);
   4606                     break;
   4607                 }
   4608             }
   4609         }
   4610 
   4611         public void cancel(MotionEvent event) {
   4612             if (mCurrentDeviceId == event.getDeviceId()
   4613                     && mCurrentSource == event.getSource()) {
   4614                 final long time = event.getEventTime();
   4615                 finishKeys(time);
   4616                 finishTracking(time);
   4617             }
   4618         }
   4619 
   4620         private void finishKeys(long time) {
   4621             cancelFling();
   4622             sendKeyUp(time);
   4623         }
   4624 
   4625         private void finishTracking(long time) {
   4626             if (mActivePointerId >= 0) {
   4627                 mActivePointerId = -1;
   4628                 mVelocityTracker.recycle();
   4629                 mVelocityTracker = null;
   4630             }
   4631         }
   4632 
   4633         private void consumeAccumulatedMovement(long time, int metaState) {
   4634             final float absX = Math.abs(mAccumulatedX);
   4635             final float absY = Math.abs(mAccumulatedY);
   4636             if (absX >= absY) {
   4637                 if (absX >= mConfigTickDistance) {
   4638                     mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX,
   4639                             KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT);
   4640                     mAccumulatedY = 0;
   4641                     mConsumedMovement = true;
   4642                 }
   4643             } else {
   4644                 if (absY >= mConfigTickDistance) {
   4645                     mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY,
   4646                             KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN);
   4647                     mAccumulatedX = 0;
   4648                     mConsumedMovement = true;
   4649                 }
   4650             }
   4651         }
   4652 
   4653         private float consumeAccumulatedMovement(long time, int metaState,
   4654                 float accumulator, int negativeKeyCode, int positiveKeyCode) {
   4655             while (accumulator <= -mConfigTickDistance) {
   4656                 sendKeyDownOrRepeat(time, negativeKeyCode, metaState);
   4657                 accumulator += mConfigTickDistance;
   4658             }
   4659             while (accumulator >= mConfigTickDistance) {
   4660                 sendKeyDownOrRepeat(time, positiveKeyCode, metaState);
   4661                 accumulator -= mConfigTickDistance;
   4662             }
   4663             return accumulator;
   4664         }
   4665 
   4666         private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) {
   4667             if (mPendingKeyCode != keyCode) {
   4668                 sendKeyUp(time);
   4669                 mPendingKeyDownTime = time;
   4670                 mPendingKeyCode = keyCode;
   4671                 mPendingKeyRepeatCount = 0;
   4672             } else {
   4673                 mPendingKeyRepeatCount += 1;
   4674             }
   4675             mPendingKeyMetaState = metaState;
   4676 
   4677             // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1
   4678             // but it doesn't quite make sense when simulating the events in this way.
   4679             if (LOCAL_DEBUG) {
   4680                 Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode
   4681                         + ", repeatCount=" + mPendingKeyRepeatCount
   4682                         + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
   4683             }
   4684             enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
   4685                     KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount,
   4686                     mPendingKeyMetaState, mCurrentDeviceId,
   4687                     KeyEvent.FLAG_FALLBACK, mCurrentSource));
   4688         }
   4689 
   4690         private void sendKeyUp(long time) {
   4691             if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
   4692                 if (LOCAL_DEBUG) {
   4693                     Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode
   4694                             + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
   4695                 }
   4696                 enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
   4697                         KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState,
   4698                         mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK,
   4699                         mCurrentSource));
   4700                 mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
   4701             }
   4702         }
   4703 
   4704         private boolean startFling(long time, float vx, float vy) {
   4705             if (LOCAL_DEBUG) {
   4706                 Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy
   4707                         + ", min=" + mConfigMinFlingVelocity);
   4708             }
   4709 
   4710             // Flings must be oriented in the same direction as the preceding movements.
   4711             switch (mPendingKeyCode) {
   4712                 case KeyEvent.KEYCODE_DPAD_LEFT:
   4713                     if (-vx >= mConfigMinFlingVelocity
   4714                             && Math.abs(vy) < mConfigMinFlingVelocity) {
   4715                         mFlingVelocity = -vx;
   4716                         break;
   4717                     }
   4718                     return false;
   4719 
   4720                 case KeyEvent.KEYCODE_DPAD_RIGHT:
   4721                     if (vx >= mConfigMinFlingVelocity
   4722                             && Math.abs(vy) < mConfigMinFlingVelocity) {
   4723                         mFlingVelocity = vx;
   4724                         break;
   4725                     }
   4726                     return false;
   4727 
   4728                 case KeyEvent.KEYCODE_DPAD_UP:
   4729                     if (-vy >= mConfigMinFlingVelocity
   4730                             && Math.abs(vx) < mConfigMinFlingVelocity) {
   4731                         mFlingVelocity = -vy;
   4732                         break;
   4733                     }
   4734                     return false;
   4735 
   4736                 case KeyEvent.KEYCODE_DPAD_DOWN:
   4737                     if (vy >= mConfigMinFlingVelocity
   4738                             && Math.abs(vx) < mConfigMinFlingVelocity) {
   4739                         mFlingVelocity = vy;
   4740                         break;
   4741                     }
   4742                     return false;
   4743             }
   4744 
   4745             // Post the first fling event.
   4746             mFlinging = postFling(time);
   4747             return mFlinging;
   4748         }
   4749 
   4750         private boolean postFling(long time) {
   4751             // The idea here is to estimate the time when the pointer would have
   4752             // traveled one tick distance unit given the current fling velocity.
   4753             // This effect creates continuity of motion.
   4754             if (mFlingVelocity >= mConfigMinFlingVelocity) {
   4755                 long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000);
   4756                 postAtTime(mFlingRunnable, time + delay);
   4757                 if (LOCAL_DEBUG) {
   4758                     Log.d(LOCAL_TAG, "Posted fling: velocity="
   4759                             + mFlingVelocity + ", delay=" + delay
   4760                             + ", keyCode=" + mPendingKeyCode);
   4761                 }
   4762                 return true;
   4763             }
   4764             return false;
   4765         }
   4766 
   4767         private void cancelFling() {
   4768             if (mFlinging) {
   4769                 removeCallbacks(mFlingRunnable);
   4770                 mFlinging = false;
   4771             }
   4772         }
   4773 
   4774         private final Runnable mFlingRunnable = new Runnable() {
   4775             @Override
   4776             public void run() {
   4777                 final long time = SystemClock.uptimeMillis();
   4778                 sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState);
   4779                 mFlingVelocity *= FLING_TICK_DECAY;
   4780                 if (!postFling(time)) {
   4781                     mFlinging = false;
   4782                     finishKeys(time);
   4783                 }
   4784             }
   4785         };
   4786     }
   4787 
   4788     /**
   4789      * Returns true if the key is used for keyboard navigation.
   4790      * @param keyEvent The key event.
   4791      * @return True if the key is used for keyboard navigation.
   4792      */
   4793     private static boolean isNavigationKey(KeyEvent keyEvent) {
   4794         switch (keyEvent.getKeyCode()) {
   4795         case KeyEvent.KEYCODE_DPAD_LEFT:
   4796         case KeyEvent.KEYCODE_DPAD_RIGHT:
   4797         case KeyEvent.KEYCODE_DPAD_UP:
   4798         case KeyEvent.KEYCODE_DPAD_DOWN:
   4799         case KeyEvent.KEYCODE_DPAD_CENTER:
   4800         case KeyEvent.KEYCODE_PAGE_UP:
   4801         case KeyEvent.KEYCODE_PAGE_DOWN:
   4802         case KeyEvent.KEYCODE_MOVE_HOME:
   4803         case KeyEvent.KEYCODE_MOVE_END:
   4804         case KeyEvent.KEYCODE_TAB:
   4805         case KeyEvent.KEYCODE_SPACE:
   4806         case KeyEvent.KEYCODE_ENTER:
   4807             return true;
   4808         }
   4809         return false;
   4810     }
   4811 
   4812     /**
   4813      * Returns true if the key is used for typing.
   4814      * @param keyEvent The key event.
   4815      * @return True if the key is used for typing.
   4816      */
   4817     private static boolean isTypingKey(KeyEvent keyEvent) {
   4818         return keyEvent.getUnicodeChar() > 0;
   4819     }
   4820 
   4821     /**
   4822      * See if the key event means we should leave touch mode (and leave touch mode if so).
   4823      * @param event The key event.
   4824      * @return Whether this key event should be consumed (meaning the act of
   4825      *   leaving touch mode alone is considered the event).
   4826      */
   4827     private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
   4828         // Only relevant in touch mode.
   4829         if (!mAttachInfo.mInTouchMode) {
   4830             return false;
   4831         }
   4832 
   4833         // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP.
   4834         final int action = event.getAction();
   4835         if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) {
   4836             return false;
   4837         }
   4838 
   4839         // Don't leave touch mode if the IME told us not to.
   4840         if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
   4841             return false;
   4842         }
   4843 
   4844         // If the key can be used for keyboard navigation then leave touch mode
   4845         // and select a focused view if needed (in ensureTouchMode).
   4846         // When a new focused view is selected, we consume the navigation key because
   4847         // navigation doesn't make much sense unless a view already has focus so
   4848         // the key's purpose is to set focus.
   4849         if (isNavigationKey(event)) {
   4850             return ensureTouchMode(false);
   4851         }
   4852 
   4853         // If the key can be used for typing then leave touch mode
   4854         // and select a focused view if needed (in ensureTouchMode).
   4855         // Always allow the view to process the typing key.
   4856         if (isTypingKey(event)) {
   4857             ensureTouchMode(false);
   4858             return false;
   4859         }
   4860 
   4861         return false;
   4862     }
   4863 
   4864     /* drag/drop */
   4865     void setLocalDragState(Object obj) {
   4866         mLocalDragState = obj;
   4867     }
   4868 
   4869     private void handleDragEvent(DragEvent event) {
   4870         // From the root, only drag start/end/location are dispatched.  entered/exited
   4871         // are determined and dispatched by the viewgroup hierarchy, who then report
   4872         // that back here for ultimate reporting back to the framework.
   4873         if (mView != null && mAdded) {
   4874             final int what = event.mAction;
   4875 
   4876             if (what == DragEvent.ACTION_DRAG_EXITED) {
   4877                 // A direct EXITED event means that the window manager knows we've just crossed
   4878                 // a window boundary, so the current drag target within this one must have
   4879                 // just been exited.  Send it the usual notifications and then we're done
   4880                 // for now.
   4881                 mView.dispatchDragEvent(event);
   4882             } else {
   4883                 // Cache the drag description when the operation starts, then fill it in
   4884                 // on subsequent calls as a convenience
   4885                 if (what == DragEvent.ACTION_DRAG_STARTED) {
   4886                     mCurrentDragView = null;    // Start the current-recipient tracking
   4887                     mDragDescription = event.mClipDescription;
   4888                 } else {
   4889                     event.mClipDescription = mDragDescription;
   4890                 }
   4891 
   4892                 // For events with a [screen] location, translate into window coordinates
   4893                 if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) {
   4894                     mDragPoint.set(event.mX, event.mY);
   4895                     if (mTranslator != null) {
   4896                         mTranslator.translatePointInScreenToAppWindow(mDragPoint);
   4897                     }
   4898 
   4899                     if (mCurScrollY != 0) {
   4900                         mDragPoint.offset(0, mCurScrollY);
   4901                     }
   4902 
   4903                     event.mX = mDragPoint.x;
   4904                     event.mY = mDragPoint.y;
   4905                 }
   4906 
   4907                 // Remember who the current drag target is pre-dispatch
   4908                 final View prevDragView = mCurrentDragView;
   4909 
   4910                 // Now dispatch the drag/drop event
   4911                 boolean result = mView.dispatchDragEvent(event);
   4912 
   4913                 // If we changed apparent drag target, tell the OS about it
   4914                 if (prevDragView != mCurrentDragView) {
   4915                     try {
   4916                         if (prevDragView != null) {
   4917                             mWindowSession.dragRecipientExited(mWindow);
   4918                         }
   4919                         if (mCurrentDragView != null) {
   4920                             mWindowSession.dragRecipientEntered(mWindow);
   4921                         }
   4922                     } catch (RemoteException e) {
   4923                         Slog.e(TAG, "Unable to note drag target change");
   4924                     }
   4925                 }
   4926 
   4927                 // Report the drop result when we're done
   4928                 if (what == DragEvent.ACTION_DROP) {
   4929                     mDragDescription = null;
   4930                     try {
   4931                         Log.i(TAG, "Reporting drop result: " + result);
   4932                         mWindowSession.reportDropResult(mWindow, result);
   4933                     } catch (RemoteException e) {
   4934                         Log.e(TAG, "Unable to report drop result");
   4935                     }
   4936                 }
   4937 
   4938                 // When the drag operation ends, release any local state object
   4939                 // that may have been in use
   4940                 if (what == DragEvent.ACTION_DRAG_ENDED) {
   4941                     setLocalDragState(null);
   4942                 }
   4943             }
   4944         }
   4945         event.recycle();
   4946     }
   4947 
   4948     public void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) {
   4949         if (mSeq != args.seq) {
   4950             // The sequence has changed, so we need to update our value and make
   4951             // sure to do a traversal afterward so the window manager is given our
   4952             // most recent data.
   4953             mSeq = args.seq;
   4954             mAttachInfo.mForceReportNewAttributes = true;
   4955             scheduleTraversals();
   4956         }
   4957         if (mView == null) return;
   4958         if (args.localChanges != 0) {
   4959             mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
   4960         }
   4961         if (mAttachInfo != null) {
   4962             int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
   4963             if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
   4964                 mAttachInfo.mGlobalSystemUiVisibility = visibility;
   4965                 mView.dispatchSystemUiVisibilityChanged(visibility);
   4966             }
   4967         }
   4968     }
   4969 
   4970     public void handleDispatchDoneAnimating() {
   4971         if (mWindowsAnimating) {
   4972             mWindowsAnimating = false;
   4973             if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded)  {
   4974                 scheduleTraversals();
   4975             }
   4976         }
   4977     }
   4978 
   4979     public void getLastTouchPoint(Point outLocation) {
   4980         outLocation.x = (int) mLastTouchPoint.x;
   4981         outLocation.y = (int) mLastTouchPoint.y;
   4982     }
   4983 
   4984     public void setDragFocus(View newDragTarget) {
   4985         if (mCurrentDragView != newDragTarget) {
   4986             mCurrentDragView = newDragTarget;
   4987         }
   4988     }
   4989 
   4990     private AudioManager getAudioManager() {
   4991         if (mView == null) {
   4992             throw new IllegalStateException("getAudioManager called when there is no mView");
   4993         }
   4994         if (mAudioManager == null) {
   4995             mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE);
   4996         }
   4997         return mAudioManager;
   4998     }
   4999 
   5000     public AccessibilityInteractionController getAccessibilityInteractionController() {
   5001         if (mView == null) {
   5002             throw new IllegalStateException("getAccessibilityInteractionController"
   5003                     + " called when there is no mView");
   5004         }
   5005         if (mAccessibilityInteractionController == null) {
   5006             mAccessibilityInteractionController = new AccessibilityInteractionController(this);
   5007         }
   5008         return mAccessibilityInteractionController;
   5009     }
   5010 
   5011     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
   5012             boolean insetsPending) throws RemoteException {
   5013 
   5014         float appScale = mAttachInfo.mApplicationScale;
   5015         boolean restore = false;
   5016         if (params != null && mTranslator != null) {
   5017             restore = true;
   5018             params.backup();
   5019             mTranslator.translateWindowLayout(params);
   5020         }
   5021         if (params != null) {
   5022             if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
   5023         }
   5024         mPendingConfiguration.seq = 0;
   5025         //Log.d(TAG, ">>>>>> CALLING relayout");
   5026         if (params != null && mOrigWindowType != params.type) {
   5027             // For compatibility with old apps, don't crash here.
   5028             if (mTargetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
   5029                 Slog.w(TAG, "Window type can not be changed after "
   5030                         + "the window is added; ignoring change of " + mView);
   5031                 params.type = mOrigWindowType;
   5032             }
   5033         }
   5034         int relayoutResult = mWindowSession.relayout(
   5035                 mWindow, mSeq, params,
   5036                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
   5037                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
   5038                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
   5039                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
   5040                 mPendingConfiguration, mSurface);
   5041         //Log.d(TAG, "<<<<<< BACK FROM relayout");
   5042         if (restore) {
   5043             params.restore();
   5044         }
   5045 
   5046         if (mTranslator != null) {
   5047             mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
   5048             mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
   5049             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
   5050             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
   5051         }
   5052         return relayoutResult;
   5053     }
   5054 
   5055     /**
   5056      * {@inheritDoc}
   5057      */
   5058     public void playSoundEffect(int effectId) {
   5059         checkThread();
   5060 
   5061         try {
   5062             final AudioManager audioManager = getAudioManager();
   5063 
   5064             switch (effectId) {
   5065                 case SoundEffectConstants.CLICK:
   5066                     audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
   5067                     return;
   5068                 case SoundEffectConstants.NAVIGATION_DOWN:
   5069                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
   5070                     return;
   5071                 case SoundEffectConstants.NAVIGATION_LEFT:
   5072                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
   5073                     return;
   5074                 case SoundEffectConstants.NAVIGATION_RIGHT:
   5075                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
   5076                     return;
   5077                 case SoundEffectConstants.NAVIGATION_UP:
   5078                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
   5079                     return;
   5080                 default:
   5081                     throw new IllegalArgumentException("unknown effect id " + effectId +
   5082                             " not defined in " + SoundEffectConstants.class.getCanonicalName());
   5083             }
   5084         } catch (IllegalStateException e) {
   5085             // Exception thrown by getAudioManager() when mView is null
   5086             Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
   5087             e.printStackTrace();
   5088         }
   5089     }
   5090 
   5091     /**
   5092      * {@inheritDoc}
   5093      */
   5094     public boolean performHapticFeedback(int effectId, boolean always) {
   5095         try {
   5096             return mWindowSession.performHapticFeedback(mWindow, effectId, always);
   5097         } catch (RemoteException e) {
   5098             return false;
   5099         }
   5100     }
   5101 
   5102     /**
   5103      * {@inheritDoc}
   5104      */
   5105     public View focusSearch(View focused, int direction) {
   5106         checkThread();
   5107         if (!(mView instanceof ViewGroup)) {
   5108             return null;
   5109         }
   5110         return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
   5111     }
   5112 
   5113     public void debug() {
   5114         mView.debug();
   5115     }
   5116 
   5117     public void dumpGfxInfo(int[] info) {
   5118         info[0] = info[1] = 0;
   5119         if (mView != null) {
   5120             getGfxInfo(mView, info);
   5121         }
   5122     }
   5123 
   5124     private static void getGfxInfo(View view, int[] info) {
   5125         DisplayList displayList = view.mDisplayList;
   5126         info[0]++;
   5127         if (displayList != null) {
   5128             info[1] += displayList.getSize();
   5129         }
   5130 
   5131         if (view instanceof ViewGroup) {
   5132             ViewGroup group = (ViewGroup) view;
   5133 
   5134             int count = group.getChildCount();
   5135             for (int i = 0; i < count; i++) {
   5136                 getGfxInfo(group.getChildAt(i), info);
   5137             }
   5138         }
   5139     }
   5140 
   5141     public void die(boolean immediate) {
   5142         // Make sure we do execute immediately if we are in the middle of a traversal or the damage
   5143         // done by dispatchDetachedFromWindow will cause havoc on return.
   5144         if (immediate && !mIsInTraversal) {
   5145             doDie();
   5146         } else {
   5147             if (!mIsDrawing) {
   5148                 destroyHardwareRenderer();
   5149             } else {
   5150                 Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
   5151                         "  window=" + this + ", title=" + mWindowAttributes.getTitle());
   5152             }
   5153             mHandler.sendEmptyMessage(MSG_DIE);
   5154         }
   5155     }
   5156 
   5157     void doDie() {
   5158         checkThread();
   5159         if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
   5160         synchronized (this) {
   5161             if (mAdded) {
   5162                 dispatchDetachedFromWindow();
   5163             }
   5164 
   5165             if (mAdded && !mFirst) {
   5166                 invalidateDisplayLists();
   5167                 destroyHardwareRenderer();
   5168 
   5169                 if (mView != null) {
   5170                     int viewVisibility = mView.getVisibility();
   5171                     boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
   5172                     if (mWindowAttributesChanged || viewVisibilityChanged) {
   5173                         // If layout params have been changed, first give them
   5174                         // to the window manager to make sure it has the correct
   5175                         // animation info.
   5176                         try {
   5177                             if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
   5178                                     & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
   5179                                 mWindowSession.finishDrawing(mWindow);
   5180                             }
   5181                         } catch (RemoteException e) {
   5182                         }
   5183                     }
   5184 
   5185                     mSurface.release();
   5186                 }
   5187             }
   5188 
   5189             mAdded = false;
   5190         }
   5191     }
   5192 
   5193     public void requestUpdateConfiguration(Configuration config) {
   5194         Message msg = mHandler.obtainMessage(MSG_UPDATE_CONFIGURATION, config);
   5195         mHandler.sendMessage(msg);
   5196     }
   5197 
   5198     public void loadSystemProperties() {
   5199         mHandler.post(new Runnable() {
   5200             @Override
   5201             public void run() {
   5202                 // Profiling
   5203                 mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
   5204                 profileRendering(mAttachInfo.mHasWindowFocus);
   5205 
   5206                 // Hardware rendering
   5207                 if (mAttachInfo.mHardwareRenderer != null) {
   5208                     if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mHolder.getSurface())) {
   5209                         invalidate();
   5210                     }
   5211                 }
   5212 
   5213                 // Layout debugging
   5214                 boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
   5215                 if (layout != mAttachInfo.mDebugLayout) {
   5216                     mAttachInfo.mDebugLayout = layout;
   5217                     if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
   5218                         mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
   5219                     }
   5220                 }
   5221             }
   5222         });
   5223     }
   5224 
   5225     private void destroyHardwareRenderer() {
   5226         AttachInfo attachInfo = mAttachInfo;
   5227         HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer;
   5228 
   5229         if (hardwareRenderer != null) {
   5230             if (mView != null) {
   5231                 hardwareRenderer.destroyHardwareResources(mView);
   5232             }
   5233             hardwareRenderer.destroy(true);
   5234             hardwareRenderer.setRequested(false);
   5235 
   5236             attachInfo.mHardwareRenderer = null;
   5237             attachInfo.mHardwareAccelerated = false;
   5238         }
   5239     }
   5240 
   5241     public void dispatchFinishInputConnection(InputConnection connection) {
   5242         Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection);
   5243         mHandler.sendMessage(msg);
   5244     }
   5245 
   5246     public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
   5247             Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
   5248         if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
   5249                 + " contentInsets=" + contentInsets.toShortString()
   5250                 + " visibleInsets=" + visibleInsets.toShortString()
   5251                 + " reportDraw=" + reportDraw);
   5252         Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
   5253         if (mTranslator != null) {
   5254             mTranslator.translateRectInScreenToAppWindow(frame);
   5255             mTranslator.translateRectInScreenToAppWindow(overscanInsets);
   5256             mTranslator.translateRectInScreenToAppWindow(contentInsets);
   5257             mTranslator.translateRectInScreenToAppWindow(visibleInsets);
   5258         }
   5259         SomeArgs args = SomeArgs.obtain();
   5260         final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
   5261         args.arg1 = sameProcessCall ? new Rect(frame) : frame;
   5262         args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets;
   5263         args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
   5264         args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig;
   5265         args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
   5266         msg.obj = args;
   5267         mHandler.sendMessage(msg);
   5268     }
   5269 
   5270     public void dispatchMoved(int newX, int newY) {
   5271         if (DEBUG_LAYOUT) Log.v(TAG, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
   5272         if (mTranslator != null) {
   5273             PointF point = new PointF(newX, newY);
   5274             mTranslator.translatePointInScreenToAppWindow(point);
   5275             newX = (int) (point.x + 0.5);
   5276             newY = (int) (point.y + 0.5);
   5277         }
   5278         Message msg = mHandler.obtainMessage(MSG_WINDOW_MOVED, newX, newY);
   5279         mHandler.sendMessage(msg);
   5280     }
   5281 
   5282     /**
   5283      * Represents a pending input event that is waiting in a queue.
   5284      *
   5285      * Input events are processed in serial order by the timestamp specified by
   5286      * {@link InputEvent#getEventTimeNano()}.  In general, the input dispatcher delivers
   5287      * one input event to the application at a time and waits for the application
   5288      * to finish handling it before delivering the next one.
   5289      *
   5290      * However, because the application or IME can synthesize and inject multiple
   5291      * key events at a time without going through the input dispatcher, we end up
   5292      * needing a queue on the application's side.
   5293      */
   5294     private static final class QueuedInputEvent {
   5295         public static final int FLAG_DELIVER_POST_IME = 1 << 0;
   5296         public static final int FLAG_DEFERRED = 1 << 1;
   5297         public static final int FLAG_FINISHED = 1 << 2;
   5298         public static final int FLAG_FINISHED_HANDLED = 1 << 3;
   5299         public static final int FLAG_RESYNTHESIZED = 1 << 4;
   5300 
   5301         public QueuedInputEvent mNext;
   5302 
   5303         public InputEvent mEvent;
   5304         public InputEventReceiver mReceiver;
   5305         public int mFlags;
   5306 
   5307         public boolean shouldSkipIme() {
   5308             if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
   5309                 return true;
   5310             }
   5311             return mEvent instanceof MotionEvent
   5312                     && mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
   5313         }
   5314     }
   5315 
   5316     private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
   5317             InputEventReceiver receiver, int flags) {
   5318         QueuedInputEvent q = mQueuedInputEventPool;
   5319         if (q != null) {
   5320             mQueuedInputEventPoolSize -= 1;
   5321             mQueuedInputEventPool = q.mNext;
   5322             q.mNext = null;
   5323         } else {
   5324             q = new QueuedInputEvent();
   5325         }
   5326 
   5327         q.mEvent = event;
   5328         q.mReceiver = receiver;
   5329         q.mFlags = flags;
   5330         return q;
   5331     }
   5332 
   5333     private void recycleQueuedInputEvent(QueuedInputEvent q) {
   5334         q.mEvent = null;
   5335         q.mReceiver = null;
   5336 
   5337         if (mQueuedInputEventPoolSize < MAX_QUEUED_INPUT_EVENT_POOL_SIZE) {
   5338             mQueuedInputEventPoolSize += 1;
   5339             q.mNext = mQueuedInputEventPool;
   5340             mQueuedInputEventPool = q;
   5341         }
   5342     }
   5343 
   5344     void enqueueInputEvent(InputEvent event) {
   5345         enqueueInputEvent(event, null, 0, false);
   5346     }
   5347 
   5348     void enqueueInputEvent(InputEvent event,
   5349             InputEventReceiver receiver, int flags, boolean processImmediately) {
   5350         QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
   5351 
   5352         // Always enqueue the input event in order, regardless of its time stamp.
   5353         // We do this because the application or the IME may inject key events
   5354         // in response to touch events and we want to ensure that the injected keys
   5355         // are processed in the order they were received and we cannot trust that
   5356         // the time stamp of injected events are monotonic.
   5357         QueuedInputEvent last = mPendingInputEventTail;
   5358         if (last == null) {
   5359             mPendingInputEventHead = q;
   5360             mPendingInputEventTail = q;
   5361         } else {
   5362             last.mNext = q;
   5363             mPendingInputEventTail = q;
   5364         }
   5365         mPendingInputEventCount += 1;
   5366         Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
   5367                 mPendingInputEventCount);
   5368 
   5369         if (processImmediately) {
   5370             doProcessInputEvents();
   5371         } else {
   5372             scheduleProcessInputEvents();
   5373         }
   5374     }
   5375 
   5376     private void scheduleProcessInputEvents() {
   5377         if (!mProcessInputEventsScheduled) {
   5378             mProcessInputEventsScheduled = true;
   5379             Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
   5380             msg.setAsynchronous(true);
   5381             mHandler.sendMessage(msg);
   5382         }
   5383     }
   5384 
   5385     void doProcessInputEvents() {
   5386         // Deliver all pending input events in the queue.
   5387         while (mPendingInputEventHead != null) {
   5388             QueuedInputEvent q = mPendingInputEventHead;
   5389             mPendingInputEventHead = q.mNext;
   5390             if (mPendingInputEventHead == null) {
   5391                 mPendingInputEventTail = null;
   5392             }
   5393             q.mNext = null;
   5394 
   5395             mPendingInputEventCount -= 1;
   5396             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
   5397                     mPendingInputEventCount);
   5398 
   5399             deliverInputEvent(q);
   5400         }
   5401 
   5402         // We are done processing all input events that we can process right now
   5403         // so we can clear the pending flag immediately.
   5404         if (mProcessInputEventsScheduled) {
   5405             mProcessInputEventsScheduled = false;
   5406             mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
   5407         }
   5408     }
   5409 
   5410     private void deliverInputEvent(QueuedInputEvent q) {
   5411         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
   5412         try {
   5413             if (mInputEventConsistencyVerifier != null) {
   5414                 mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
   5415             }
   5416 
   5417             InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
   5418             if (stage != null) {
   5419                 stage.deliver(q);
   5420             } else {
   5421                 finishInputEvent(q);
   5422             }
   5423         } finally {
   5424             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
   5425         }
   5426     }
   5427 
   5428     private void finishInputEvent(QueuedInputEvent q) {
   5429         if (q.mReceiver != null) {
   5430             boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
   5431             q.mReceiver.finishInputEvent(q.mEvent, handled);
   5432         } else {
   5433             q.mEvent.recycleIfNeededAfterDispatch();
   5434         }
   5435 
   5436         recycleQueuedInputEvent(q);
   5437     }
   5438 
   5439     static boolean isTerminalInputEvent(InputEvent event) {
   5440         if (event instanceof KeyEvent) {
   5441             final KeyEvent keyEvent = (KeyEvent)event;
   5442             return keyEvent.getAction() == KeyEvent.ACTION_UP;
   5443         } else {
   5444             final MotionEvent motionEvent = (MotionEvent)event;
   5445             final int action = motionEvent.getAction();
   5446             return action == MotionEvent.ACTION_UP
   5447                     || action == MotionEvent.ACTION_CANCEL
   5448                     || action == MotionEvent.ACTION_HOVER_EXIT;
   5449         }
   5450     }
   5451 
   5452     void scheduleConsumeBatchedInput() {
   5453         if (!mConsumeBatchedInputScheduled) {
   5454             mConsumeBatchedInputScheduled = true;
   5455             mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
   5456                     mConsumedBatchedInputRunnable, null);
   5457         }
   5458     }
   5459 
   5460     void unscheduleConsumeBatchedInput() {
   5461         if (mConsumeBatchedInputScheduled) {
   5462             mConsumeBatchedInputScheduled = false;
   5463             mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT,
   5464                     mConsumedBatchedInputRunnable, null);
   5465         }
   5466     }
   5467 
   5468     void doConsumeBatchedInput(long frameTimeNanos) {
   5469         if (mConsumeBatchedInputScheduled) {
   5470             mConsumeBatchedInputScheduled = false;
   5471             if (mInputEventReceiver != null) {
   5472                 mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos);
   5473             }
   5474             doProcessInputEvents();
   5475         }
   5476     }
   5477 
   5478     final class TraversalRunnable implements Runnable {
   5479         @Override
   5480         public void run() {
   5481             doTraversal();
   5482         }
   5483     }
   5484     final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
   5485 
   5486     final class WindowInputEventReceiver extends InputEventReceiver {
   5487         public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
   5488             super(inputChannel, looper);
   5489         }
   5490 
   5491         @Override
   5492         public void onInputEvent(InputEvent event) {
   5493             enqueueInputEvent(event, this, 0, true);
   5494         }
   5495 
   5496         @Override
   5497         public void onBatchedInputEventPending() {
   5498             scheduleConsumeBatchedInput();
   5499         }
   5500 
   5501         @Override
   5502         public void dispose() {
   5503             unscheduleConsumeBatchedInput();
   5504             super.dispose();
   5505         }
   5506     }
   5507     WindowInputEventReceiver mInputEventReceiver;
   5508 
   5509     final class ConsumeBatchedInputRunnable implements Runnable {
   5510         @Override
   5511         public void run() {
   5512             doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
   5513         }
   5514     }
   5515     final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable =
   5516             new ConsumeBatchedInputRunnable();
   5517     boolean mConsumeBatchedInputScheduled;
   5518 
   5519     final class InvalidateOnAnimationRunnable implements Runnable {
   5520         private boolean mPosted;
   5521         private ArrayList<View> mViews = new ArrayList<View>();
   5522         private ArrayList<AttachInfo.InvalidateInfo> mViewRects =
   5523                 new ArrayList<AttachInfo.InvalidateInfo>();
   5524         private View[] mTempViews;
   5525         private AttachInfo.InvalidateInfo[] mTempViewRects;
   5526 
   5527         public void addView(View view) {
   5528             synchronized (this) {
   5529                 mViews.add(view);
   5530                 postIfNeededLocked();
   5531             }
   5532         }
   5533 
   5534         public void addViewRect(AttachInfo.InvalidateInfo info) {
   5535             synchronized (this) {
   5536                 mViewRects.add(info);
   5537                 postIfNeededLocked();
   5538             }
   5539         }
   5540 
   5541         public void removeView(View view) {
   5542             synchronized (this) {
   5543                 mViews.remove(view);
   5544 
   5545                 for (int i = mViewRects.size(); i-- > 0; ) {
   5546                     AttachInfo.InvalidateInfo info = mViewRects.get(i);
   5547                     if (info.target == view) {
   5548                         mViewRects.remove(i);
   5549                         info.recycle();
   5550                     }
   5551                 }
   5552 
   5553                 if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) {
   5554                     mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null);
   5555                     mPosted = false;
   5556                 }
   5557             }
   5558         }
   5559 
   5560         @Override
   5561         public void run() {
   5562             final int viewCount;
   5563             final int viewRectCount;
   5564             synchronized (this) {
   5565                 mPosted = false;
   5566 
   5567                 viewCount = mViews.size();
   5568                 if (viewCount != 0) {
   5569                     mTempViews = mViews.toArray(mTempViews != null
   5570                             ? mTempViews : new View[viewCount]);
   5571                     mViews.clear();
   5572                 }
   5573 
   5574                 viewRectCount = mViewRects.size();
   5575                 if (viewRectCount != 0) {
   5576                     mTempViewRects = mViewRects.toArray(mTempViewRects != null
   5577                             ? mTempViewRects : new AttachInfo.InvalidateInfo[viewRectCount]);
   5578                     mViewRects.clear();
   5579                 }
   5580             }
   5581 
   5582             for (int i = 0; i < viewCount; i++) {
   5583                 mTempViews[i].invalidate();
   5584                 mTempViews[i] = null;
   5585             }
   5586 
   5587             for (int i = 0; i < viewRectCount; i++) {
   5588                 final View.AttachInfo.InvalidateInfo info = mTempViewRects[i];
   5589                 info.target.invalidate(info.left, info.top, info.right, info.bottom);
   5590                 info.recycle();
   5591             }
   5592         }
   5593 
   5594         private void postIfNeededLocked() {
   5595             if (!mPosted) {
   5596                 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
   5597                 mPosted = true;
   5598             }
   5599         }
   5600     }
   5601     final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable =
   5602             new InvalidateOnAnimationRunnable();
   5603 
   5604     public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
   5605         Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
   5606         mHandler.sendMessageDelayed(msg, delayMilliseconds);
   5607     }
   5608 
   5609     public void dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info,
   5610             long delayMilliseconds) {
   5611         final Message msg = mHandler.obtainMessage(MSG_INVALIDATE_RECT, info);
   5612         mHandler.sendMessageDelayed(msg, delayMilliseconds);
   5613     }
   5614 
   5615     public void dispatchInvalidateOnAnimation(View view) {
   5616         mInvalidateOnAnimationRunnable.addView(view);
   5617     }
   5618 
   5619     public void dispatchInvalidateRectOnAnimation(AttachInfo.InvalidateInfo info) {
   5620         mInvalidateOnAnimationRunnable.addViewRect(info);
   5621     }
   5622 
   5623     public void enqueueDisplayList(DisplayList displayList) {
   5624         mDisplayLists.add(displayList);
   5625     }
   5626 
   5627     public void cancelInvalidate(View view) {
   5628         mHandler.removeMessages(MSG_INVALIDATE, view);
   5629         // fixme: might leak the AttachInfo.InvalidateInfo objects instead of returning
   5630         // them to the pool
   5631         mHandler.removeMessages(MSG_INVALIDATE_RECT, view);
   5632         mInvalidateOnAnimationRunnable.removeView(view);
   5633     }
   5634 
   5635     public void dispatchKey(KeyEvent event) {
   5636         Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY, event);
   5637         msg.setAsynchronous(true);
   5638         mHandler.sendMessage(msg);
   5639     }
   5640 
   5641     public void dispatchKeyFromIme(KeyEvent event) {
   5642         Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event);
   5643         msg.setAsynchronous(true);
   5644         mHandler.sendMessage(msg);
   5645     }
   5646 
   5647     public void dispatchUnhandledKey(KeyEvent event) {
   5648         if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
   5649             final KeyCharacterMap kcm = event.getKeyCharacterMap();
   5650             final int keyCode = event.getKeyCode();
   5651             final int metaState = event.getMetaState();
   5652 
   5653             // Check for fallback actions specified by the key character map.
   5654             KeyCharacterMap.FallbackAction fallbackAction =
   5655                     kcm.getFallbackAction(keyCode, metaState);
   5656             if (fallbackAction != null) {
   5657                 final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
   5658                 KeyEvent fallbackEvent = KeyEvent.obtain(
   5659                         event.getDownTime(), event.getEventTime(),
   5660                         event.getAction(), fallbackAction.keyCode,
   5661                         event.getRepeatCount(), fallbackAction.metaState,
   5662                         event.getDeviceId(), event.getScanCode(),
   5663                         flags, event.getSource(), null);
   5664                 fallbackAction.recycle();
   5665 
   5666                 dispatchKey(fallbackEvent);
   5667             }
   5668         }
   5669     }
   5670 
   5671     public void dispatchAppVisibility(boolean visible) {
   5672         Message msg = mHandler.obtainMessage(MSG_DISPATCH_APP_VISIBILITY);
   5673         msg.arg1 = visible ? 1 : 0;
   5674         mHandler.sendMessage(msg);
   5675     }
   5676 
   5677     public void dispatchScreenStateChange(boolean on) {
   5678         Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATE);
   5679         msg.arg1 = on ? 1 : 0;
   5680         mHandler.sendMessage(msg);
   5681     }
   5682 
   5683     public void dispatchGetNewSurface() {
   5684         Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE);
   5685         mHandler.sendMessage(msg);
   5686     }
   5687 
   5688     public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
   5689         Message msg = Message.obtain();
   5690         msg.what = MSG_WINDOW_FOCUS_CHANGED;
   5691         msg.arg1 = hasFocus ? 1 : 0;
   5692         msg.arg2 = inTouchMode ? 1 : 0;
   5693         mHandler.sendMessage(msg);
   5694     }
   5695 
   5696     public void dispatchCloseSystemDialogs(String reason) {
   5697         Message msg = Message.obtain();
   5698         msg.what = MSG_CLOSE_SYSTEM_DIALOGS;
   5699         msg.obj = reason;
   5700         mHandler.sendMessage(msg);
   5701     }
   5702 
   5703     public void dispatchDragEvent(DragEvent event) {
   5704         final int what;
   5705         if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
   5706             what = MSG_DISPATCH_DRAG_LOCATION_EVENT;
   5707             mHandler.removeMessages(what);
   5708         } else {
   5709             what = MSG_DISPATCH_DRAG_EVENT;
   5710         }
   5711         Message msg = mHandler.obtainMessage(what, event);
   5712         mHandler.sendMessage(msg);
   5713     }
   5714 
   5715     public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
   5716             int localValue, int localChanges) {
   5717         SystemUiVisibilityInfo args = new SystemUiVisibilityInfo();
   5718         args.seq = seq;
   5719         args.globalVisibility = globalVisibility;
   5720         args.localValue = localValue;
   5721         args.localChanges = localChanges;
   5722         mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
   5723     }
   5724 
   5725     public void dispatchDoneAnimating() {
   5726         mHandler.sendEmptyMessage(MSG_DISPATCH_DONE_ANIMATING);
   5727     }
   5728 
   5729     public void dispatchCheckFocus() {
   5730         if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
   5731             // This will result in a call to checkFocus() below.
   5732             mHandler.sendEmptyMessage(MSG_CHECK_FOCUS);
   5733         }
   5734     }
   5735 
   5736     /**
   5737      * Post a callback to send a
   5738      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
   5739      * This event is send at most once every
   5740      * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
   5741      */
   5742     private void postSendWindowContentChangedCallback(View source) {
   5743         if (mSendWindowContentChangedAccessibilityEvent == null) {
   5744             mSendWindowContentChangedAccessibilityEvent =
   5745                 new SendWindowContentChangedAccessibilityEvent();
   5746         }
   5747         View oldSource = mSendWindowContentChangedAccessibilityEvent.mSource;
   5748         if (oldSource == null) {
   5749             mSendWindowContentChangedAccessibilityEvent.mSource = source;
   5750             mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
   5751                     ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
   5752         } else {
   5753             mSendWindowContentChangedAccessibilityEvent.mSource =
   5754                     getCommonPredecessor(oldSource, source);
   5755         }
   5756     }
   5757 
   5758     /**
   5759      * Remove a posted callback to send a
   5760      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
   5761      */
   5762     private void removeSendWindowContentChangedCallback() {
   5763         if (mSendWindowContentChangedAccessibilityEvent != null) {
   5764             mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
   5765         }
   5766     }
   5767 
   5768     public boolean showContextMenuForChild(View originalView) {
   5769         return false;
   5770     }
   5771 
   5772     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
   5773         return null;
   5774     }
   5775 
   5776     public void createContextMenu(ContextMenu menu) {
   5777     }
   5778 
   5779     public void childDrawableStateChanged(View child) {
   5780     }
   5781 
   5782     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
   5783         if (mView == null) {
   5784             return false;
   5785         }
   5786         // Intercept accessibility focus events fired by virtual nodes to keep
   5787         // track of accessibility focus position in such nodes.
   5788         final int eventType = event.getEventType();
   5789         switch (eventType) {
   5790             case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
   5791                 final long sourceNodeId = event.getSourceNodeId();
   5792                 final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
   5793                         sourceNodeId);
   5794                 View source = mView.findViewByAccessibilityId(accessibilityViewId);
   5795                 if (source != null) {
   5796                     AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
   5797                     if (provider != null) {
   5798                         AccessibilityNodeInfo node = provider.createAccessibilityNodeInfo(
   5799                                 AccessibilityNodeInfo.getVirtualDescendantId(sourceNodeId));
   5800                         setAccessibilityFocus(source, node);
   5801                     }
   5802                 }
   5803             } break;
   5804             case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
   5805                 final long sourceNodeId = event.getSourceNodeId();
   5806                 final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
   5807                         sourceNodeId);
   5808                 View source = mView.findViewByAccessibilityId(accessibilityViewId);
   5809                 if (source != null) {
   5810                     AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
   5811                     if (provider != null) {
   5812                         setAccessibilityFocus(null, null);
   5813                     }
   5814                 }
   5815             } break;
   5816         }
   5817         mAccessibilityManager.sendAccessibilityEvent(event);
   5818         return true;
   5819     }
   5820 
   5821     @Override
   5822     public void childAccessibilityStateChanged(View child) {
   5823         postSendWindowContentChangedCallback(child);
   5824     }
   5825 
   5826     @Override
   5827     public boolean canResolveLayoutDirection() {
   5828         return true;
   5829     }
   5830 
   5831     @Override
   5832     public boolean isLayoutDirectionResolved() {
   5833         return true;
   5834     }
   5835 
   5836     @Override
   5837     public int getLayoutDirection() {
   5838         return View.LAYOUT_DIRECTION_RESOLVED_DEFAULT;
   5839     }
   5840 
   5841     @Override
   5842     public boolean canResolveTextDirection() {
   5843         return true;
   5844     }
   5845 
   5846     @Override
   5847     public boolean isTextDirectionResolved() {
   5848         return true;
   5849     }
   5850 
   5851     @Override
   5852     public int getTextDirection() {
   5853         return View.TEXT_DIRECTION_RESOLVED_DEFAULT;
   5854     }
   5855 
   5856     @Override
   5857     public boolean canResolveTextAlignment() {
   5858         return true;
   5859     }
   5860 
   5861     @Override
   5862     public boolean isTextAlignmentResolved() {
   5863         return true;
   5864     }
   5865 
   5866     @Override
   5867     public int getTextAlignment() {
   5868         return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT;
   5869     }
   5870 
   5871     private View getCommonPredecessor(View first, View second) {
   5872         if (mAttachInfo != null) {
   5873             if (mTempHashSet == null) {
   5874                 mTempHashSet = new HashSet<View>();
   5875             }
   5876             HashSet<View> seen = mTempHashSet;
   5877             seen.clear();
   5878             View firstCurrent = first;
   5879             while (firstCurrent != null) {
   5880                 seen.add(firstCurrent);
   5881                 ViewParent firstCurrentParent = firstCurrent.mParent;
   5882                 if (firstCurrentParent instanceof View) {
   5883                     firstCurrent = (View) firstCurrentParent;
   5884                 } else {
   5885                     firstCurrent = null;
   5886                 }
   5887             }
   5888             View secondCurrent = second;
   5889             while (secondCurrent != null) {
   5890                 if (seen.contains(secondCurrent)) {
   5891                     seen.clear();
   5892                     return secondCurrent;
   5893                 }
   5894                 ViewParent secondCurrentParent = secondCurrent.mParent;
   5895                 if (secondCurrentParent instanceof View) {
   5896                     secondCurrent = (View) secondCurrentParent;
   5897                 } else {
   5898                     secondCurrent = null;
   5899                 }
   5900             }
   5901             seen.clear();
   5902         }
   5903         return null;
   5904     }
   5905 
   5906     void checkThread() {
   5907         if (mThread != Thread.currentThread()) {
   5908             throw new CalledFromWrongThreadException(
   5909                     "Only the original thread that created a view hierarchy can touch its views.");
   5910         }
   5911     }
   5912 
   5913     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   5914         // ViewAncestor never intercepts touch event, so this can be a no-op
   5915     }
   5916 
   5917     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
   5918         final boolean scrolled = scrollToRectOrFocus(rectangle, immediate);
   5919         if (rectangle != null) {
   5920             mTempRect.set(rectangle);
   5921             mTempRect.offset(0, -mCurScrollY);
   5922             mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
   5923             try {
   5924                 mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect, immediate);
   5925             } catch (RemoteException re) {
   5926                 /* ignore */
   5927             }
   5928         }
   5929         return scrolled;
   5930     }
   5931 
   5932     public void childHasTransientStateChanged(View child, boolean hasTransientState) {
   5933         // Do nothing.
   5934     }
   5935 
   5936     class TakenSurfaceHolder extends BaseSurfaceHolder {
   5937         @Override
   5938         public boolean onAllowLockCanvas() {
   5939             return mDrawingAllowed;
   5940         }
   5941 
   5942         @Override
   5943         public void onRelayoutContainer() {
   5944             // Not currently interesting -- from changing between fixed and layout size.
   5945         }
   5946 
   5947         public void setFormat(int format) {
   5948             ((RootViewSurfaceTaker)mView).setSurfaceFormat(format);
   5949         }
   5950 
   5951         public void setType(int type) {
   5952             ((RootViewSurfaceTaker)mView).setSurfaceType(type);
   5953         }
   5954 
   5955         @Override
   5956         public void onUpdateSurface() {
   5957             // We take care of format and type changes on our own.
   5958             throw new IllegalStateException("Shouldn't be here");
   5959         }
   5960 
   5961         public boolean isCreating() {
   5962             return mIsCreating;
   5963         }
   5964 
   5965         @Override
   5966         public void setFixedSize(int width, int height) {
   5967             throw new UnsupportedOperationException(
   5968                     "Currently only support sizing from layout");
   5969         }
   5970 
   5971         public void setKeepScreenOn(boolean screenOn) {
   5972             ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
   5973         }
   5974     }
   5975 
   5976     static class W extends IWindow.Stub {
   5977         private final WeakReference<ViewRootImpl> mViewAncestor;
   5978         private final IWindowSession mWindowSession;
   5979 
   5980         W(ViewRootImpl viewAncestor) {
   5981             mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
   5982             mWindowSession = viewAncestor.mWindowSession;
   5983         }
   5984 
   5985         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
   5986                 Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
   5987             final ViewRootImpl viewAncestor = mViewAncestor.get();
   5988             if (viewAncestor != null) {
   5989                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
   5990                         visibleInsets, reportDraw, newConfig);
   5991             }
   5992         }
   5993 
   5994         @Override
   5995         public void moved(int newX, int newY) {
   5996             final ViewRootImpl viewAncestor = mViewAncestor.get();
   5997             if (viewAncestor != null) {
   5998                 viewAncestor.dispatchMoved(newX, newY);
   5999             }
   6000         }
   6001 
   6002         public void dispatchAppVisibility(boolean visible) {
   6003             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6004             if (viewAncestor != null) {
   6005                 viewAncestor.dispatchAppVisibility(visible);
   6006             }
   6007         }
   6008 
   6009         public void dispatchScreenState(boolean on) {
   6010             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6011             if (viewAncestor != null) {
   6012                 viewAncestor.dispatchScreenStateChange(on);
   6013             }
   6014         }
   6015 
   6016         public void dispatchGetNewSurface() {
   6017             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6018             if (viewAncestor != null) {
   6019                 viewAncestor.dispatchGetNewSurface();
   6020             }
   6021         }
   6022 
   6023         public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
   6024             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6025             if (viewAncestor != null) {
   6026                 viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
   6027             }
   6028         }
   6029 
   6030         private static int checkCallingPermission(String permission) {
   6031             try {
   6032                 return ActivityManagerNative.getDefault().checkPermission(
   6033                         permission, Binder.getCallingPid(), Binder.getCallingUid());
   6034             } catch (RemoteException e) {
   6035                 return PackageManager.PERMISSION_DENIED;
   6036             }
   6037         }
   6038 
   6039         public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
   6040             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6041             if (viewAncestor != null) {
   6042                 final View view = viewAncestor.mView;
   6043                 if (view != null) {
   6044                     if (checkCallingPermission(Manifest.permission.DUMP) !=
   6045                             PackageManager.PERMISSION_GRANTED) {
   6046                         throw new SecurityException("Insufficient permissions to invoke"
   6047                                 + " executeCommand() from pid=" + Binder.getCallingPid()
   6048                                 + ", uid=" + Binder.getCallingUid());
   6049                     }
   6050 
   6051                     OutputStream clientStream = null;
   6052                     try {
   6053                         clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out);
   6054                         ViewDebug.dispatchCommand(view, command, parameters, clientStream);
   6055                     } catch (IOException e) {
   6056                         e.printStackTrace();
   6057                     } finally {
   6058                         if (clientStream != null) {
   6059                             try {
   6060                                 clientStream.close();
   6061                             } catch (IOException e) {
   6062                                 e.printStackTrace();
   6063                             }
   6064                         }
   6065                     }
   6066                 }
   6067             }
   6068         }
   6069 
   6070         public void closeSystemDialogs(String reason) {
   6071             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6072             if (viewAncestor != null) {
   6073                 viewAncestor.dispatchCloseSystemDialogs(reason);
   6074             }
   6075         }
   6076 
   6077         public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
   6078                 boolean sync) {
   6079             if (sync) {
   6080                 try {
   6081                     mWindowSession.wallpaperOffsetsComplete(asBinder());
   6082                 } catch (RemoteException e) {
   6083                 }
   6084             }
   6085         }
   6086 
   6087         public void dispatchWallpaperCommand(String action, int x, int y,
   6088                 int z, Bundle extras, boolean sync) {
   6089             if (sync) {
   6090                 try {
   6091                     mWindowSession.wallpaperCommandComplete(asBinder(), null);
   6092                 } catch (RemoteException e) {
   6093                 }
   6094             }
   6095         }
   6096 
   6097         /* Drag/drop */
   6098         public void dispatchDragEvent(DragEvent event) {
   6099             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6100             if (viewAncestor != null) {
   6101                 viewAncestor.dispatchDragEvent(event);
   6102             }
   6103         }
   6104 
   6105         public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
   6106                 int localValue, int localChanges) {
   6107             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6108             if (viewAncestor != null) {
   6109                 viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility,
   6110                         localValue, localChanges);
   6111             }
   6112         }
   6113 
   6114         public void doneAnimating() {
   6115             final ViewRootImpl viewAncestor = mViewAncestor.get();
   6116             if (viewAncestor != null) {
   6117                 viewAncestor.dispatchDoneAnimating();
   6118             }
   6119         }
   6120     }
   6121 
   6122     public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
   6123         public CalledFromWrongThreadException(String msg) {
   6124             super(msg);
   6125         }
   6126     }
   6127 
   6128     private SurfaceHolder mHolder = new SurfaceHolder() {
   6129         // we only need a SurfaceHolder for opengl. it would be nice
   6130         // to implement everything else though, especially the callback
   6131         // support (opengl doesn't make use of it right now, but eventually
   6132         // will).
   6133         public Surface getSurface() {
   6134             return mSurface;
   6135         }
   6136 
   6137         public boolean isCreating() {
   6138             return false;
   6139         }
   6140 
   6141         public void addCallback(Callback callback) {
   6142         }
   6143 
   6144         public void removeCallback(Callback callback) {
   6145         }
   6146 
   6147         public void setFixedSize(int width, int height) {
   6148         }
   6149 
   6150         public void setSizeFromLayout() {
   6151         }
   6152 
   6153         public void setFormat(int format) {
   6154         }
   6155 
   6156         public void setType(int type) {
   6157         }
   6158 
   6159         public void setKeepScreenOn(boolean screenOn) {
   6160         }
   6161 
   6162         public Canvas lockCanvas() {
   6163             return null;
   6164         }
   6165 
   6166         public Canvas lockCanvas(Rect dirty) {
   6167             return null;
   6168         }
   6169 
   6170         public void unlockCanvasAndPost(Canvas canvas) {
   6171         }
   6172         public Rect getSurfaceFrame() {
   6173             return null;
   6174         }
   6175     };
   6176 
   6177     static RunQueue getRunQueue() {
   6178         RunQueue rq = sRunQueues.get();
   6179         if (rq != null) {
   6180             return rq;
   6181         }
   6182         rq = new RunQueue();
   6183         sRunQueues.set(rq);
   6184         return rq;
   6185     }
   6186 
   6187     /**
   6188      * The run queue is used to enqueue pending work from Views when no Handler is
   6189      * attached.  The work is executed during the next call to performTraversals on
   6190      * the thread.
   6191      * @hide
   6192      */
   6193     static final class RunQueue {
   6194         private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
   6195 
   6196         void post(Runnable action) {
   6197             postDelayed(action, 0);
   6198         }
   6199 
   6200         void postDelayed(Runnable action, long delayMillis) {
   6201             HandlerAction handlerAction = new HandlerAction();
   6202             handlerAction.action = action;
   6203             handlerAction.delay = delayMillis;
   6204 
   6205             synchronized (mActions) {
   6206                 mActions.add(handlerAction);
   6207             }
   6208         }
   6209 
   6210         void removeCallbacks(Runnable action) {
   6211             final HandlerAction handlerAction = new HandlerAction();
   6212             handlerAction.action = action;
   6213 
   6214             synchronized (mActions) {
   6215                 final ArrayList<HandlerAction> actions = mActions;
   6216 
   6217                 while (actions.remove(handlerAction)) {
   6218                     // Keep going
   6219                 }
   6220             }
   6221         }
   6222 
   6223         void executeActions(Handler handler) {
   6224             synchronized (mActions) {
   6225                 final ArrayList<HandlerAction> actions = mActions;
   6226                 final int count = actions.size();
   6227 
   6228                 for (int i = 0; i < count; i++) {
   6229                     final HandlerAction handlerAction = actions.get(i);
   6230                     handler.postDelayed(handlerAction.action, handlerAction.delay);
   6231                 }
   6232 
   6233                 actions.clear();
   6234             }
   6235         }
   6236 
   6237         private static class HandlerAction {
   6238             Runnable action;
   6239             long delay;
   6240 
   6241             @Override
   6242             public boolean equals(Object o) {
   6243                 if (this == o) return true;
   6244                 if (o == null || getClass() != o.getClass()) return false;
   6245 
   6246                 HandlerAction that = (HandlerAction) o;
   6247                 return !(action != null ? !action.equals(that.action) : that.action != null);
   6248 
   6249             }
   6250 
   6251             @Override
   6252             public int hashCode() {
   6253                 int result = action != null ? action.hashCode() : 0;
   6254                 result = 31 * result + (int) (delay ^ (delay >>> 32));
   6255                 return result;
   6256             }
   6257         }
   6258     }
   6259 
   6260     /**
   6261      * Class for managing the accessibility interaction connection
   6262      * based on the global accessibility state.
   6263      */
   6264     final class AccessibilityInteractionConnectionManager
   6265             implements AccessibilityStateChangeListener {
   6266         public void onAccessibilityStateChanged(boolean enabled) {
   6267             if (enabled) {
   6268                 ensureConnection();
   6269                 if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
   6270                     mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   6271                     View focusedView = mView.findFocus();
   6272                     if (focusedView != null && focusedView != mView) {
   6273                         focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
   6274                     }
   6275                 }
   6276             } else {
   6277                 ensureNoConnection();
   6278                 mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget();
   6279             }
   6280         }
   6281 
   6282         public void ensureConnection() {
   6283             if (mAttachInfo != null) {
   6284                 final boolean registered =
   6285                     mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
   6286                 if (!registered) {
   6287                     mAttachInfo.mAccessibilityWindowId =
   6288                         mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
   6289                                 new AccessibilityInteractionConnection(ViewRootImpl.this));
   6290                 }
   6291             }
   6292         }
   6293 
   6294         public void ensureNoConnection() {
   6295             final boolean registered =
   6296                 mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
   6297             if (registered) {
   6298                 mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED;
   6299                 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
   6300             }
   6301         }
   6302     }
   6303 
   6304     /**
   6305      * This class is an interface this ViewAncestor provides to the
   6306      * AccessibilityManagerService to the latter can interact with
   6307      * the view hierarchy in this ViewAncestor.
   6308      */
   6309     static final class AccessibilityInteractionConnection
   6310             extends IAccessibilityInteractionConnection.Stub {
   6311         private final WeakReference<ViewRootImpl> mViewRootImpl;
   6312 
   6313         AccessibilityInteractionConnection(ViewRootImpl viewRootImpl) {
   6314             mViewRootImpl = new WeakReference<ViewRootImpl>(viewRootImpl);
   6315         }
   6316 
   6317         @Override
   6318         public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
   6319                 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
   6320                 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
   6321             ViewRootImpl viewRootImpl = mViewRootImpl.get();
   6322             if (viewRootImpl != null && viewRootImpl.mView != null) {
   6323                 viewRootImpl.getAccessibilityInteractionController()
   6324                     .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
   6325                             interactionId, callback, flags, interrogatingPid, interrogatingTid,
   6326                             spec);
   6327             } else {
   6328                 // We cannot make the call and notify the caller so it does not wait.
   6329                 try {
   6330                     callback.setFindAccessibilityNodeInfosResult(null, interactionId);
   6331                 } catch (RemoteException re) {
   6332                     /* best effort - ignore */
   6333                 }
   6334             }
   6335         }
   6336 
   6337         @Override
   6338         public void performAccessibilityAction(long accessibilityNodeId, int action,
   6339                 Bundle arguments, int interactionId,
   6340                 IAccessibilityInteractionConnectionCallback callback, int flags,
   6341                 int interogatingPid, long interrogatingTid) {
   6342             ViewRootImpl viewRootImpl = mViewRootImpl.get();
   6343             if (viewRootImpl != null && viewRootImpl.mView != null) {
   6344                 viewRootImpl.getAccessibilityInteractionController()
   6345                     .performAccessibilityActionClientThread(accessibilityNodeId, action, arguments,
   6346                             interactionId, callback, flags, interogatingPid, interrogatingTid);
   6347             } else {
   6348                 // We cannot make the call and notify the caller so it does not wait.
   6349                 try {
   6350                     callback.setPerformAccessibilityActionResult(false, interactionId);
   6351                 } catch (RemoteException re) {
   6352                     /* best effort - ignore */
   6353                 }
   6354             }
   6355         }
   6356 
   6357         @Override
   6358         public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
   6359                 String viewId, int interactionId,
   6360                 IAccessibilityInteractionConnectionCallback callback, int flags,
   6361                 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
   6362             ViewRootImpl viewRootImpl = mViewRootImpl.get();
   6363             if (viewRootImpl != null && viewRootImpl.mView != null) {
   6364                 viewRootImpl.getAccessibilityInteractionController()
   6365                     .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId,
   6366                             viewId, interactionId, callback, flags, interrogatingPid,
   6367                             interrogatingTid, spec);
   6368             } else {
   6369                 // We cannot make the call and notify the caller so it does not wait.
   6370                 try {
   6371                     callback.setFindAccessibilityNodeInfoResult(null, interactionId);
   6372                 } catch (RemoteException re) {
   6373                     /* best effort - ignore */
   6374                 }
   6375             }
   6376         }
   6377 
   6378         @Override
   6379         public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
   6380                 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
   6381                 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
   6382             ViewRootImpl viewRootImpl = mViewRootImpl.get();
   6383             if (viewRootImpl != null && viewRootImpl.mView != null) {
   6384                 viewRootImpl.getAccessibilityInteractionController()
   6385                     .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
   6386                             interactionId, callback, flags, interrogatingPid, interrogatingTid,
   6387                             spec);
   6388             } else {
   6389                 // We cannot make the call and notify the caller so it does not wait.
   6390                 try {
   6391                     callback.setFindAccessibilityNodeInfosResult(null, interactionId);
   6392                 } catch (RemoteException re) {
   6393                     /* best effort - ignore */
   6394                 }
   6395             }
   6396         }
   6397 
   6398         @Override
   6399         public void findFocus(long accessibilityNodeId, int focusType, int interactionId,
   6400                 IAccessibilityInteractionConnectionCallback callback, int flags,
   6401                 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
   6402             ViewRootImpl viewRootImpl = mViewRootImpl.get();
   6403             if (viewRootImpl != null && viewRootImpl.mView != null) {
   6404                 viewRootImpl.getAccessibilityInteractionController()
   6405                     .findFocusClientThread(accessibilityNodeId, focusType, interactionId, callback,
   6406                             flags, interrogatingPid, interrogatingTid, spec);
   6407             } else {
   6408                 // We cannot make the call and notify the caller so it does not wait.
   6409                 try {
   6410                     callback.setFindAccessibilityNodeInfoResult(null, interactionId);
   6411                 } catch (RemoteException re) {
   6412                     /* best effort - ignore */
   6413                 }
   6414             }
   6415         }
   6416 
   6417         @Override
   6418         public void focusSearch(long accessibilityNodeId, int direction, int interactionId,
   6419                 IAccessibilityInteractionConnectionCallback callback, int flags,
   6420                 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
   6421             ViewRootImpl viewRootImpl = mViewRootImpl.get();
   6422             if (viewRootImpl != null && viewRootImpl.mView != null) {
   6423                 viewRootImpl.getAccessibilityInteractionController()
   6424                     .focusSearchClientThread(accessibilityNodeId, direction, interactionId,
   6425                             callback, flags, interrogatingPid, interrogatingTid, spec);
   6426             } else {
   6427                 // We cannot make the call and notify the caller so it does not wait.
   6428                 try {
   6429                     callback.setFindAccessibilityNodeInfoResult(null, interactionId);
   6430                 } catch (RemoteException re) {
   6431                     /* best effort - ignore */
   6432                 }
   6433             }
   6434         }
   6435     }
   6436 
   6437     private class SendWindowContentChangedAccessibilityEvent implements Runnable {
   6438         public View mSource;
   6439 
   6440         public void run() {
   6441             if (mSource != null) {
   6442                 mSource.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
   6443                 mSource.resetAccessibilityStateChanged();
   6444                 mSource = null;
   6445             }
   6446         }
   6447     }
   6448 }
   6449