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