Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view;
     18 
     19 import android.Manifest;
     20 import android.animation.LayoutTransition;
     21 import android.app.ActivityManagerNative;
     22 import android.content.ClipDescription;
     23 import android.content.ComponentCallbacks;
     24 import android.content.ComponentCallbacks2;
     25 import android.content.Context;
     26 import android.content.pm.PackageManager;
     27 import android.content.res.CompatibilityInfo;
     28 import android.content.res.Configuration;
     29 import android.content.res.Resources;
     30 import android.graphics.Canvas;
     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.media.AudioManager;
     39 import android.os.Binder;
     40 import android.os.Bundle;
     41 import android.os.Debug;
     42 import android.os.Handler;
     43 import android.os.LatencyTimer;
     44 import android.os.Looper;
     45 import android.os.Message;
     46 import android.os.ParcelFileDescriptor;
     47 import android.os.Process;
     48 import android.os.RemoteException;
     49 import android.os.SystemClock;
     50 import android.os.SystemProperties;
     51 import android.util.AndroidRuntimeException;
     52 import android.util.DisplayMetrics;
     53 import android.util.EventLog;
     54 import android.util.Log;
     55 import android.util.Pool;
     56 import android.util.Poolable;
     57 import android.util.PoolableManager;
     58 import android.util.Pools;
     59 import android.util.Slog;
     60 import android.util.SparseArray;
     61 import android.util.TypedValue;
     62 import android.view.View.MeasureSpec;
     63 import android.view.accessibility.AccessibilityEvent;
     64 import android.view.accessibility.AccessibilityInteractionClient;
     65 import android.view.accessibility.AccessibilityManager;
     66 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
     67 import android.view.accessibility.AccessibilityNodeInfo;
     68 import android.view.accessibility.IAccessibilityInteractionConnection;
     69 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
     70 import android.view.animation.AccelerateDecelerateInterpolator;
     71 import android.view.animation.Interpolator;
     72 import android.view.inputmethod.InputConnection;
     73 import android.view.inputmethod.InputMethodManager;
     74 import android.widget.Scroller;
     75 
     76 import com.android.internal.policy.PolicyManager;
     77 import com.android.internal.view.BaseSurfaceHolder;
     78 import com.android.internal.view.IInputMethodCallback;
     79 import com.android.internal.view.IInputMethodSession;
     80 import com.android.internal.view.RootViewSurfaceTaker;
     81 
     82 import java.io.IOException;
     83 import java.io.OutputStream;
     84 import java.io.PrintWriter;
     85 import java.lang.ref.WeakReference;
     86 import java.util.ArrayList;
     87 import java.util.List;
     88 
     89 /**
     90  * The top of a view hierarchy, implementing the needed protocol between View
     91  * and the WindowManager.  This is for the most part an internal implementation
     92  * detail of {@link WindowManagerImpl}.
     93  *
     94  * {@hide}
     95  */
     96 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
     97 public final class ViewRootImpl extends Handler implements ViewParent,
     98         View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
     99     private static final String TAG = "ViewRootImpl";
    100     private static final boolean DBG = false;
    101     private static final boolean LOCAL_LOGV = false;
    102     /** @noinspection PointlessBooleanExpression*/
    103     private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
    104     private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
    105     private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV;
    106     private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;
    107     private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
    108     private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
    109     private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
    110     private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
    111     private static final boolean DEBUG_FPS = false;
    112     private static final boolean WATCH_POINTER = false;
    113 
    114     /**
    115      * Set this system property to true to force the view hierarchy to render
    116      * at 60 Hz. This can be used to measure the potential framerate.
    117      */
    118     private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
    119 
    120     private static final boolean MEASURE_LATENCY = false;
    121     private static LatencyTimer lt;
    122 
    123     /**
    124      * Maximum time we allow the user to roll the trackball enough to generate
    125      * a key event, before resetting the counters.
    126      */
    127     static final int MAX_TRACKBALL_DELAY = 250;
    128 
    129     static IWindowSession sWindowSession;
    130 
    131     static final Object mStaticInit = new Object();
    132     static boolean mInitialized = false;
    133 
    134     static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
    135 
    136     static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
    137     static boolean sFirstDrawComplete = false;
    138 
    139     static final ArrayList<ComponentCallbacks> sConfigCallbacks
    140             = new ArrayList<ComponentCallbacks>();
    141 
    142     long mLastTrackballTime = 0;
    143     final TrackballAxis mTrackballAxisX = new TrackballAxis();
    144     final TrackballAxis mTrackballAxisY = new TrackballAxis();
    145 
    146     int mLastJoystickXDirection;
    147     int mLastJoystickYDirection;
    148     int mLastJoystickXKeyCode;
    149     int mLastJoystickYKeyCode;
    150 
    151     final int[] mTmpLocation = new int[2];
    152 
    153     final TypedValue mTmpValue = new TypedValue();
    154 
    155     final InputMethodCallback mInputMethodCallback;
    156     final SparseArray<Object> mPendingEvents = new SparseArray<Object>();
    157     int mPendingEventSeq = 0;
    158 
    159     final Thread mThread;
    160 
    161     final WindowLeaked mLocation;
    162 
    163     final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
    164 
    165     final W mWindow;
    166 
    167     final int mTargetSdkVersion;
    168 
    169     int mSeq;
    170 
    171     View mView;
    172     View mFocusedView;
    173     View mRealFocusedView;  // this is not set to null in touch mode
    174     int mViewVisibility;
    175     boolean mAppVisible = true;
    176     int mOrigWindowType = -1;
    177 
    178     // Set to true if the owner of this window is in the stopped state,
    179     // so the window should no longer be active.
    180     boolean mStopped = false;
    181 
    182     boolean mLastInCompatMode = false;
    183 
    184     SurfaceHolder.Callback2 mSurfaceHolderCallback;
    185     BaseSurfaceHolder mSurfaceHolder;
    186     boolean mIsCreating;
    187     boolean mDrawingAllowed;
    188 
    189     final Region mTransparentRegion;
    190     final Region mPreviousTransparentRegion;
    191 
    192     int mWidth;
    193     int mHeight;
    194     Rect mDirty;
    195     final Rect mCurrentDirty = new Rect();
    196     final Rect mPreviousDirty = new Rect();
    197     boolean mIsAnimating;
    198 
    199     CompatibilityInfo.Translator mTranslator;
    200 
    201     final View.AttachInfo mAttachInfo;
    202     InputChannel mInputChannel;
    203     InputQueue.Callback mInputQueueCallback;
    204     InputQueue mInputQueue;
    205     FallbackEventHandler mFallbackEventHandler;
    206 
    207     final Rect mTempRect; // used in the transaction to not thrash the heap.
    208     final Rect mVisRect; // used to retrieve visible rect of focused view.
    209 
    210     boolean mTraversalScheduled;
    211     long mLastTraversalFinishedTimeNanos;
    212     long mLastDrawDurationNanos;
    213     boolean mWillDrawSoon;
    214     boolean mLayoutRequested;
    215     boolean mFirst;
    216     boolean mReportNextDraw;
    217     boolean mFullRedrawNeeded;
    218     boolean mNewSurfaceNeeded;
    219     boolean mHasHadWindowFocus;
    220     boolean mLastWasImTarget;
    221     InputEventMessage mPendingInputEvents = null;
    222 
    223     boolean mWindowAttributesChanged = false;
    224     int mWindowAttributesChangesFlag = 0;
    225 
    226     // These can be accessed by any thread, must be protected with a lock.
    227     // Surface can never be reassigned or cleared (use Surface.clear()).
    228     private final Surface mSurface = new Surface();
    229 
    230     boolean mAdded;
    231     boolean mAddedTouchMode;
    232 
    233     CompatibilityInfoHolder mCompatibilityInfo;
    234 
    235     /*package*/ int mAddNesting;
    236 
    237     // These are accessed by multiple threads.
    238     final Rect mWinFrame; // frame given by window manager.
    239 
    240     final Rect mPendingVisibleInsets = new Rect();
    241     final Rect mPendingContentInsets = new Rect();
    242     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
    243             = new ViewTreeObserver.InternalInsetsInfo();
    244 
    245     final Configuration mLastConfiguration = new Configuration();
    246     final Configuration mPendingConfiguration = new Configuration();
    247 
    248     class ResizedInfo {
    249         Rect coveredInsets;
    250         Rect visibleInsets;
    251         Configuration newConfig;
    252     }
    253 
    254     boolean mScrollMayChange;
    255     int mSoftInputMode;
    256     View mLastScrolledFocus;
    257     int mScrollY;
    258     int mCurScrollY;
    259     Scroller mScroller;
    260     HardwareLayer mResizeBuffer;
    261     long mResizeBufferStartTime;
    262     int mResizeBufferDuration;
    263     static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
    264     private ArrayList<LayoutTransition> mPendingTransitions;
    265 
    266     final ViewConfiguration mViewConfiguration;
    267 
    268     /* Drag/drop */
    269     ClipDescription mDragDescription;
    270     View mCurrentDragView;
    271     volatile Object mLocalDragState;
    272     final PointF mDragPoint = new PointF();
    273     final PointF mLastTouchPoint = new PointF();
    274 
    275     private boolean mProfileRendering;
    276     private Thread mRenderProfiler;
    277     private volatile boolean mRenderProfilingEnabled;
    278 
    279     // Variables to track frames per second, enabled via DEBUG_FPS flag
    280     private long mFpsStartTime = -1;
    281     private long mFpsPrevTime = -1;
    282     private int mFpsNumFrames;
    283 
    284     /**
    285      * see {@link #playSoundEffect(int)}
    286      */
    287     AudioManager mAudioManager;
    288 
    289     final AccessibilityManager mAccessibilityManager;
    290 
    291     AccessibilityInteractionController mAccessibilityInteractionController;
    292 
    293     AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager;
    294 
    295     SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
    296 
    297     private final int mDensity;
    298 
    299     /**
    300      * Consistency verifier for debugging purposes.
    301      */
    302     protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
    303             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
    304                     new InputEventConsistencyVerifier(this, 0) : null;
    305 
    306     public static IWindowSession getWindowSession(Looper mainLooper) {
    307         synchronized (mStaticInit) {
    308             if (!mInitialized) {
    309                 try {
    310                     InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
    311                     sWindowSession = Display.getWindowManager().openSession(
    312                             imm.getClient(), imm.getInputContext());
    313                     mInitialized = true;
    314                 } catch (RemoteException e) {
    315                 }
    316             }
    317             return sWindowSession;
    318         }
    319     }
    320 
    321     static final class SystemUiVisibilityInfo {
    322         int seq;
    323         int globalVisibility;
    324         int localValue;
    325         int localChanges;
    326     }
    327 
    328     public ViewRootImpl(Context context) {
    329         super();
    330 
    331         if (MEASURE_LATENCY) {
    332             if (lt == null) {
    333                 lt = new LatencyTimer(100, 1000);
    334             }
    335         }
    336 
    337         // Initialize the statics when this class is first instantiated. This is
    338         // done here instead of in the static block because Zygote does not
    339         // allow the spawning of threads.
    340         getWindowSession(context.getMainLooper());
    341 
    342         mThread = Thread.currentThread();
    343         mLocation = new WindowLeaked(null);
    344         mLocation.fillInStackTrace();
    345         mWidth = -1;
    346         mHeight = -1;
    347         mDirty = new Rect();
    348         mTempRect = new Rect();
    349         mVisRect = new Rect();
    350         mWinFrame = new Rect();
    351         mWindow = new W(this);
    352         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
    353         mInputMethodCallback = new InputMethodCallback(this);
    354         mViewVisibility = View.GONE;
    355         mTransparentRegion = new Region();
    356         mPreviousTransparentRegion = new Region();
    357         mFirst = true; // true for the first time the view is added
    358         mAdded = false;
    359         mAccessibilityManager = AccessibilityManager.getInstance(context);
    360         mAccessibilityInteractionConnectionManager =
    361             new AccessibilityInteractionConnectionManager();
    362         mAccessibilityManager.addAccessibilityStateChangeListener(
    363                 mAccessibilityInteractionConnectionManager);
    364         mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
    365         mViewConfiguration = ViewConfiguration.get(context);
    366         mDensity = context.getResources().getDisplayMetrics().densityDpi;
    367         mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
    368         mProfileRendering = Boolean.parseBoolean(
    369                 SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false"));
    370     }
    371 
    372     public static void addFirstDrawHandler(Runnable callback) {
    373         synchronized (sFirstDrawHandlers) {
    374             if (!sFirstDrawComplete) {
    375                 sFirstDrawHandlers.add(callback);
    376             }
    377         }
    378     }
    379 
    380     public static void addConfigCallback(ComponentCallbacks callback) {
    381         synchronized (sConfigCallbacks) {
    382             sConfigCallbacks.add(callback);
    383         }
    384     }
    385 
    386     // FIXME for perf testing only
    387     private boolean mProfile = false;
    388 
    389     /**
    390      * Call this to profile the next traversal call.
    391      * FIXME for perf testing only. Remove eventually
    392      */
    393     public void profile() {
    394         mProfile = true;
    395     }
    396 
    397     /**
    398      * Indicates whether we are in touch mode. Calling this method triggers an IPC
    399      * call and should be avoided whenever possible.
    400      *
    401      * @return True, if the device is in touch mode, false otherwise.
    402      *
    403      * @hide
    404      */
    405     static boolean isInTouchMode() {
    406         if (mInitialized) {
    407             try {
    408                 return sWindowSession.getInTouchMode();
    409             } catch (RemoteException e) {
    410             }
    411         }
    412         return false;
    413     }
    414 
    415     /**
    416      * We have one child
    417      */
    418     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    419         synchronized (this) {
    420             if (mView == null) {
    421                 mView = view;
    422                 mFallbackEventHandler.setView(view);
    423                 mWindowAttributes.copyFrom(attrs);
    424                 attrs = mWindowAttributes;
    425 
    426                 if (view instanceof RootViewSurfaceTaker) {
    427                     mSurfaceHolderCallback =
    428                             ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
    429                     if (mSurfaceHolderCallback != null) {
    430                         mSurfaceHolder = new TakenSurfaceHolder();
    431                         mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
    432                     }
    433                 }
    434 
    435                 CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
    436                 mTranslator = compatibilityInfo.getTranslator();
    437 
    438                 // If the application owns the surface, don't enable hardware acceleration
    439                 if (mSurfaceHolder == null) {
    440                     enableHardwareAcceleration(attrs);
    441                 }
    442 
    443                 boolean restore = false;
    444                 if (mTranslator != null) {
    445                     mSurface.setCompatibilityTranslator(mTranslator);
    446                     restore = true;
    447                     attrs.backup();
    448                     mTranslator.translateWindowLayout(attrs);
    449                 }
    450                 if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
    451 
    452                 if (!compatibilityInfo.supportsScreen()) {
    453                     attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
    454                     mLastInCompatMode = true;
    455                 }
    456 
    457                 mSoftInputMode = attrs.softInputMode;
    458                 mWindowAttributesChanged = true;
    459                 mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
    460                 mAttachInfo.mRootView = view;
    461                 mAttachInfo.mScalingRequired = mTranslator != null;
    462                 mAttachInfo.mApplicationScale =
    463                         mTranslator == null ? 1.0f : mTranslator.applicationScale;
    464                 if (panelParentView != null) {
    465                     mAttachInfo.mPanelParentWindowToken
    466                             = panelParentView.getApplicationWindowToken();
    467                 }
    468                 mAdded = true;
    469                 int res; /* = WindowManagerImpl.ADD_OKAY; */
    470 
    471                 // Schedule the first layout -before- adding to the window
    472                 // manager, to make sure we do the relayout before receiving
    473                 // any other events from the system.
    474                 requestLayout();
    475                 if ((mWindowAttributes.inputFeatures
    476                         & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
    477                     mInputChannel = new InputChannel();
    478                 }
    479                 try {
    480                     mOrigWindowType = mWindowAttributes.type;
    481                     res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
    482                             getHostVisibility(), mAttachInfo.mContentInsets,
    483                             mInputChannel);
    484                 } catch (RemoteException e) {
    485                     mAdded = false;
    486                     mView = null;
    487                     mAttachInfo.mRootView = null;
    488                     mInputChannel = null;
    489                     mFallbackEventHandler.setView(null);
    490                     unscheduleTraversals();
    491                     throw new RuntimeException("Adding window failed", e);
    492                 } finally {
    493                     if (restore) {
    494                         attrs.restore();
    495                     }
    496                 }
    497 
    498                 if (mTranslator != null) {
    499                     mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
    500                 }
    501                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
    502                 mPendingVisibleInsets.set(0, 0, 0, 0);
    503                 if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
    504                 if (res < WindowManagerImpl.ADD_OKAY) {
    505                     mView = null;
    506                     mAttachInfo.mRootView = null;
    507                     mAdded = false;
    508                     mFallbackEventHandler.setView(null);
    509                     unscheduleTraversals();
    510                     switch (res) {
    511                         case WindowManagerImpl.ADD_BAD_APP_TOKEN:
    512                         case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
    513                             throw new WindowManagerImpl.BadTokenException(
    514                                 "Unable to add window -- token " + attrs.token
    515                                 + " is not valid; is your activity running?");
    516                         case WindowManagerImpl.ADD_NOT_APP_TOKEN:
    517                             throw new WindowManagerImpl.BadTokenException(
    518                                 "Unable to add window -- token " + attrs.token
    519                                 + " is not for an application");
    520                         case WindowManagerImpl.ADD_APP_EXITING:
    521                             throw new WindowManagerImpl.BadTokenException(
    522                                 "Unable to add window -- app for token " + attrs.token
    523                                 + " is exiting");
    524                         case WindowManagerImpl.ADD_DUPLICATE_ADD:
    525                             throw new WindowManagerImpl.BadTokenException(
    526                                 "Unable to add window -- window " + mWindow
    527                                 + " has already been added");
    528                         case WindowManagerImpl.ADD_STARTING_NOT_NEEDED:
    529                             // Silently ignore -- we would have just removed it
    530                             // right away, anyway.
    531                             return;
    532                         case WindowManagerImpl.ADD_MULTIPLE_SINGLETON:
    533                             throw new WindowManagerImpl.BadTokenException(
    534                                 "Unable to add window " + mWindow +
    535                                 " -- another window of this type already exists");
    536                         case WindowManagerImpl.ADD_PERMISSION_DENIED:
    537                             throw new WindowManagerImpl.BadTokenException(
    538                                 "Unable to add window " + mWindow +
    539                                 " -- permission denied for this window type");
    540                     }
    541                     throw new RuntimeException(
    542                         "Unable to add window -- unknown error code " + res);
    543                 }
    544 
    545                 if (view instanceof RootViewSurfaceTaker) {
    546                     mInputQueueCallback =
    547                         ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
    548                 }
    549                 if (mInputChannel != null) {
    550                     if (mInputQueueCallback != null) {
    551                         mInputQueue = new InputQueue(mInputChannel);
    552                         mInputQueueCallback.onInputQueueCreated(mInputQueue);
    553                     } else {
    554                         InputQueue.registerInputChannel(mInputChannel, mInputHandler,
    555                                 Looper.myQueue());
    556                     }
    557                 }
    558 
    559                 view.assignParent(this);
    560                 mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
    561                 mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
    562 
    563                 if (mAccessibilityManager.isEnabled()) {
    564                     mAccessibilityInteractionConnectionManager.ensureConnection();
    565                 }
    566             }
    567         }
    568     }
    569 
    570     void destroyHardwareResources() {
    571         if (mAttachInfo.mHardwareRenderer != null) {
    572             if (mAttachInfo.mHardwareRenderer.isEnabled()) {
    573                 mAttachInfo.mHardwareRenderer.destroyLayers(mView);
    574             }
    575             mAttachInfo.mHardwareRenderer.destroy(false);
    576         }
    577     }
    578 
    579     void terminateHardwareResources() {
    580         if (mAttachInfo.mHardwareRenderer != null) {
    581             mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
    582             mAttachInfo.mHardwareRenderer.destroy(false);
    583         }
    584     }
    585 
    586     void destroyHardwareLayers() {
    587         if (mThread != Thread.currentThread()) {
    588             if (mAttachInfo.mHardwareRenderer != null &&
    589                     mAttachInfo.mHardwareRenderer.isEnabled()) {
    590                 HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE);
    591             }
    592         } else {
    593             if (mAttachInfo.mHardwareRenderer != null &&
    594                     mAttachInfo.mHardwareRenderer.isEnabled()) {
    595                 mAttachInfo.mHardwareRenderer.destroyLayers(mView);
    596             }
    597         }
    598     }
    599 
    600     private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
    601         mAttachInfo.mHardwareAccelerated = false;
    602         mAttachInfo.mHardwareAccelerationRequested = false;
    603 
    604         // Don't enable hardware acceleration when the application is in compatibility mode
    605         if (mTranslator != null) return;
    606 
    607         // Try to enable hardware acceleration if requested
    608         final boolean hardwareAccelerated =
    609                 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
    610 
    611         if (hardwareAccelerated) {
    612             if (!HardwareRenderer.isAvailable()) {
    613                 return;
    614             }
    615 
    616             // Persistent processes (including the system) should not do
    617             // accelerated rendering on low-end devices.  In that case,
    618             // sRendererDisabled will be set.  In addition, the system process
    619             // itself should never do accelerated rendering.  In that case, both
    620             // sRendererDisabled and sSystemRendererDisabled are set.  When
    621             // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
    622             // can be used by code on the system process to escape that and enable
    623             // HW accelerated drawing.  (This is basically for the lock screen.)
    624 
    625             final boolean fakeHwAccelerated = (attrs.privateFlags &
    626                     WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
    627             final boolean forceHwAccelerated = (attrs.privateFlags &
    628                     WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
    629 
    630             if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
    631                     && forceHwAccelerated)) {
    632                 // Don't enable hardware acceleration when we're not on the main thread
    633                 if (!HardwareRenderer.sSystemRendererDisabled
    634                         && Looper.getMainLooper() != Looper.myLooper()) {
    635                     Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
    636                             + "acceleration outside of the main thread, aborting");
    637                     return;
    638                 }
    639 
    640                 final boolean translucent = attrs.format != PixelFormat.OPAQUE;
    641                 if (mAttachInfo.mHardwareRenderer != null) {
    642                     mAttachInfo.mHardwareRenderer.destroy(true);
    643                 }
    644                 mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
    645                 mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested
    646                         = mAttachInfo.mHardwareRenderer != null;
    647             } else if (fakeHwAccelerated) {
    648                 // The window had wanted to use hardware acceleration, but this
    649                 // is not allowed in its process.  By setting this flag, it can
    650                 // still render as if it was accelerated.  This is basically for
    651                 // the preview windows the window manager shows for launching
    652                 // applications, so they will look more like the app being launched.
    653                 mAttachInfo.mHardwareAccelerationRequested = true;
    654             }
    655         }
    656     }
    657 
    658     public View getView() {
    659         return mView;
    660     }
    661 
    662     final WindowLeaked getLocation() {
    663         return mLocation;
    664     }
    665 
    666     void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
    667         synchronized (this) {
    668             int oldSoftInputMode = mWindowAttributes.softInputMode;
    669             // preserve compatible window flag if exists.
    670             int compatibleWindowFlag =
    671                 mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
    672             mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs);
    673             mWindowAttributes.flags |= compatibleWindowFlag;
    674 
    675             if (newView) {
    676                 mSoftInputMode = attrs.softInputMode;
    677                 requestLayout();
    678             }
    679             // Don't lose the mode we last auto-computed.
    680             if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
    681                     == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
    682                 mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
    683                         & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
    684                         | (oldSoftInputMode
    685                                 & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
    686             }
    687             mWindowAttributesChanged = true;
    688             scheduleTraversals();
    689         }
    690     }
    691 
    692     void handleAppVisibility(boolean visible) {
    693         if (mAppVisible != visible) {
    694             mAppVisible = visible;
    695             scheduleTraversals();
    696         }
    697     }
    698 
    699     void handleGetNewSurface() {
    700         mNewSurfaceNeeded = true;
    701         mFullRedrawNeeded = true;
    702         scheduleTraversals();
    703     }
    704 
    705     /**
    706      * {@inheritDoc}
    707      */
    708     public void requestLayout() {
    709         checkThread();
    710         mLayoutRequested = true;
    711         scheduleTraversals();
    712     }
    713 
    714     /**
    715      * {@inheritDoc}
    716      */
    717     public boolean isLayoutRequested() {
    718         return mLayoutRequested;
    719     }
    720 
    721     public void invalidateChild(View child, Rect dirty) {
    722         checkThread();
    723         if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
    724         if (dirty == null) {
    725             // Fast invalidation for GL-enabled applications; GL must redraw everything
    726             invalidate();
    727             return;
    728         }
    729         if (mCurScrollY != 0 || mTranslator != null) {
    730             mTempRect.set(dirty);
    731             dirty = mTempRect;
    732             if (mCurScrollY != 0) {
    733                dirty.offset(0, -mCurScrollY);
    734             }
    735             if (mTranslator != null) {
    736                 mTranslator.translateRectInAppWindowToScreen(dirty);
    737             }
    738             if (mAttachInfo.mScalingRequired) {
    739                 dirty.inset(-1, -1);
    740             }
    741         }
    742         if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
    743             mAttachInfo.mSetIgnoreDirtyState = true;
    744             mAttachInfo.mIgnoreDirtyState = true;
    745         }
    746         mDirty.union(dirty);
    747         if (!mWillDrawSoon) {
    748             scheduleTraversals();
    749         }
    750     }
    751 
    752     void invalidate() {
    753         mDirty.set(0, 0, mWidth, mHeight);
    754         scheduleTraversals();
    755     }
    756 
    757     void setStopped(boolean stopped) {
    758         if (mStopped != stopped) {
    759             mStopped = stopped;
    760             if (!stopped) {
    761                 scheduleTraversals();
    762             }
    763         }
    764     }
    765 
    766     public ViewParent getParent() {
    767         return null;
    768     }
    769 
    770     public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
    771         invalidateChild(null, dirty);
    772         return null;
    773     }
    774 
    775     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
    776         if (child != mView) {
    777             throw new RuntimeException("child is not mine, honest!");
    778         }
    779         // Note: don't apply scroll offset, because we want to know its
    780         // visibility in the virtual canvas being given to the view hierarchy.
    781         return r.intersect(0, 0, mWidth, mHeight);
    782     }
    783 
    784     public void bringChildToFront(View child) {
    785     }
    786 
    787     public void scheduleTraversals() {
    788         if (!mTraversalScheduled) {
    789             mTraversalScheduled = true;
    790 
    791             //noinspection ConstantConditions
    792             if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
    793                 final long now = System.nanoTime();
    794                 Log.d(TAG, "Latency: Scheduled traversal, it has been "
    795                         + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
    796                         + "ms since the last traversal finished.");
    797             }
    798 
    799             sendEmptyMessage(DO_TRAVERSAL);
    800         }
    801     }
    802 
    803     public void unscheduleTraversals() {
    804         if (mTraversalScheduled) {
    805             mTraversalScheduled = false;
    806             removeMessages(DO_TRAVERSAL);
    807         }
    808     }
    809 
    810     int getHostVisibility() {
    811         return mAppVisible ? mView.getVisibility() : View.GONE;
    812     }
    813 
    814     void disposeResizeBuffer() {
    815         if (mResizeBuffer != null) {
    816             mResizeBuffer.destroy();
    817             mResizeBuffer = null;
    818         }
    819     }
    820 
    821     /**
    822      * Add LayoutTransition to the list of transitions to be started in the next traversal.
    823      * This list will be cleared after the transitions on the list are start()'ed. These
    824      * transitionsa re added by LayoutTransition itself when it sets up animations. The setup
    825      * happens during the layout phase of traversal, which we want to complete before any of the
    826      * animations are started (because those animations may side-effect properties that layout
    827      * depends upon, like the bounding rectangles of the affected views). So we add the transition
    828      * to the list and it is started just prior to starting the drawing phase of traversal.
    829      *
    830      * @param transition The LayoutTransition to be started on the next traversal.
    831      *
    832      * @hide
    833      */
    834     public void requestTransitionStart(LayoutTransition transition) {
    835         if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) {
    836             if (mPendingTransitions == null) {
    837                  mPendingTransitions = new ArrayList<LayoutTransition>();
    838             }
    839             mPendingTransitions.add(transition);
    840         }
    841     }
    842 
    843     private void processInputEvents(boolean outOfOrder) {
    844         while (mPendingInputEvents != null) {
    845             handleMessage(mPendingInputEvents.mMessage);
    846             InputEventMessage tmpMessage = mPendingInputEvents;
    847             mPendingInputEvents = mPendingInputEvents.mNext;
    848             tmpMessage.recycle();
    849             if (outOfOrder) {
    850                 removeMessages(PROCESS_INPUT_EVENTS);
    851             }
    852         }
    853     }
    854 
    855     private void performTraversals() {
    856         // cache mView since it is used so much below...
    857         final View host = mView;
    858 
    859         processInputEvents(true);
    860 
    861         if (DBG) {
    862             System.out.println("======================================");
    863             System.out.println("performTraversals");
    864             host.debug();
    865         }
    866 
    867         if (host == null || !mAdded)
    868             return;
    869 
    870         mTraversalScheduled = false;
    871         mWillDrawSoon = true;
    872         boolean windowSizeMayChange = false;
    873         boolean fullRedrawNeeded = mFullRedrawNeeded;
    874         boolean newSurface = false;
    875         boolean surfaceChanged = false;
    876         WindowManager.LayoutParams lp = mWindowAttributes;
    877 
    878         int desiredWindowWidth;
    879         int desiredWindowHeight;
    880         int childWidthMeasureSpec;
    881         int childHeightMeasureSpec;
    882 
    883         final View.AttachInfo attachInfo = mAttachInfo;
    884 
    885         final int viewVisibility = getHostVisibility();
    886         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
    887                 || mNewSurfaceNeeded;
    888 
    889         WindowManager.LayoutParams params = null;
    890         if (mWindowAttributesChanged) {
    891             mWindowAttributesChanged = false;
    892             surfaceChanged = true;
    893             params = lp;
    894         }
    895         CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
    896         if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
    897             params = lp;
    898             fullRedrawNeeded = true;
    899             mLayoutRequested = true;
    900             if (mLastInCompatMode) {
    901                 params.flags &= ~WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
    902                 mLastInCompatMode = false;
    903             } else {
    904                 params.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
    905                 mLastInCompatMode = true;
    906             }
    907         }
    908 
    909         mWindowAttributesChangesFlag = 0;
    910 
    911         Rect frame = mWinFrame;
    912         if (mFirst) {
    913             fullRedrawNeeded = true;
    914             mLayoutRequested = true;
    915 
    916             if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) {
    917                 // NOTE -- system code, won't try to do compat mode.
    918                 Display disp = WindowManagerImpl.getDefault().getDefaultDisplay();
    919                 Point size = new Point();
    920                 disp.getRealSize(size);
    921                 desiredWindowWidth = size.x;
    922                 desiredWindowHeight = size.y;
    923             } else {
    924                 DisplayMetrics packageMetrics =
    925                     mView.getContext().getResources().getDisplayMetrics();
    926                 desiredWindowWidth = packageMetrics.widthPixels;
    927                 desiredWindowHeight = packageMetrics.heightPixels;
    928             }
    929 
    930             // For the very first time, tell the view hierarchy that it
    931             // is attached to the window.  Note that at this point the surface
    932             // object is not initialized to its backing store, but soon it
    933             // will be (assuming the window is visible).
    934             attachInfo.mSurface = mSurface;
    935             // We used to use the following condition to choose 32 bits drawing caches:
    936             // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
    937             // However, windows are now always 32 bits by default, so choose 32 bits
    938             attachInfo.mUse32BitDrawingCache = true;
    939             attachInfo.mHasWindowFocus = false;
    940             attachInfo.mWindowVisibility = viewVisibility;
    941             attachInfo.mRecomputeGlobalAttributes = false;
    942             attachInfo.mKeepScreenOn = false;
    943             attachInfo.mSystemUiVisibility = 0;
    944             viewVisibilityChanged = false;
    945             mLastConfiguration.setTo(host.getResources().getConfiguration());
    946             host.dispatchAttachedToWindow(attachInfo, 0);
    947             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
    948 
    949             host.fitSystemWindows(mAttachInfo.mContentInsets);
    950 
    951         } else {
    952             desiredWindowWidth = frame.width();
    953             desiredWindowHeight = frame.height();
    954             if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
    955                 if (DEBUG_ORIENTATION) Log.v(TAG,
    956                         "View " + host + " resized to: " + frame);
    957                 fullRedrawNeeded = true;
    958                 mLayoutRequested = true;
    959                 windowSizeMayChange = true;
    960             }
    961         }
    962 
    963         if (viewVisibilityChanged) {
    964             attachInfo.mWindowVisibility = viewVisibility;
    965             host.dispatchWindowVisibilityChanged(viewVisibility);
    966             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
    967                 destroyHardwareResources();
    968             }
    969             if (viewVisibility == View.GONE) {
    970                 // After making a window gone, we will count it as being
    971                 // shown for the first time the next time it gets focus.
    972                 mHasHadWindowFocus = false;
    973             }
    974         }
    975 
    976         boolean insetsChanged = false;
    977 
    978         if (mLayoutRequested && !mStopped) {
    979             // Execute enqueued actions on every layout in case a view that was detached
    980             // enqueued an action after being detached
    981             getRunQueue().executeActions(attachInfo.mHandler);
    982 
    983             final Resources res = mView.getContext().getResources();
    984 
    985             if (mFirst) {
    986                 // make sure touch mode code executes by setting cached value
    987                 // to opposite of the added touch mode.
    988                 mAttachInfo.mInTouchMode = !mAddedTouchMode;
    989                 ensureTouchModeLocally(mAddedTouchMode);
    990             } else {
    991                 if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
    992                     insetsChanged = true;
    993                 }
    994                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
    995                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
    996                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
    997                             + mAttachInfo.mVisibleInsets);
    998                 }
    999                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
   1000                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
   1001                     windowSizeMayChange = true;
   1002 
   1003                     if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) {
   1004                         // NOTE -- system code, won't try to do compat mode.
   1005                         Display disp = WindowManagerImpl.getDefault().getDefaultDisplay();
   1006                         Point size = new Point();
   1007                         disp.getRealSize(size);
   1008                         desiredWindowWidth = size.x;
   1009                         desiredWindowHeight = size.y;
   1010                     } else {
   1011                         DisplayMetrics packageMetrics = res.getDisplayMetrics();
   1012                         desiredWindowWidth = packageMetrics.widthPixels;
   1013                         desiredWindowHeight = packageMetrics.heightPixels;
   1014                     }
   1015                 }
   1016             }
   1017 
   1018             // Ask host how big it wants to be
   1019             if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
   1020                     "Measuring " + host + " in display " + desiredWindowWidth
   1021                     + "x" + desiredWindowHeight + "...");
   1022 
   1023             boolean goodMeasure = false;
   1024             if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
   1025                 // On large screens, we don't want to allow dialogs to just
   1026                 // stretch to fill the entire width of the screen to display
   1027                 // one line of text.  First try doing the layout at a smaller
   1028                 // size to see if it will fit.
   1029                 final DisplayMetrics packageMetrics = res.getDisplayMetrics();
   1030                 res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
   1031                 int baseSize = 0;
   1032                 if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
   1033                     baseSize = (int)mTmpValue.getDimension(packageMetrics);
   1034                 }
   1035                 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
   1036                 if (baseSize != 0 && desiredWindowWidth > baseSize) {
   1037                     childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
   1038                     childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
   1039                     host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
   1040                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
   1041                             + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
   1042                     if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
   1043                         goodMeasure = true;
   1044                     } else {
   1045                         // Didn't fit in that size... try expanding a bit.
   1046                         baseSize = (baseSize+desiredWindowWidth)/2;
   1047                         if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
   1048                                 + baseSize);
   1049                         childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
   1050                         host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
   1051                         if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
   1052                                 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
   1053                         if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
   1054                             if (DEBUG_DIALOG) Log.v(TAG, "Good!");
   1055                             goodMeasure = true;
   1056                         }
   1057                     }
   1058                 }
   1059             }
   1060 
   1061             if (!goodMeasure) {
   1062                 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
   1063                 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
   1064                 host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
   1065                 if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
   1066                     windowSizeMayChange = true;
   1067                 }
   1068             }
   1069 
   1070             if (DBG) {
   1071                 System.out.println("======================================");
   1072                 System.out.println("performTraversals -- after measure");
   1073                 host.debug();
   1074             }
   1075         }
   1076 
   1077         if (attachInfo.mRecomputeGlobalAttributes && host.mAttachInfo != null) {
   1078             //Log.i(TAG, "Computing view hierarchy attributes!");
   1079             attachInfo.mRecomputeGlobalAttributes = false;
   1080             boolean oldScreenOn = attachInfo.mKeepScreenOn;
   1081             int oldVis = attachInfo.mSystemUiVisibility;
   1082             boolean oldHasSystemUiListeners = attachInfo.mHasSystemUiListeners;
   1083             attachInfo.mKeepScreenOn = false;
   1084             attachInfo.mSystemUiVisibility = 0;
   1085             attachInfo.mHasSystemUiListeners = false;
   1086             host.dispatchCollectViewAttributes(0);
   1087             if (attachInfo.mKeepScreenOn != oldScreenOn
   1088                     || attachInfo.mSystemUiVisibility != oldVis
   1089                     || attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) {
   1090                 params = lp;
   1091             }
   1092         }
   1093         if (attachInfo.mForceReportNewAttributes) {
   1094             attachInfo.mForceReportNewAttributes = false;
   1095             params = lp;
   1096         }
   1097 
   1098         if (mFirst || attachInfo.mViewVisibilityChanged) {
   1099             attachInfo.mViewVisibilityChanged = false;
   1100             int resizeMode = mSoftInputMode &
   1101                     WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
   1102             // If we are in auto resize mode, then we need to determine
   1103             // what mode to use now.
   1104             if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
   1105                 final int N = attachInfo.mScrollContainers.size();
   1106                 for (int i=0; i<N; i++) {
   1107                     if (attachInfo.mScrollContainers.get(i).isShown()) {
   1108                         resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
   1109                     }
   1110                 }
   1111                 if (resizeMode == 0) {
   1112                     resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
   1113                 }
   1114                 if ((lp.softInputMode &
   1115                         WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
   1116                     lp.softInputMode = (lp.softInputMode &
   1117                             ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
   1118                             resizeMode;
   1119                     params = lp;
   1120                 }
   1121             }
   1122         }
   1123 
   1124         if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
   1125             if (!PixelFormat.formatHasAlpha(params.format)) {
   1126                 params.format = PixelFormat.TRANSLUCENT;
   1127             }
   1128         }
   1129 
   1130         boolean windowShouldResize = mLayoutRequested && windowSizeMayChange
   1131             && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
   1132                 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
   1133                         frame.width() < desiredWindowWidth && frame.width() != mWidth)
   1134                 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
   1135                         frame.height() < desiredWindowHeight && frame.height() != mHeight));
   1136 
   1137         final boolean computesInternalInsets =
   1138                 attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
   1139 
   1140         boolean insetsPending = false;
   1141         int relayoutResult = 0;
   1142 
   1143         if (mFirst || windowShouldResize || insetsChanged ||
   1144                 viewVisibilityChanged || params != null) {
   1145 
   1146             if (viewVisibility == View.VISIBLE) {
   1147                 // If this window is giving internal insets to the window
   1148                 // manager, and it is being added or changing its visibility,
   1149                 // then we want to first give the window manager "fake"
   1150                 // insets to cause it to effectively ignore the content of
   1151                 // the window during layout.  This avoids it briefly causing
   1152                 // other windows to resize/move based on the raw frame of the
   1153                 // window, waiting until we can finish laying out this window
   1154                 // and get back to the window manager with the ultimately
   1155                 // computed insets.
   1156                 insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged);
   1157             }
   1158 
   1159             if (mSurfaceHolder != null) {
   1160                 mSurfaceHolder.mSurfaceLock.lock();
   1161                 mDrawingAllowed = true;
   1162             }
   1163 
   1164             boolean hwInitialized = false;
   1165             boolean contentInsetsChanged = false;
   1166             boolean visibleInsetsChanged;
   1167             boolean hadSurface = mSurface.isValid();
   1168 
   1169             try {
   1170                 int fl = 0;
   1171                 if (params != null) {
   1172                     fl = params.flags;
   1173                     if (attachInfo.mKeepScreenOn) {
   1174                         params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
   1175                     }
   1176                     params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility;
   1177                     params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners;
   1178                 }
   1179                 if (DEBUG_LAYOUT) {
   1180                     Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" +
   1181                             host.getMeasuredHeight() + ", params=" + params);
   1182                 }
   1183 
   1184                 final int surfaceGenerationId = mSurface.getGenerationId();
   1185                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
   1186 
   1187                 if (params != null) {
   1188                     params.flags = fl;
   1189                 }
   1190 
   1191                 if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
   1192                         + " content=" + mPendingContentInsets.toShortString()
   1193                         + " visible=" + mPendingVisibleInsets.toShortString()
   1194                         + " surface=" + mSurface);
   1195 
   1196                 if (mPendingConfiguration.seq != 0) {
   1197                     if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
   1198                             + mPendingConfiguration);
   1199                     updateConfiguration(mPendingConfiguration, !mFirst);
   1200                     mPendingConfiguration.seq = 0;
   1201                 }
   1202 
   1203                 contentInsetsChanged = !mPendingContentInsets.equals(
   1204                         mAttachInfo.mContentInsets);
   1205                 visibleInsetsChanged = !mPendingVisibleInsets.equals(
   1206                         mAttachInfo.mVisibleInsets);
   1207                 if (contentInsetsChanged) {
   1208                     if (mWidth > 0 && mHeight > 0 &&
   1209                             mSurface != null && mSurface.isValid() &&
   1210                             !mAttachInfo.mTurnOffWindowResizeAnim &&
   1211                             mAttachInfo.mHardwareRenderer != null &&
   1212                             mAttachInfo.mHardwareRenderer.isEnabled() &&
   1213                             mAttachInfo.mHardwareRenderer.validate() &&
   1214                             lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
   1215 
   1216                         disposeResizeBuffer();
   1217 
   1218                         boolean completed = false;
   1219                         HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas();
   1220                         HardwareCanvas layerCanvas = null;
   1221                         try {
   1222                             if (mResizeBuffer == null) {
   1223                                 mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
   1224                                         mWidth, mHeight, false);
   1225                             } else if (mResizeBuffer.getWidth() != mWidth ||
   1226                                     mResizeBuffer.getHeight() != mHeight) {
   1227                                 mResizeBuffer.resize(mWidth, mHeight);
   1228                             }
   1229                             layerCanvas = mResizeBuffer.start(hwRendererCanvas);
   1230                             layerCanvas.setViewport(mWidth, mHeight);
   1231                             layerCanvas.onPreDraw(null);
   1232                             final int restoreCount = layerCanvas.save();
   1233 
   1234                             layerCanvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
   1235 
   1236                             int yoff;
   1237                             final boolean scrolling = mScroller != null
   1238                                     && mScroller.computeScrollOffset();
   1239                             if (scrolling) {
   1240                                 yoff = mScroller.getCurrY();
   1241                                 mScroller.abortAnimation();
   1242                             } else {
   1243                                 yoff = mScrollY;
   1244                             }
   1245 
   1246                             layerCanvas.translate(0, -yoff);
   1247                             if (mTranslator != null) {
   1248                                 mTranslator.translateCanvas(layerCanvas);
   1249                             }
   1250 
   1251                             mView.draw(layerCanvas);
   1252 
   1253                             mResizeBufferStartTime = SystemClock.uptimeMillis();
   1254                             mResizeBufferDuration = mView.getResources().getInteger(
   1255                                     com.android.internal.R.integer.config_mediumAnimTime);
   1256                             completed = true;
   1257 
   1258                             layerCanvas.restoreToCount(restoreCount);
   1259                         } catch (OutOfMemoryError e) {
   1260                             Log.w(TAG, "Not enough memory for content change anim buffer", e);
   1261                         } finally {
   1262                             if (layerCanvas != null) {
   1263                                 layerCanvas.onPostDraw();
   1264                             }
   1265                             if (mResizeBuffer != null) {
   1266                                 mResizeBuffer.end(hwRendererCanvas);
   1267                                 if (!completed) {
   1268                                     mResizeBuffer.destroy();
   1269                                     mResizeBuffer = null;
   1270                                 }
   1271                             }
   1272                         }
   1273                     }
   1274                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
   1275                     host.fitSystemWindows(mAttachInfo.mContentInsets);
   1276                     if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
   1277                             + mAttachInfo.mContentInsets);
   1278                 }
   1279                 if (visibleInsetsChanged) {
   1280                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
   1281                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
   1282                             + mAttachInfo.mVisibleInsets);
   1283                 }
   1284 
   1285                 if (!hadSurface) {
   1286                     if (mSurface.isValid()) {
   1287                         // If we are creating a new surface, then we need to
   1288                         // completely redraw it.  Also, when we get to the
   1289                         // point of drawing it we will hold off and schedule
   1290                         // a new traversal instead.  This is so we can tell the
   1291                         // window manager about all of the windows being displayed
   1292                         // before actually drawing them, so it can display then
   1293                         // all at once.
   1294                         newSurface = true;
   1295                         fullRedrawNeeded = true;
   1296                         mPreviousTransparentRegion.setEmpty();
   1297 
   1298                         if (mAttachInfo.mHardwareRenderer != null) {
   1299                             try {
   1300                                 hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mHolder);
   1301                             } catch (Surface.OutOfResourcesException e) {
   1302                                 Log.e(TAG, "OutOfResourcesException initializing HW surface", e);
   1303                                 try {
   1304                                     if (!sWindowSession.outOfMemory(mWindow)) {
   1305                                         Slog.w(TAG, "No processes killed for memory; killing self");
   1306                                         Process.killProcess(Process.myPid());
   1307                                     }
   1308                                 } catch (RemoteException ex) {
   1309                                 }
   1310                                 mLayoutRequested = true;    // ask wm for a new surface next time.
   1311                                 return;
   1312                             }
   1313                         }
   1314                     }
   1315                 } else if (!mSurface.isValid()) {
   1316                     // If the surface has been removed, then reset the scroll
   1317                     // positions.
   1318                     mLastScrolledFocus = null;
   1319                     mScrollY = mCurScrollY = 0;
   1320                     if (mScroller != null) {
   1321                         mScroller.abortAnimation();
   1322                     }
   1323                     disposeResizeBuffer();
   1324                     // Our surface is gone
   1325                     if (mAttachInfo.mHardwareRenderer != null &&
   1326                             mAttachInfo.mHardwareRenderer.isEnabled()) {
   1327                         mAttachInfo.mHardwareRenderer.destroy(true);
   1328                     }
   1329                 } else if (surfaceGenerationId != mSurface.getGenerationId() &&
   1330                         mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) {
   1331                     fullRedrawNeeded = true;
   1332                     try {
   1333                         mAttachInfo.mHardwareRenderer.updateSurface(mHolder);
   1334                     } catch (Surface.OutOfResourcesException e) {
   1335                         Log.e(TAG, "OutOfResourcesException updating HW surface", e);
   1336                         try {
   1337                             if (!sWindowSession.outOfMemory(mWindow)) {
   1338                                 Slog.w(TAG, "No processes killed for memory; killing self");
   1339                                 Process.killProcess(Process.myPid());
   1340                             }
   1341                         } catch (RemoteException ex) {
   1342                         }
   1343                         mLayoutRequested = true;    // ask wm for a new surface next time.
   1344                         return;
   1345                     }
   1346                 }
   1347             } catch (RemoteException e) {
   1348             }
   1349 
   1350             if (DEBUG_ORIENTATION) Log.v(
   1351                     TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
   1352 
   1353             attachInfo.mWindowLeft = frame.left;
   1354             attachInfo.mWindowTop = frame.top;
   1355 
   1356             // !!FIXME!! This next section handles the case where we did not get the
   1357             // window size we asked for. We should avoid this by getting a maximum size from
   1358             // the window session beforehand.
   1359             mWidth = frame.width();
   1360             mHeight = frame.height();
   1361 
   1362             if (mSurfaceHolder != null) {
   1363                 // The app owns the surface; tell it about what is going on.
   1364                 if (mSurface.isValid()) {
   1365                     // XXX .copyFrom() doesn't work!
   1366                     //mSurfaceHolder.mSurface.copyFrom(mSurface);
   1367                     mSurfaceHolder.mSurface = mSurface;
   1368                 }
   1369                 mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
   1370                 mSurfaceHolder.mSurfaceLock.unlock();
   1371                 if (mSurface.isValid()) {
   1372                     if (!hadSurface) {
   1373                         mSurfaceHolder.ungetCallbacks();
   1374 
   1375                         mIsCreating = true;
   1376                         mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
   1377                         SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   1378                         if (callbacks != null) {
   1379                             for (SurfaceHolder.Callback c : callbacks) {
   1380                                 c.surfaceCreated(mSurfaceHolder);
   1381                             }
   1382                         }
   1383                         surfaceChanged = true;
   1384                     }
   1385                     if (surfaceChanged) {
   1386                         mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
   1387                                 lp.format, mWidth, mHeight);
   1388                         SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   1389                         if (callbacks != null) {
   1390                             for (SurfaceHolder.Callback c : callbacks) {
   1391                                 c.surfaceChanged(mSurfaceHolder, lp.format,
   1392                                         mWidth, mHeight);
   1393                             }
   1394                         }
   1395                     }
   1396                     mIsCreating = false;
   1397                 } else if (hadSurface) {
   1398                     mSurfaceHolder.ungetCallbacks();
   1399                     SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   1400                     mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
   1401                     if (callbacks != null) {
   1402                         for (SurfaceHolder.Callback c : callbacks) {
   1403                             c.surfaceDestroyed(mSurfaceHolder);
   1404                         }
   1405                     }
   1406                     mSurfaceHolder.mSurfaceLock.lock();
   1407                     try {
   1408                         mSurfaceHolder.mSurface = new Surface();
   1409                     } finally {
   1410                         mSurfaceHolder.mSurfaceLock.unlock();
   1411                     }
   1412                 }
   1413             }
   1414 
   1415             if (mAttachInfo.mHardwareRenderer != null &&
   1416                     mAttachInfo.mHardwareRenderer.isEnabled()) {
   1417                 if (hwInitialized || windowShouldResize ||
   1418                         mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
   1419                         mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
   1420                     mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
   1421                     if (!hwInitialized) {
   1422                         mAttachInfo.mHardwareRenderer.invalidate(mHolder);
   1423                     }
   1424                 }
   1425             }
   1426 
   1427             if (!mStopped) {
   1428                 boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
   1429                         (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
   1430                 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
   1431                         || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
   1432                     childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
   1433                     childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
   1434 
   1435                     if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="
   1436                             + mWidth + " measuredWidth=" + host.getMeasuredWidth()
   1437                             + " mHeight=" + mHeight
   1438                             + " measuredHeight=" + host.getMeasuredHeight()
   1439                             + " coveredInsetsChanged=" + contentInsetsChanged);
   1440 
   1441                      // Ask host how big it wants to be
   1442                     host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
   1443 
   1444                     // Implementation of weights from WindowManager.LayoutParams
   1445                     // We just grow the dimensions as needed and re-measure if
   1446                     // needs be
   1447                     int width = host.getMeasuredWidth();
   1448                     int height = host.getMeasuredHeight();
   1449                     boolean measureAgain = false;
   1450 
   1451                     if (lp.horizontalWeight > 0.0f) {
   1452                         width += (int) ((mWidth - width) * lp.horizontalWeight);
   1453                         childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
   1454                                 MeasureSpec.EXACTLY);
   1455                         measureAgain = true;
   1456                     }
   1457                     if (lp.verticalWeight > 0.0f) {
   1458                         height += (int) ((mHeight - height) * lp.verticalWeight);
   1459                         childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
   1460                                 MeasureSpec.EXACTLY);
   1461                         measureAgain = true;
   1462                     }
   1463 
   1464                     if (measureAgain) {
   1465                         if (DEBUG_LAYOUT) Log.v(TAG,
   1466                                 "And hey let's measure once more: width=" + width
   1467                                 + " height=" + height);
   1468                         host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
   1469                     }
   1470 
   1471                     mLayoutRequested = true;
   1472                 }
   1473             }
   1474         }
   1475 
   1476         final boolean didLayout = mLayoutRequested && !mStopped;
   1477         boolean triggerGlobalLayoutListener = didLayout
   1478                 || attachInfo.mRecomputeGlobalAttributes;
   1479         if (didLayout) {
   1480             mLayoutRequested = false;
   1481             mScrollMayChange = true;
   1482             if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
   1483                 TAG, "Laying out " + host + " to (" +
   1484                 host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
   1485             long startTime = 0L;
   1486             if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
   1487                 startTime = SystemClock.elapsedRealtime();
   1488             }
   1489             host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
   1490 
   1491             if (false && ViewDebug.consistencyCheckEnabled) {
   1492                 if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
   1493                     throw new IllegalStateException("The view hierarchy is an inconsistent state,"
   1494                             + "please refer to the logs with the tag "
   1495                             + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");
   1496                 }
   1497             }
   1498 
   1499             if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
   1500                 EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
   1501             }
   1502 
   1503             // By this point all views have been sized and positionned
   1504             // We can compute the transparent area
   1505 
   1506             if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
   1507                 // start out transparent
   1508                 // TODO: AVOID THAT CALL BY CACHING THE RESULT?
   1509                 host.getLocationInWindow(mTmpLocation);
   1510                 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
   1511                         mTmpLocation[0] + host.mRight - host.mLeft,
   1512                         mTmpLocation[1] + host.mBottom - host.mTop);
   1513 
   1514                 host.gatherTransparentRegion(mTransparentRegion);
   1515                 if (mTranslator != null) {
   1516                     mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
   1517                 }
   1518 
   1519                 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
   1520                     mPreviousTransparentRegion.set(mTransparentRegion);
   1521                     // reconfigure window manager
   1522                     try {
   1523                         sWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
   1524                     } catch (RemoteException e) {
   1525                     }
   1526                 }
   1527             }
   1528 
   1529             if (DBG) {
   1530                 System.out.println("======================================");
   1531                 System.out.println("performTraversals -- after setFrame");
   1532                 host.debug();
   1533             }
   1534         }
   1535 
   1536         if (triggerGlobalLayoutListener) {
   1537             attachInfo.mRecomputeGlobalAttributes = false;
   1538             attachInfo.mTreeObserver.dispatchOnGlobalLayout();
   1539 
   1540             if (AccessibilityManager.getInstance(host.mContext).isEnabled()) {
   1541                 postSendWindowContentChangedCallback();
   1542             }
   1543         }
   1544 
   1545         if (computesInternalInsets) {
   1546             // Clear the original insets.
   1547             final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
   1548             insets.reset();
   1549 
   1550             // Compute new insets in place.
   1551             attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
   1552 
   1553             // Tell the window manager.
   1554             if (insetsPending || !mLastGivenInsets.equals(insets)) {
   1555                 mLastGivenInsets.set(insets);
   1556 
   1557                 // Translate insets to screen coordinates if needed.
   1558                 final Rect contentInsets;
   1559                 final Rect visibleInsets;
   1560                 final Region touchableRegion;
   1561                 if (mTranslator != null) {
   1562                     contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
   1563                     visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
   1564                     touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion);
   1565                 } else {
   1566                     contentInsets = insets.contentInsets;
   1567                     visibleInsets = insets.visibleInsets;
   1568                     touchableRegion = insets.touchableRegion;
   1569                 }
   1570 
   1571                 try {
   1572                     sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
   1573                             contentInsets, visibleInsets, touchableRegion);
   1574                 } catch (RemoteException e) {
   1575                 }
   1576             }
   1577         }
   1578 
   1579         if (mFirst) {
   1580             // handle first focus request
   1581             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
   1582                     + mView.hasFocus());
   1583             if (mView != null) {
   1584                 if (!mView.hasFocus()) {
   1585                     mView.requestFocus(View.FOCUS_FORWARD);
   1586                     mFocusedView = mRealFocusedView = mView.findFocus();
   1587                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
   1588                             + mFocusedView);
   1589                 } else {
   1590                     mRealFocusedView = mView.findFocus();
   1591                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
   1592                             + mRealFocusedView);
   1593                 }
   1594             }
   1595         }
   1596 
   1597         mFirst = false;
   1598         mWillDrawSoon = false;
   1599         mNewSurfaceNeeded = false;
   1600         mViewVisibility = viewVisibility;
   1601 
   1602         if (mAttachInfo.mHasWindowFocus) {
   1603             final boolean imTarget = WindowManager.LayoutParams
   1604                     .mayUseInputMethod(mWindowAttributes.flags);
   1605             if (imTarget != mLastWasImTarget) {
   1606                 mLastWasImTarget = imTarget;
   1607                 InputMethodManager imm = InputMethodManager.peekInstance();
   1608                 if (imm != null && imTarget) {
   1609                     imm.startGettingWindowFocus(mView);
   1610                     imm.onWindowFocus(mView, mView.findFocus(),
   1611                             mWindowAttributes.softInputMode,
   1612                             !mHasHadWindowFocus, mWindowAttributes.flags);
   1613                 }
   1614             }
   1615         }
   1616 
   1617         boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
   1618                 viewVisibility != View.VISIBLE;
   1619 
   1620         if (!cancelDraw && !newSurface) {
   1621             if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
   1622                 for (int i = 0; i < mPendingTransitions.size(); ++i) {
   1623                     mPendingTransitions.get(i).startChangingAnimations();
   1624                 }
   1625                 mPendingTransitions.clear();
   1626             }
   1627             mFullRedrawNeeded = false;
   1628 
   1629             final long drawStartTime;
   1630             if (ViewDebug.DEBUG_LATENCY) {
   1631                 drawStartTime = System.nanoTime();
   1632             }
   1633 
   1634             draw(fullRedrawNeeded);
   1635 
   1636             if (ViewDebug.DEBUG_LATENCY) {
   1637                 mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
   1638             }
   1639 
   1640             if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0
   1641                     || mReportNextDraw) {
   1642                 if (LOCAL_LOGV) {
   1643                     Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
   1644                 }
   1645                 mReportNextDraw = false;
   1646                 if (mSurfaceHolder != null && mSurface.isValid()) {
   1647                     mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
   1648                     SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
   1649                     if (callbacks != null) {
   1650                         for (SurfaceHolder.Callback c : callbacks) {
   1651                             if (c instanceof SurfaceHolder.Callback2) {
   1652                                 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
   1653                                         mSurfaceHolder);
   1654                             }
   1655                         }
   1656                     }
   1657                 }
   1658                 try {
   1659                     sWindowSession.finishDrawing(mWindow);
   1660                 } catch (RemoteException e) {
   1661                 }
   1662             }
   1663         } else {
   1664             // End any pending transitions on this non-visible window
   1665             if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
   1666                 for (int i = 0; i < mPendingTransitions.size(); ++i) {
   1667                     mPendingTransitions.get(i).endChangingAnimations();
   1668                 }
   1669                 mPendingTransitions.clear();
   1670             }
   1671             // We were supposed to report when we are done drawing. Since we canceled the
   1672             // draw, remember it here.
   1673             if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
   1674                 mReportNextDraw = true;
   1675             }
   1676             if (fullRedrawNeeded) {
   1677                 mFullRedrawNeeded = true;
   1678             }
   1679 
   1680             if (viewVisibility == View.VISIBLE) {
   1681                 // Try again
   1682                 scheduleTraversals();
   1683             }
   1684         }
   1685     }
   1686 
   1687     public void requestTransparentRegion(View child) {
   1688         // the test below should not fail unless someone is messing with us
   1689         checkThread();
   1690         if (mView == child) {
   1691             mView.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
   1692             // Need to make sure we re-evaluate the window attributes next
   1693             // time around, to ensure the window has the correct format.
   1694             mWindowAttributesChanged = true;
   1695             mWindowAttributesChangesFlag = 0;
   1696             requestLayout();
   1697         }
   1698     }
   1699 
   1700     /**
   1701      * Figures out the measure spec for the root view in a window based on it's
   1702      * layout params.
   1703      *
   1704      * @param windowSize
   1705      *            The available width or height of the window
   1706      *
   1707      * @param rootDimension
   1708      *            The layout params for one dimension (width or height) of the
   1709      *            window.
   1710      *
   1711      * @return The measure spec to use to measure the root view.
   1712      */
   1713     private int getRootMeasureSpec(int windowSize, int rootDimension) {
   1714         int measureSpec;
   1715         switch (rootDimension) {
   1716 
   1717         case ViewGroup.LayoutParams.MATCH_PARENT:
   1718             // Window can't resize. Force root view to be windowSize.
   1719             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
   1720             break;
   1721         case ViewGroup.LayoutParams.WRAP_CONTENT:
   1722             // Window can resize. Set max size for root view.
   1723             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
   1724             break;
   1725         default:
   1726             // Window wants to be an exact size. Force root view to be that size.
   1727             measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
   1728             break;
   1729         }
   1730         return measureSpec;
   1731     }
   1732 
   1733     int mHardwareYOffset;
   1734     int mResizeAlpha;
   1735     final Paint mResizePaint = new Paint();
   1736 
   1737     public void onHardwarePreDraw(HardwareCanvas canvas) {
   1738         canvas.translate(0, -mHardwareYOffset);
   1739     }
   1740 
   1741     public void onHardwarePostDraw(HardwareCanvas canvas) {
   1742         if (mResizeBuffer != null) {
   1743             mResizePaint.setAlpha(mResizeAlpha);
   1744             canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
   1745         }
   1746     }
   1747 
   1748     /**
   1749      * @hide
   1750      */
   1751     void outputDisplayList(View view) {
   1752         if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) {
   1753             DisplayList displayList = view.getDisplayList();
   1754             if (displayList != null) {
   1755                 mAttachInfo.mHardwareCanvas.outputDisplayList(displayList);
   1756             }
   1757         }
   1758     }
   1759 
   1760     /**
   1761      * @see #PROPERTY_PROFILE_RENDERING
   1762      */
   1763     private void profileRendering(boolean enabled) {
   1764         if (mProfileRendering) {
   1765             mRenderProfilingEnabled = enabled;
   1766             if (mRenderProfiler == null) {
   1767                 mRenderProfiler = new Thread(new Runnable() {
   1768                     @Override
   1769                     public void run() {
   1770                         Log.d(TAG, "Starting profiling thread");
   1771                         while (mRenderProfilingEnabled) {
   1772                             mAttachInfo.mHandler.post(new Runnable() {
   1773                                 @Override
   1774                                 public void run() {
   1775                                     mDirty.set(0, 0, mWidth, mHeight);
   1776                                     scheduleTraversals();
   1777                                 }
   1778                             });
   1779                             try {
   1780                                 // TODO: This should use vsync when we get an API
   1781                                 Thread.sleep(15);
   1782                             } catch (InterruptedException e) {
   1783                                 Log.d(TAG, "Exiting profiling thread");
   1784                             }
   1785                         }
   1786                     }
   1787                 }, "Rendering Profiler");
   1788                 mRenderProfiler.start();
   1789             } else {
   1790                 mRenderProfiler.interrupt();
   1791                 mRenderProfiler = null;
   1792             }
   1793         }
   1794     }
   1795 
   1796     /**
   1797      * Called from draw() when DEBUG_FPS is enabled
   1798      */
   1799     private void trackFPS() {
   1800         // Tracks frames per second drawn. First value in a series of draws may be bogus
   1801         // because it down not account for the intervening idle time
   1802         long nowTime = System.currentTimeMillis();
   1803         if (mFpsStartTime < 0) {
   1804             mFpsStartTime = mFpsPrevTime = nowTime;
   1805             mFpsNumFrames = 0;
   1806         } else {
   1807             ++mFpsNumFrames;
   1808             String thisHash = Integer.toHexString(System.identityHashCode(this));
   1809             long frameTime = nowTime - mFpsPrevTime;
   1810             long totalTime = nowTime - mFpsStartTime;
   1811             Log.v(TAG, "0x" + thisHash + "\tFrame time:\t" + frameTime);
   1812             mFpsPrevTime = nowTime;
   1813             if (totalTime > 1000) {
   1814                 float fps = (float) mFpsNumFrames * 1000 / totalTime;
   1815                 Log.v(TAG, "0x" + thisHash + "\tFPS:\t" + fps);
   1816                 mFpsStartTime = nowTime;
   1817                 mFpsNumFrames = 0;
   1818             }
   1819         }
   1820     }
   1821 
   1822     private void draw(boolean fullRedrawNeeded) {
   1823         Surface surface = mSurface;
   1824         if (surface == null || !surface.isValid()) {
   1825             return;
   1826         }
   1827 
   1828         if (DEBUG_FPS) {
   1829             trackFPS();
   1830         }
   1831 
   1832         if (!sFirstDrawComplete) {
   1833             synchronized (sFirstDrawHandlers) {
   1834                 sFirstDrawComplete = true;
   1835                 final int count = sFirstDrawHandlers.size();
   1836                 for (int i = 0; i< count; i++) {
   1837                     post(sFirstDrawHandlers.get(i));
   1838                 }
   1839             }
   1840         }
   1841 
   1842         scrollToRectOrFocus(null, false);
   1843 
   1844         if (mAttachInfo.mViewScrollChanged) {
   1845             mAttachInfo.mViewScrollChanged = false;
   1846             mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
   1847         }
   1848 
   1849         int yoff;
   1850         boolean animating = mScroller != null && mScroller.computeScrollOffset();
   1851         if (animating) {
   1852             yoff = mScroller.getCurrY();
   1853         } else {
   1854             yoff = mScrollY;
   1855         }
   1856         if (mCurScrollY != yoff) {
   1857             mCurScrollY = yoff;
   1858             fullRedrawNeeded = true;
   1859         }
   1860         float appScale = mAttachInfo.mApplicationScale;
   1861         boolean scalingRequired = mAttachInfo.mScalingRequired;
   1862 
   1863         int resizeAlpha = 0;
   1864         if (mResizeBuffer != null) {
   1865             long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime;
   1866             if (deltaTime < mResizeBufferDuration) {
   1867                 float amt = deltaTime/(float) mResizeBufferDuration;
   1868                 amt = mResizeInterpolator.getInterpolation(amt);
   1869                 animating = true;
   1870                 resizeAlpha = 255 - (int)(amt*255);
   1871             } else {
   1872                 disposeResizeBuffer();
   1873             }
   1874         }
   1875 
   1876         Rect dirty = mDirty;
   1877         if (mSurfaceHolder != null) {
   1878             // The app owns the surface, we won't draw.
   1879             dirty.setEmpty();
   1880             if (animating) {
   1881                 if (mScroller != null) {
   1882                     mScroller.abortAnimation();
   1883                 }
   1884                 disposeResizeBuffer();
   1885             }
   1886             return;
   1887         }
   1888 
   1889         if (fullRedrawNeeded) {
   1890             mAttachInfo.mIgnoreDirtyState = true;
   1891             dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
   1892         }
   1893 
   1894         if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
   1895             if (!dirty.isEmpty() || mIsAnimating) {
   1896                 mIsAnimating = false;
   1897                 mHardwareYOffset = yoff;
   1898                 mResizeAlpha = resizeAlpha;
   1899 
   1900                 mCurrentDirty.set(dirty);
   1901                 mCurrentDirty.union(mPreviousDirty);
   1902                 mPreviousDirty.set(dirty);
   1903                 dirty.setEmpty();
   1904 
   1905                 Rect currentDirty = mCurrentDirty;
   1906                 if (animating) {
   1907                     currentDirty = null;
   1908                 }
   1909 
   1910                 if (mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this, currentDirty)) {
   1911                     mPreviousDirty.set(0, 0, mWidth, mHeight);
   1912                 }
   1913             }
   1914 
   1915             if (animating) {
   1916                 mFullRedrawNeeded = true;
   1917                 scheduleTraversals();
   1918             }
   1919 
   1920             return;
   1921         }
   1922 
   1923         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
   1924             Log.v(TAG, "Draw " + mView + "/"
   1925                     + mWindowAttributes.getTitle()
   1926                     + ": dirty={" + dirty.left + "," + dirty.top
   1927                     + "," + dirty.right + "," + dirty.bottom + "} surface="
   1928                     + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
   1929                     appScale + ", width=" + mWidth + ", height=" + mHeight);
   1930         }
   1931 
   1932         if (!dirty.isEmpty() || mIsAnimating) {
   1933             Canvas canvas;
   1934             try {
   1935                 int left = dirty.left;
   1936                 int top = dirty.top;
   1937                 int right = dirty.right;
   1938                 int bottom = dirty.bottom;
   1939 
   1940                 final long lockCanvasStartTime;
   1941                 if (ViewDebug.DEBUG_LATENCY) {
   1942                     lockCanvasStartTime = System.nanoTime();
   1943                 }
   1944 
   1945                 canvas = surface.lockCanvas(dirty);
   1946 
   1947                 if (ViewDebug.DEBUG_LATENCY) {
   1948                     long now = System.nanoTime();
   1949                     Log.d(TAG, "Latency: Spent "
   1950                             + ((now - lockCanvasStartTime) * 0.000001f)
   1951                             + "ms waiting for surface.lockCanvas()");
   1952                 }
   1953 
   1954                 if (left != dirty.left || top != dirty.top || right != dirty.right ||
   1955                         bottom != dirty.bottom) {
   1956                     mAttachInfo.mIgnoreDirtyState = true;
   1957                 }
   1958 
   1959                 // TODO: Do this in native
   1960                 canvas.setDensity(mDensity);
   1961             } catch (Surface.OutOfResourcesException e) {
   1962                 Log.e(TAG, "OutOfResourcesException locking surface", e);
   1963                 try {
   1964                     if (!sWindowSession.outOfMemory(mWindow)) {
   1965                         Slog.w(TAG, "No processes killed for memory; killing self");
   1966                         Process.killProcess(Process.myPid());
   1967                     }
   1968                 } catch (RemoteException ex) {
   1969                 }
   1970                 mLayoutRequested = true;    // ask wm for a new surface next time.
   1971                 return;
   1972             } catch (IllegalArgumentException e) {
   1973                 Log.e(TAG, "IllegalArgumentException locking surface", e);
   1974                 // Don't assume this is due to out of memory, it could be
   1975                 // something else, and if it is something else then we could
   1976                 // kill stuff (or ourself) for no reason.
   1977                 mLayoutRequested = true;    // ask wm for a new surface next time.
   1978                 return;
   1979             }
   1980 
   1981             try {
   1982                 if (!dirty.isEmpty() || mIsAnimating) {
   1983                     long startTime = 0L;
   1984 
   1985                     if (DEBUG_ORIENTATION || DEBUG_DRAW) {
   1986                         Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
   1987                                 + canvas.getWidth() + ", h=" + canvas.getHeight());
   1988                         //canvas.drawARGB(255, 255, 0, 0);
   1989                     }
   1990 
   1991                     if (ViewDebug.DEBUG_PROFILE_DRAWING) {
   1992                         startTime = SystemClock.elapsedRealtime();
   1993                     }
   1994 
   1995                     // If this bitmap's format includes an alpha channel, we
   1996                     // need to clear it before drawing so that the child will
   1997                     // properly re-composite its drawing on a transparent
   1998                     // background. This automatically respects the clip/dirty region
   1999                     // or
   2000                     // If we are applying an offset, we need to clear the area
   2001                     // where the offset doesn't appear to avoid having garbage
   2002                     // left in the blank areas.
   2003                     if (!canvas.isOpaque() || yoff != 0) {
   2004                         canvas.drawColor(0, PorterDuff.Mode.CLEAR);
   2005                     }
   2006 
   2007                     dirty.setEmpty();
   2008                     mIsAnimating = false;
   2009                     mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
   2010                     mView.mPrivateFlags |= View.DRAWN;
   2011 
   2012                     if (DEBUG_DRAW) {
   2013                         Context cxt = mView.getContext();
   2014                         Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
   2015                                 ", metrics=" + cxt.getResources().getDisplayMetrics() +
   2016                                 ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
   2017                     }
   2018                     try {
   2019                         canvas.translate(0, -yoff);
   2020                         if (mTranslator != null) {
   2021                             mTranslator.translateCanvas(canvas);
   2022                         }
   2023                         canvas.setScreenDensity(scalingRequired
   2024                                 ? DisplayMetrics.DENSITY_DEVICE : 0);
   2025                         mAttachInfo.mSetIgnoreDirtyState = false;
   2026                         mView.draw(canvas);
   2027                     } finally {
   2028                         if (!mAttachInfo.mSetIgnoreDirtyState) {
   2029                             // Only clear the flag if it was not set during the mView.draw() call
   2030                             mAttachInfo.mIgnoreDirtyState = false;
   2031                         }
   2032                     }
   2033 
   2034                     if (false && ViewDebug.consistencyCheckEnabled) {
   2035                         mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
   2036                     }
   2037 
   2038                     if (ViewDebug.DEBUG_PROFILE_DRAWING) {
   2039                         EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
   2040                     }
   2041                 }
   2042 
   2043             } finally {
   2044                 surface.unlockCanvasAndPost(canvas);
   2045             }
   2046         }
   2047 
   2048         if (LOCAL_LOGV) {
   2049             Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
   2050         }
   2051 
   2052         if (animating) {
   2053             mFullRedrawNeeded = true;
   2054             scheduleTraversals();
   2055         }
   2056     }
   2057 
   2058     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
   2059         final View.AttachInfo attachInfo = mAttachInfo;
   2060         final Rect ci = attachInfo.mContentInsets;
   2061         final Rect vi = attachInfo.mVisibleInsets;
   2062         int scrollY = 0;
   2063         boolean handled = false;
   2064 
   2065         if (vi.left > ci.left || vi.top > ci.top
   2066                 || vi.right > ci.right || vi.bottom > ci.bottom) {
   2067             // We'll assume that we aren't going to change the scroll
   2068             // offset, since we want to avoid that unless it is actually
   2069             // going to make the focus visible...  otherwise we scroll
   2070             // all over the place.
   2071             scrollY = mScrollY;
   2072             // We can be called for two different situations: during a draw,
   2073             // to update the scroll position if the focus has changed (in which
   2074             // case 'rectangle' is null), or in response to a
   2075             // requestChildRectangleOnScreen() call (in which case 'rectangle'
   2076             // is non-null and we just want to scroll to whatever that
   2077             // rectangle is).
   2078             View focus = mRealFocusedView;
   2079 
   2080             // When in touch mode, focus points to the previously focused view,
   2081             // which may have been removed from the view hierarchy. The following
   2082             // line checks whether the view is still in our hierarchy.
   2083             if (focus == null || focus.mAttachInfo != mAttachInfo) {
   2084                 mRealFocusedView = null;
   2085                 return false;
   2086             }
   2087 
   2088             if (focus != mLastScrolledFocus) {
   2089                 // If the focus has changed, then ignore any requests to scroll
   2090                 // to a rectangle; first we want to make sure the entire focus
   2091                 // view is visible.
   2092                 rectangle = null;
   2093             }
   2094             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
   2095                     + " rectangle=" + rectangle + " ci=" + ci
   2096                     + " vi=" + vi);
   2097             if (focus == mLastScrolledFocus && !mScrollMayChange
   2098                     && rectangle == null) {
   2099                 // Optimization: if the focus hasn't changed since last
   2100                 // time, and no layout has happened, then just leave things
   2101                 // as they are.
   2102                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
   2103                         + mScrollY + " vi=" + vi.toShortString());
   2104             } else if (focus != null) {
   2105                 // We need to determine if the currently focused view is
   2106                 // within the visible part of the window and, if not, apply
   2107                 // a pan so it can be seen.
   2108                 mLastScrolledFocus = focus;
   2109                 mScrollMayChange = false;
   2110                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
   2111                 // Try to find the rectangle from the focus view.
   2112                 if (focus.getGlobalVisibleRect(mVisRect, null)) {
   2113                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w="
   2114                             + mView.getWidth() + " h=" + mView.getHeight()
   2115                             + " ci=" + ci.toShortString()
   2116                             + " vi=" + vi.toShortString());
   2117                     if (rectangle == null) {
   2118                         focus.getFocusedRect(mTempRect);
   2119                         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
   2120                                 + ": focusRect=" + mTempRect.toShortString());
   2121                         if (mView instanceof ViewGroup) {
   2122                             ((ViewGroup) mView).offsetDescendantRectToMyCoords(
   2123                                     focus, mTempRect);
   2124                         }
   2125                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2126                                 "Focus in window: focusRect="
   2127                                 + mTempRect.toShortString()
   2128                                 + " visRect=" + mVisRect.toShortString());
   2129                     } else {
   2130                         mTempRect.set(rectangle);
   2131                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2132                                 "Request scroll to rect: "
   2133                                 + mTempRect.toShortString()
   2134                                 + " visRect=" + mVisRect.toShortString());
   2135                     }
   2136                     if (mTempRect.intersect(mVisRect)) {
   2137                         if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2138                                 "Focus window visible rect: "
   2139                                 + mTempRect.toShortString());
   2140                         if (mTempRect.height() >
   2141                                 (mView.getHeight()-vi.top-vi.bottom)) {
   2142                             // If the focus simply is not going to fit, then
   2143                             // best is probably just to leave things as-is.
   2144                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2145                                     "Too tall; leaving scrollY=" + scrollY);
   2146                         } else if ((mTempRect.top-scrollY) < vi.top) {
   2147                             scrollY -= vi.top - (mTempRect.top-scrollY);
   2148                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2149                                     "Top covered; scrollY=" + scrollY);
   2150                         } else if ((mTempRect.bottom-scrollY)
   2151                                 > (mView.getHeight()-vi.bottom)) {
   2152                             scrollY += (mTempRect.bottom-scrollY)
   2153                                     - (mView.getHeight()-vi.bottom);
   2154                             if (DEBUG_INPUT_RESIZE) Log.v(TAG,
   2155                                     "Bottom covered; scrollY=" + scrollY);
   2156                         }
   2157                         handled = true;
   2158                     }
   2159                 }
   2160             }
   2161         }
   2162 
   2163         if (scrollY != mScrollY) {
   2164             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
   2165                     + mScrollY + " , new=" + scrollY);
   2166             if (!immediate && mResizeBuffer == null) {
   2167                 if (mScroller == null) {
   2168                     mScroller = new Scroller(mView.getContext());
   2169                 }
   2170                 mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);
   2171             } else if (mScroller != null) {
   2172                 mScroller.abortAnimation();
   2173             }
   2174             mScrollY = scrollY;
   2175         }
   2176 
   2177         return handled;
   2178     }
   2179 
   2180     public void requestChildFocus(View child, View focused) {
   2181         checkThread();
   2182         if (mFocusedView != focused) {
   2183             mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
   2184             scheduleTraversals();
   2185         }
   2186         mFocusedView = mRealFocusedView = focused;
   2187         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
   2188                 + mFocusedView);
   2189     }
   2190 
   2191     public void clearChildFocus(View child) {
   2192         checkThread();
   2193 
   2194         View oldFocus = mFocusedView;
   2195 
   2196         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus");
   2197         mFocusedView = mRealFocusedView = null;
   2198         if (mView != null && !mView.hasFocus()) {
   2199             // If a view gets the focus, the listener will be invoked from requestChildFocus()
   2200             if (!mView.requestFocus(View.FOCUS_FORWARD)) {
   2201                 mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
   2202             }
   2203         } else if (oldFocus != null) {
   2204             mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
   2205         }
   2206     }
   2207 
   2208 
   2209     public void focusableViewAvailable(View v) {
   2210         checkThread();
   2211 
   2212         if (mView != null) {
   2213             if (!mView.hasFocus()) {
   2214                 v.requestFocus();
   2215             } else {
   2216                 // the one case where will transfer focus away from the current one
   2217                 // is if the current view is a view group that prefers to give focus
   2218                 // to its children first AND the view is a descendant of it.
   2219                 mFocusedView = mView.findFocus();
   2220                 boolean descendantsHaveDibsOnFocus =
   2221                         (mFocusedView instanceof ViewGroup) &&
   2222                             (((ViewGroup) mFocusedView).getDescendantFocusability() ==
   2223                                     ViewGroup.FOCUS_AFTER_DESCENDANTS);
   2224                 if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
   2225                     // If a view gets the focus, the listener will be invoked from requestChildFocus()
   2226                     v.requestFocus();
   2227                 }
   2228             }
   2229         }
   2230     }
   2231 
   2232     public void recomputeViewAttributes(View child) {
   2233         checkThread();
   2234         if (mView == child) {
   2235             mAttachInfo.mRecomputeGlobalAttributes = true;
   2236             if (!mWillDrawSoon) {
   2237                 scheduleTraversals();
   2238             }
   2239         }
   2240     }
   2241 
   2242     void dispatchDetachedFromWindow() {
   2243         if (mView != null && mView.mAttachInfo != null) {
   2244             if (mAttachInfo.mHardwareRenderer != null &&
   2245                     mAttachInfo.mHardwareRenderer.isEnabled()) {
   2246                 mAttachInfo.mHardwareRenderer.validate();
   2247             }
   2248             mView.dispatchDetachedFromWindow();
   2249         }
   2250 
   2251         mAccessibilityInteractionConnectionManager.ensureNoConnection();
   2252         mAccessibilityManager.removeAccessibilityStateChangeListener(
   2253                 mAccessibilityInteractionConnectionManager);
   2254         removeSendWindowContentChangedCallback();
   2255 
   2256         mView = null;
   2257         mAttachInfo.mRootView = null;
   2258         mAttachInfo.mSurface = null;
   2259 
   2260         destroyHardwareRenderer();
   2261 
   2262         mSurface.release();
   2263 
   2264         if (mInputQueueCallback != null && mInputQueue != null) {
   2265             mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
   2266             mInputQueueCallback = null;
   2267             mInputQueue = null;
   2268         } else if (mInputChannel != null) {
   2269             InputQueue.unregisterInputChannel(mInputChannel);
   2270         }
   2271         try {
   2272             sWindowSession.remove(mWindow);
   2273         } catch (RemoteException e) {
   2274         }
   2275 
   2276         // Dispose the input channel after removing the window so the Window Manager
   2277         // doesn't interpret the input channel being closed as an abnormal termination.
   2278         if (mInputChannel != null) {
   2279             mInputChannel.dispose();
   2280             mInputChannel = null;
   2281         }
   2282     }
   2283 
   2284     void updateConfiguration(Configuration config, boolean force) {
   2285         if (DEBUG_CONFIGURATION) Log.v(TAG,
   2286                 "Applying new config to window "
   2287                 + mWindowAttributes.getTitle()
   2288                 + ": " + config);
   2289 
   2290         CompatibilityInfo ci = mCompatibilityInfo.getIfNeeded();
   2291         if (ci != null) {
   2292             config = new Configuration(config);
   2293             ci.applyToConfiguration(config);
   2294         }
   2295 
   2296         synchronized (sConfigCallbacks) {
   2297             for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
   2298                 sConfigCallbacks.get(i).onConfigurationChanged(config);
   2299             }
   2300         }
   2301         if (mView != null) {
   2302             // At this point the resources have been updated to
   2303             // have the most recent config, whatever that is.  Use
   2304             // the on in them which may be newer.
   2305             config = mView.getResources().getConfiguration();
   2306             if (force || mLastConfiguration.diff(config) != 0) {
   2307                 mLastConfiguration.setTo(config);
   2308                 mView.dispatchConfigurationChanged(config);
   2309             }
   2310         }
   2311     }
   2312 
   2313     /**
   2314      * Return true if child is an ancestor of parent, (or equal to the parent).
   2315      */
   2316     private static boolean isViewDescendantOf(View child, View parent) {
   2317         if (child == parent) {
   2318             return true;
   2319         }
   2320 
   2321         final ViewParent theParent = child.getParent();
   2322         return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
   2323     }
   2324 
   2325     private static void forceLayout(View view) {
   2326         view.forceLayout();
   2327         if (view instanceof ViewGroup) {
   2328             ViewGroup group = (ViewGroup) view;
   2329             final int count = group.getChildCount();
   2330             for (int i = 0; i < count; i++) {
   2331                 forceLayout(group.getChildAt(i));
   2332             }
   2333         }
   2334     }
   2335 
   2336     public final static int DO_TRAVERSAL = 1000;
   2337     public final static int DIE = 1001;
   2338     public final static int RESIZED = 1002;
   2339     public final static int RESIZED_REPORT = 1003;
   2340     public final static int WINDOW_FOCUS_CHANGED = 1004;
   2341     public final static int DISPATCH_KEY = 1005;
   2342     public final static int DISPATCH_POINTER = 1006;
   2343     public final static int DISPATCH_TRACKBALL = 1007;
   2344     public final static int DISPATCH_APP_VISIBILITY = 1008;
   2345     public final static int DISPATCH_GET_NEW_SURFACE = 1009;
   2346     public final static int FINISHED_EVENT = 1010;
   2347     public final static int DISPATCH_KEY_FROM_IME = 1011;
   2348     public final static int FINISH_INPUT_CONNECTION = 1012;
   2349     public final static int CHECK_FOCUS = 1013;
   2350     public final static int CLOSE_SYSTEM_DIALOGS = 1014;
   2351     public final static int DISPATCH_DRAG_EVENT = 1015;
   2352     public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
   2353     public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017;
   2354     public final static int DISPATCH_GENERIC_MOTION = 1018;
   2355     public final static int UPDATE_CONFIGURATION = 1019;
   2356     public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1020;
   2357     public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021;
   2358     public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022;
   2359     public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023;
   2360     public final static int PROCESS_INPUT_EVENTS = 1024;
   2361 
   2362     @Override
   2363     public String getMessageName(Message message) {
   2364         switch (message.what) {
   2365             case DO_TRAVERSAL:
   2366                 return "DO_TRAVERSAL";
   2367             case DIE:
   2368                 return "DIE";
   2369             case RESIZED:
   2370                 return "RESIZED";
   2371             case RESIZED_REPORT:
   2372                 return "RESIZED_REPORT";
   2373             case WINDOW_FOCUS_CHANGED:
   2374                 return "WINDOW_FOCUS_CHANGED";
   2375             case DISPATCH_KEY:
   2376                 return "DISPATCH_KEY";
   2377             case DISPATCH_POINTER:
   2378                 return "DISPATCH_POINTER";
   2379             case DISPATCH_TRACKBALL:
   2380                 return "DISPATCH_TRACKBALL";
   2381             case DISPATCH_APP_VISIBILITY:
   2382                 return "DISPATCH_APP_VISIBILITY";
   2383             case DISPATCH_GET_NEW_SURFACE:
   2384                 return "DISPATCH_GET_NEW_SURFACE";
   2385             case FINISHED_EVENT:
   2386                 return "FINISHED_EVENT";
   2387             case DISPATCH_KEY_FROM_IME:
   2388                 return "DISPATCH_KEY_FROM_IME";
   2389             case FINISH_INPUT_CONNECTION:
   2390                 return "FINISH_INPUT_CONNECTION";
   2391             case CHECK_FOCUS:
   2392                 return "CHECK_FOCUS";
   2393             case CLOSE_SYSTEM_DIALOGS:
   2394                 return "CLOSE_SYSTEM_DIALOGS";
   2395             case DISPATCH_DRAG_EVENT:
   2396                 return "DISPATCH_DRAG_EVENT";
   2397             case DISPATCH_DRAG_LOCATION_EVENT:
   2398                 return "DISPATCH_DRAG_LOCATION_EVENT";
   2399             case DISPATCH_SYSTEM_UI_VISIBILITY:
   2400                 return "DISPATCH_SYSTEM_UI_VISIBILITY";
   2401             case DISPATCH_GENERIC_MOTION:
   2402                 return "DISPATCH_GENERIC_MOTION";
   2403             case UPDATE_CONFIGURATION:
   2404                 return "UPDATE_CONFIGURATION";
   2405             case DO_PERFORM_ACCESSIBILITY_ACTION:
   2406                 return "DO_PERFORM_ACCESSIBILITY_ACTION";
   2407             case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
   2408                 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
   2409             case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
   2410                 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
   2411             case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT:
   2412                 return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT";
   2413             case PROCESS_INPUT_EVENTS:
   2414                 return "PROCESS_INPUT_EVENTS";
   2415 
   2416         }
   2417         return super.getMessageName(message);
   2418     }
   2419 
   2420     @Override
   2421     public void handleMessage(Message msg) {
   2422         switch (msg.what) {
   2423         case View.AttachInfo.INVALIDATE_MSG:
   2424             ((View) msg.obj).invalidate();
   2425             break;
   2426         case View.AttachInfo.INVALIDATE_RECT_MSG:
   2427             final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
   2428             info.target.invalidate(info.left, info.top, info.right, info.bottom);
   2429             info.release();
   2430             break;
   2431         case DO_TRAVERSAL:
   2432             if (mProfile) {
   2433                 Debug.startMethodTracing("ViewAncestor");
   2434             }
   2435 
   2436             final long traversalStartTime;
   2437             if (ViewDebug.DEBUG_LATENCY) {
   2438                 traversalStartTime = System.nanoTime();
   2439                 mLastDrawDurationNanos = 0;
   2440             }
   2441 
   2442             performTraversals();
   2443 
   2444             if (ViewDebug.DEBUG_LATENCY) {
   2445                 long now = System.nanoTime();
   2446                 Log.d(TAG, "Latency: Spent "
   2447                         + ((now - traversalStartTime) * 0.000001f)
   2448                         + "ms in performTraversals(), with "
   2449                         + (mLastDrawDurationNanos * 0.000001f)
   2450                         + "ms of that time in draw()");
   2451                 mLastTraversalFinishedTimeNanos = now;
   2452             }
   2453 
   2454             if (mProfile) {
   2455                 Debug.stopMethodTracing();
   2456                 mProfile = false;
   2457             }
   2458             break;
   2459         case FINISHED_EVENT:
   2460             handleFinishedEvent(msg.arg1, msg.arg2 != 0);
   2461             break;
   2462         case DISPATCH_KEY:
   2463             deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
   2464             break;
   2465         case DISPATCH_POINTER:
   2466             deliverPointerEvent((MotionEvent) msg.obj, msg.arg1 != 0);
   2467             break;
   2468         case DISPATCH_TRACKBALL:
   2469             deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0);
   2470             break;
   2471         case DISPATCH_GENERIC_MOTION:
   2472             deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0);
   2473             break;
   2474         case PROCESS_INPUT_EVENTS:
   2475             processInputEvents(false);
   2476             break;
   2477         case DISPATCH_APP_VISIBILITY:
   2478             handleAppVisibility(msg.arg1 != 0);
   2479             break;
   2480         case DISPATCH_GET_NEW_SURFACE:
   2481             handleGetNewSurface();
   2482             break;
   2483         case RESIZED:
   2484             ResizedInfo ri = (ResizedInfo)msg.obj;
   2485 
   2486             if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
   2487                     && mPendingContentInsets.equals(ri.coveredInsets)
   2488                     && mPendingVisibleInsets.equals(ri.visibleInsets)
   2489                     && ((ResizedInfo)msg.obj).newConfig == null) {
   2490                 break;
   2491             }
   2492             // fall through...
   2493         case RESIZED_REPORT:
   2494             if (mAdded) {
   2495                 Configuration config = ((ResizedInfo)msg.obj).newConfig;
   2496                 if (config != null) {
   2497                     updateConfiguration(config, false);
   2498                 }
   2499                 mWinFrame.left = 0;
   2500                 mWinFrame.right = msg.arg1;
   2501                 mWinFrame.top = 0;
   2502                 mWinFrame.bottom = msg.arg2;
   2503                 mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets);
   2504                 mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets);
   2505                 if (msg.what == RESIZED_REPORT) {
   2506                     mReportNextDraw = true;
   2507                 }
   2508 
   2509                 if (mView != null) {
   2510                     forceLayout(mView);
   2511                 }
   2512                 requestLayout();
   2513             }
   2514             break;
   2515         case WINDOW_FOCUS_CHANGED: {
   2516             if (mAdded) {
   2517                 boolean hasWindowFocus = msg.arg1 != 0;
   2518                 mAttachInfo.mHasWindowFocus = hasWindowFocus;
   2519 
   2520                 profileRendering(hasWindowFocus);
   2521 
   2522                 if (hasWindowFocus) {
   2523                     boolean inTouchMode = msg.arg2 != 0;
   2524                     ensureTouchModeLocally(inTouchMode);
   2525 
   2526                     if (mAttachInfo.mHardwareRenderer != null &&
   2527                             mSurface != null && mSurface.isValid()) {
   2528                         mFullRedrawNeeded = true;
   2529                         try {
   2530                             mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
   2531                                     mAttachInfo, mHolder);
   2532                         } catch (Surface.OutOfResourcesException e) {
   2533                             Log.e(TAG, "OutOfResourcesException locking surface", e);
   2534                             try {
   2535                                 if (!sWindowSession.outOfMemory(mWindow)) {
   2536                                     Slog.w(TAG, "No processes killed for memory; killing self");
   2537                                     Process.killProcess(Process.myPid());
   2538                                 }
   2539                             } catch (RemoteException ex) {
   2540                             }
   2541                             // Retry in a bit.
   2542                             sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500);
   2543                             return;
   2544                         }
   2545                     }
   2546                 }
   2547 
   2548                 mLastWasImTarget = WindowManager.LayoutParams
   2549                         .mayUseInputMethod(mWindowAttributes.flags);
   2550 
   2551                 InputMethodManager imm = InputMethodManager.peekInstance();
   2552                 if (mView != null) {
   2553                     if (hasWindowFocus && imm != null && mLastWasImTarget) {
   2554                         imm.startGettingWindowFocus(mView);
   2555                     }
   2556                     mAttachInfo.mKeyDispatchState.reset();
   2557                     mView.dispatchWindowFocusChanged(hasWindowFocus);
   2558                 }
   2559 
   2560                 // Note: must be done after the focus change callbacks,
   2561                 // so all of the view state is set up correctly.
   2562                 if (hasWindowFocus) {
   2563                     if (imm != null && mLastWasImTarget) {
   2564                         imm.onWindowFocus(mView, mView.findFocus(),
   2565                                 mWindowAttributes.softInputMode,
   2566                                 !mHasHadWindowFocus, mWindowAttributes.flags);
   2567                     }
   2568                     // Clear the forward bit.  We can just do this directly, since
   2569                     // the window manager doesn't care about it.
   2570                     mWindowAttributes.softInputMode &=
   2571                             ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
   2572                     ((WindowManager.LayoutParams)mView.getLayoutParams())
   2573                             .softInputMode &=
   2574                                 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
   2575                     mHasHadWindowFocus = true;
   2576                 }
   2577 
   2578                 if (hasWindowFocus && mView != null) {
   2579                     sendAccessibilityEvents();
   2580                 }
   2581             }
   2582         } break;
   2583         case DIE:
   2584             doDie();
   2585             break;
   2586         case DISPATCH_KEY_FROM_IME: {
   2587             if (LOCAL_LOGV) Log.v(
   2588                 TAG, "Dispatching key "
   2589                 + msg.obj + " from IME to " + mView);
   2590             KeyEvent event = (KeyEvent)msg.obj;
   2591             if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
   2592                 // The IME is trying to say this event is from the
   2593                 // system!  Bad bad bad!
   2594                 //noinspection UnusedAssignment
   2595                 event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
   2596             }
   2597             deliverKeyEventPostIme((KeyEvent)msg.obj, false);
   2598         } break;
   2599         case FINISH_INPUT_CONNECTION: {
   2600             InputMethodManager imm = InputMethodManager.peekInstance();
   2601             if (imm != null) {
   2602                 imm.reportFinishInputConnection((InputConnection)msg.obj);
   2603             }
   2604         } break;
   2605         case CHECK_FOCUS: {
   2606             InputMethodManager imm = InputMethodManager.peekInstance();
   2607             if (imm != null) {
   2608                 imm.checkFocus();
   2609             }
   2610         } break;
   2611         case CLOSE_SYSTEM_DIALOGS: {
   2612             if (mView != null) {
   2613                 mView.onCloseSystemDialogs((String)msg.obj);
   2614             }
   2615         } break;
   2616         case DISPATCH_DRAG_EVENT:
   2617         case DISPATCH_DRAG_LOCATION_EVENT: {
   2618             DragEvent event = (DragEvent)msg.obj;
   2619             event.mLocalState = mLocalDragState;    // only present when this app called startDrag()
   2620             handleDragEvent(event);
   2621         } break;
   2622         case DISPATCH_SYSTEM_UI_VISIBILITY: {
   2623             handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo)msg.obj);
   2624         } break;
   2625         case UPDATE_CONFIGURATION: {
   2626             Configuration config = (Configuration)msg.obj;
   2627             if (config.isOtherSeqNewer(mLastConfiguration)) {
   2628                 config = mLastConfiguration;
   2629             }
   2630             updateConfiguration(config, false);
   2631         } break;
   2632         case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
   2633             if (mView != null) {
   2634                 getAccessibilityInteractionController()
   2635                     .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
   2636             }
   2637         } break;
   2638         case DO_PERFORM_ACCESSIBILITY_ACTION: {
   2639             if (mView != null) {
   2640                 getAccessibilityInteractionController()
   2641                     .perfromAccessibilityActionUiThread(msg);
   2642             }
   2643         } break;
   2644         case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
   2645             if (mView != null) {
   2646                 getAccessibilityInteractionController()
   2647                     .findAccessibilityNodeInfoByViewIdUiThread(msg);
   2648             }
   2649         } break;
   2650         case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: {
   2651             if (mView != null) {
   2652                 getAccessibilityInteractionController()
   2653                     .findAccessibilityNodeInfosByViewTextUiThread(msg);
   2654             }
   2655         } break;
   2656         }
   2657     }
   2658 
   2659     private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
   2660         if (mFinishedCallback != null) {
   2661             Slog.w(TAG, "Received a new input event from the input queue but there is "
   2662                     + "already an unfinished input event in progress.");
   2663         }
   2664 
   2665         if (ViewDebug.DEBUG_LATENCY) {
   2666             mInputEventReceiveTimeNanos = System.nanoTime();
   2667             mInputEventDeliverTimeNanos = 0;
   2668             mInputEventDeliverPostImeTimeNanos = 0;
   2669         }
   2670 
   2671         mFinishedCallback = finishedCallback;
   2672     }
   2673 
   2674     private void finishInputEvent(InputEvent event, boolean handled) {
   2675         if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
   2676 
   2677         if (mFinishedCallback == null) {
   2678             Slog.w(TAG, "Attempted to tell the input queue that the current input event "
   2679                     + "is finished but there is no input event actually in progress.");
   2680             return;
   2681         }
   2682 
   2683         if (ViewDebug.DEBUG_LATENCY) {
   2684             final long now = System.nanoTime();
   2685             final long eventTime = event.getEventTimeNano();
   2686             final StringBuilder msg = new StringBuilder();
   2687             msg.append("Latency: Spent ");
   2688             msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f);
   2689             msg.append("ms processing ");
   2690             if (event instanceof KeyEvent) {
   2691                 final KeyEvent  keyEvent = (KeyEvent)event;
   2692                 msg.append("key event, action=");
   2693                 msg.append(KeyEvent.actionToString(keyEvent.getAction()));
   2694             } else {
   2695                 final MotionEvent motionEvent = (MotionEvent)event;
   2696                 msg.append("motion event, action=");
   2697                 msg.append(MotionEvent.actionToString(motionEvent.getAction()));
   2698                 msg.append(", historySize=");
   2699                 msg.append(motionEvent.getHistorySize());
   2700             }
   2701             msg.append(", handled=");
   2702             msg.append(handled);
   2703             msg.append(", received at +");
   2704             msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f);
   2705             if (mInputEventDeliverTimeNanos != 0) {
   2706                 msg.append("ms, delivered at +");
   2707                 msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f);
   2708             }
   2709             if (mInputEventDeliverPostImeTimeNanos != 0) {
   2710                 msg.append("ms, delivered post IME at +");
   2711                 msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f);
   2712             }
   2713             msg.append("ms, finished at +");
   2714             msg.append((now - eventTime) * 0.000001f);
   2715             msg.append("ms.");
   2716             Log.d(TAG, msg.toString());
   2717         }
   2718 
   2719         mFinishedCallback.finished(handled);
   2720         mFinishedCallback = null;
   2721     }
   2722 
   2723     /**
   2724      * Something in the current window tells us we need to change the touch mode.  For
   2725      * example, we are not in touch mode, and the user touches the screen.
   2726      *
   2727      * If the touch mode has changed, tell the window manager, and handle it locally.
   2728      *
   2729      * @param inTouchMode Whether we want to be in touch mode.
   2730      * @return True if the touch mode changed and focus changed was changed as a result
   2731      */
   2732     boolean ensureTouchMode(boolean inTouchMode) {
   2733         if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
   2734                 + "touch mode is " + mAttachInfo.mInTouchMode);
   2735         if (mAttachInfo.mInTouchMode == inTouchMode) return false;
   2736 
   2737         // tell the window manager
   2738         try {
   2739             sWindowSession.setInTouchMode(inTouchMode);
   2740         } catch (RemoteException e) {
   2741             throw new RuntimeException(e);
   2742         }
   2743 
   2744         // handle the change
   2745         return ensureTouchModeLocally(inTouchMode);
   2746     }
   2747 
   2748     /**
   2749      * Ensure that the touch mode for this window is set, and if it is changing,
   2750      * take the appropriate action.
   2751      * @param inTouchMode Whether we want to be in touch mode.
   2752      * @return True if the touch mode changed and focus changed was changed as a result
   2753      */
   2754     private boolean ensureTouchModeLocally(boolean inTouchMode) {
   2755         if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current "
   2756                 + "touch mode is " + mAttachInfo.mInTouchMode);
   2757 
   2758         if (mAttachInfo.mInTouchMode == inTouchMode) return false;
   2759 
   2760         mAttachInfo.mInTouchMode = inTouchMode;
   2761         mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);
   2762 
   2763         return (inTouchMode) ? enterTouchMode() : leaveTouchMode();
   2764     }
   2765 
   2766     private boolean enterTouchMode() {
   2767         if (mView != null) {
   2768             if (mView.hasFocus()) {
   2769                 // note: not relying on mFocusedView here because this could
   2770                 // be when the window is first being added, and mFocused isn't
   2771                 // set yet.
   2772                 final View focused = mView.findFocus();
   2773                 if (focused != null && !focused.isFocusableInTouchMode()) {
   2774 
   2775                     final ViewGroup ancestorToTakeFocus =
   2776                             findAncestorToTakeFocusInTouchMode(focused);
   2777                     if (ancestorToTakeFocus != null) {
   2778                         // there is an ancestor that wants focus after its descendants that
   2779                         // is focusable in touch mode.. give it focus
   2780                         return ancestorToTakeFocus.requestFocus();
   2781                     } else {
   2782                         // nothing appropriate to have focus in touch mode, clear it out
   2783                         mView.unFocus();
   2784                         mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
   2785                         mFocusedView = null;
   2786                         return true;
   2787                     }
   2788                 }
   2789             }
   2790         }
   2791         return false;
   2792     }
   2793 
   2794 
   2795     /**
   2796      * Find an ancestor of focused that wants focus after its descendants and is
   2797      * focusable in touch mode.
   2798      * @param focused The currently focused view.
   2799      * @return An appropriate view, or null if no such view exists.
   2800      */
   2801     private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
   2802         ViewParent parent = focused.getParent();
   2803         while (parent instanceof ViewGroup) {
   2804             final ViewGroup vgParent = (ViewGroup) parent;
   2805             if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
   2806                     && vgParent.isFocusableInTouchMode()) {
   2807                 return vgParent;
   2808             }
   2809             if (vgParent.isRootNamespace()) {
   2810                 return null;
   2811             } else {
   2812                 parent = vgParent.getParent();
   2813             }
   2814         }
   2815         return null;
   2816     }
   2817 
   2818     private boolean leaveTouchMode() {
   2819         if (mView != null) {
   2820             if (mView.hasFocus()) {
   2821                 // i learned the hard way to not trust mFocusedView :)
   2822                 mFocusedView = mView.findFocus();
   2823                 if (!(mFocusedView instanceof ViewGroup)) {
   2824                     // some view has focus, let it keep it
   2825                     return false;
   2826                 } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
   2827                         ViewGroup.FOCUS_AFTER_DESCENDANTS) {
   2828                     // some view group has focus, and doesn't prefer its children
   2829                     // over itself for focus, so let them keep it.
   2830                     return false;
   2831                 }
   2832             }
   2833 
   2834             // find the best view to give focus to in this brave new non-touch-mode
   2835             // world
   2836             final View focused = focusSearch(null, View.FOCUS_DOWN);
   2837             if (focused != null) {
   2838                 return focused.requestFocus(View.FOCUS_DOWN);
   2839             }
   2840         }
   2841         return false;
   2842     }
   2843 
   2844     private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
   2845         if (ViewDebug.DEBUG_LATENCY) {
   2846             mInputEventDeliverTimeNanos = System.nanoTime();
   2847         }
   2848 
   2849         final boolean isTouchEvent = event.isTouchEvent();
   2850         if (mInputEventConsistencyVerifier != null) {
   2851             if (isTouchEvent) {
   2852                 mInputEventConsistencyVerifier.onTouchEvent(event, 0);
   2853             } else {
   2854                 mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
   2855             }
   2856         }
   2857 
   2858         // If there is no view, then the event will not be handled.
   2859         if (mView == null || !mAdded) {
   2860             finishMotionEvent(event, sendDone, false);
   2861             return;
   2862         }
   2863 
   2864         // Translate the pointer event for compatibility, if needed.
   2865         if (mTranslator != null) {
   2866             mTranslator.translateEventInScreenToAppWindow(event);
   2867         }
   2868 
   2869         // Enter touch mode on down or scroll.
   2870         final int action = event.getAction();
   2871         if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
   2872             ensureTouchMode(true);
   2873         }
   2874 
   2875         // Offset the scroll position.
   2876         if (mCurScrollY != 0) {
   2877             event.offsetLocation(0, mCurScrollY);
   2878         }
   2879         if (MEASURE_LATENCY) {
   2880             lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());
   2881         }
   2882 
   2883         // Remember the touch position for possible drag-initiation.
   2884         if (isTouchEvent) {
   2885             mLastTouchPoint.x = event.getRawX();
   2886             mLastTouchPoint.y = event.getRawY();
   2887         }
   2888 
   2889         // Dispatch touch to view hierarchy.
   2890         boolean handled = mView.dispatchPointerEvent(event);
   2891         if (MEASURE_LATENCY) {
   2892             lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
   2893         }
   2894         if (handled) {
   2895             finishMotionEvent(event, sendDone, true);
   2896             return;
   2897         }
   2898 
   2899         // Pointer event was unhandled.
   2900         finishMotionEvent(event, sendDone, false);
   2901     }
   2902 
   2903     private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
   2904         event.recycle();
   2905         if (sendDone) {
   2906             finishInputEvent(event, handled);
   2907         }
   2908         //noinspection ConstantConditions
   2909         if (LOCAL_LOGV || WATCH_POINTER) {
   2910             if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
   2911                 Log.i(TAG, "Done dispatching!");
   2912             }
   2913         }
   2914     }
   2915 
   2916     private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
   2917         if (ViewDebug.DEBUG_LATENCY) {
   2918             mInputEventDeliverTimeNanos = System.nanoTime();
   2919         }
   2920 
   2921         if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
   2922 
   2923         if (mInputEventConsistencyVerifier != null) {
   2924             mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
   2925         }
   2926 
   2927         // If there is no view, then the event will not be handled.
   2928         if (mView == null || !mAdded) {
   2929             finishMotionEvent(event, sendDone, false);
   2930             return;
   2931         }
   2932 
   2933         // Deliver the trackball event to the view.
   2934         if (mView.dispatchTrackballEvent(event)) {
   2935             // If we reach this, we delivered a trackball event to mView and
   2936             // mView consumed it. Because we will not translate the trackball
   2937             // event into a key event, touch mode will not exit, so we exit
   2938             // touch mode here.
   2939             ensureTouchMode(false);
   2940 
   2941             finishMotionEvent(event, sendDone, true);
   2942             mLastTrackballTime = Integer.MIN_VALUE;
   2943             return;
   2944         }
   2945 
   2946         // Translate the trackball event into DPAD keys and try to deliver those.
   2947         final TrackballAxis x = mTrackballAxisX;
   2948         final TrackballAxis y = mTrackballAxisY;
   2949 
   2950         long curTime = SystemClock.uptimeMillis();
   2951         if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
   2952             // It has been too long since the last movement,
   2953             // so restart at the beginning.
   2954             x.reset(0);
   2955             y.reset(0);
   2956             mLastTrackballTime = curTime;
   2957         }
   2958 
   2959         final int action = event.getAction();
   2960         final int metaState = event.getMetaState();
   2961         switch (action) {
   2962             case MotionEvent.ACTION_DOWN:
   2963                 x.reset(2);
   2964                 y.reset(2);
   2965                 deliverKeyEvent(new KeyEvent(curTime, curTime,
   2966                         KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
   2967                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   2968                         InputDevice.SOURCE_KEYBOARD), false);
   2969                 break;
   2970             case MotionEvent.ACTION_UP:
   2971                 x.reset(2);
   2972                 y.reset(2);
   2973                 deliverKeyEvent(new KeyEvent(curTime, curTime,
   2974                         KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
   2975                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   2976                         InputDevice.SOURCE_KEYBOARD), false);
   2977                 break;
   2978         }
   2979 
   2980         if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
   2981                 + x.step + " dir=" + x.dir + " acc=" + x.acceleration
   2982                 + " move=" + event.getX()
   2983                 + " / Y=" + y.position + " step="
   2984                 + y.step + " dir=" + y.dir + " acc=" + y.acceleration
   2985                 + " move=" + event.getY());
   2986         final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
   2987         final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
   2988 
   2989         // Generate DPAD events based on the trackball movement.
   2990         // We pick the axis that has moved the most as the direction of
   2991         // the DPAD.  When we generate DPAD events for one axis, then the
   2992         // other axis is reset -- we don't want to perform DPAD jumps due
   2993         // to slight movements in the trackball when making major movements
   2994         // along the other axis.
   2995         int keycode = 0;
   2996         int movement = 0;
   2997         float accel = 1;
   2998         if (xOff > yOff) {
   2999             movement = x.generate((2/event.getXPrecision()));
   3000             if (movement != 0) {
   3001                 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
   3002                         : KeyEvent.KEYCODE_DPAD_LEFT;
   3003                 accel = x.acceleration;
   3004                 y.reset(2);
   3005             }
   3006         } else if (yOff > 0) {
   3007             movement = y.generate((2/event.getYPrecision()));
   3008             if (movement != 0) {
   3009                 keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
   3010                         : KeyEvent.KEYCODE_DPAD_UP;
   3011                 accel = y.acceleration;
   3012                 x.reset(2);
   3013             }
   3014         }
   3015 
   3016         if (keycode != 0) {
   3017             if (movement < 0) movement = -movement;
   3018             int accelMovement = (int)(movement * accel);
   3019             if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
   3020                     + " accelMovement=" + accelMovement
   3021                     + " accel=" + accel);
   3022             if (accelMovement > movement) {
   3023                 if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
   3024                         + keycode);
   3025                 movement--;
   3026                 int repeatCount = accelMovement - movement;
   3027                 deliverKeyEvent(new KeyEvent(curTime, curTime,
   3028                         KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
   3029                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   3030                         InputDevice.SOURCE_KEYBOARD), false);
   3031             }
   3032             while (movement > 0) {
   3033                 if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
   3034                         + keycode);
   3035                 movement--;
   3036                 curTime = SystemClock.uptimeMillis();
   3037                 deliverKeyEvent(new KeyEvent(curTime, curTime,
   3038                         KeyEvent.ACTION_DOWN, keycode, 0, metaState,
   3039                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   3040                         InputDevice.SOURCE_KEYBOARD), false);
   3041                 deliverKeyEvent(new KeyEvent(curTime, curTime,
   3042                         KeyEvent.ACTION_UP, keycode, 0, metaState,
   3043                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
   3044                         InputDevice.SOURCE_KEYBOARD), false);
   3045                 }
   3046             mLastTrackballTime = curTime;
   3047         }
   3048 
   3049         // Unfortunately we can't tell whether the application consumed the keys, so
   3050         // we always consider the trackball event handled.
   3051         finishMotionEvent(event, sendDone, true);
   3052     }
   3053 
   3054     private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
   3055         if (ViewDebug.DEBUG_LATENCY) {
   3056             mInputEventDeliverTimeNanos = System.nanoTime();
   3057         }
   3058 
   3059         if (mInputEventConsistencyVerifier != null) {
   3060             mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
   3061         }
   3062 
   3063         final int source = event.getSource();
   3064         final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
   3065 
   3066         // If there is no view, then the event will not be handled.
   3067         if (mView == null || !mAdded) {
   3068             if (isJoystick) {
   3069                 updateJoystickDirection(event, false);
   3070             }
   3071             finishMotionEvent(event, sendDone, false);
   3072             return;
   3073         }
   3074 
   3075         // Deliver the event to the view.
   3076         if (mView.dispatchGenericMotionEvent(event)) {
   3077             if (isJoystick) {
   3078                 updateJoystickDirection(event, false);
   3079             }
   3080             finishMotionEvent(event, sendDone, true);
   3081             return;
   3082         }
   3083 
   3084         if (isJoystick) {
   3085             // Translate the joystick event into DPAD keys and try to deliver those.
   3086             updateJoystickDirection(event, true);
   3087             finishMotionEvent(event, sendDone, true);
   3088         } else {
   3089             finishMotionEvent(event, sendDone, false);
   3090         }
   3091     }
   3092 
   3093     private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
   3094         final long time = event.getEventTime();
   3095         final int metaState = event.getMetaState();
   3096         final int deviceId = event.getDeviceId();
   3097         final int source = event.getSource();
   3098 
   3099         int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X));
   3100         if (xDirection == 0) {
   3101             xDirection = joystickAxisValueToDirection(event.getX());
   3102         }
   3103 
   3104         int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y));
   3105         if (yDirection == 0) {
   3106             yDirection = joystickAxisValueToDirection(event.getY());
   3107         }
   3108 
   3109         if (xDirection != mLastJoystickXDirection) {
   3110             if (mLastJoystickXKeyCode != 0) {
   3111                 deliverKeyEvent(new KeyEvent(time, time,
   3112                         KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
   3113                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
   3114                 mLastJoystickXKeyCode = 0;
   3115             }
   3116 
   3117             mLastJoystickXDirection = xDirection;
   3118 
   3119             if (xDirection != 0 && synthesizeNewKeys) {
   3120                 mLastJoystickXKeyCode = xDirection > 0
   3121                         ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
   3122                 deliverKeyEvent(new KeyEvent(time, time,
   3123                         KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
   3124                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
   3125             }
   3126         }
   3127 
   3128         if (yDirection != mLastJoystickYDirection) {
   3129             if (mLastJoystickYKeyCode != 0) {
   3130                 deliverKeyEvent(new KeyEvent(time, time,
   3131                         KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
   3132                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
   3133                 mLastJoystickYKeyCode = 0;
   3134             }
   3135 
   3136             mLastJoystickYDirection = yDirection;
   3137 
   3138             if (yDirection != 0 && synthesizeNewKeys) {
   3139                 mLastJoystickYKeyCode = yDirection > 0
   3140                         ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
   3141                 deliverKeyEvent(new KeyEvent(time, time,
   3142                         KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
   3143                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
   3144             }
   3145         }
   3146     }
   3147 
   3148     private static int joystickAxisValueToDirection(float value) {
   3149         if (value >= 0.5f) {
   3150             return 1;
   3151         } else if (value <= -0.5f) {
   3152             return -1;
   3153         } else {
   3154             return 0;
   3155         }
   3156     }
   3157 
   3158     /**
   3159      * Returns true if the key is used for keyboard navigation.
   3160      * @param keyEvent The key event.
   3161      * @return True if the key is used for keyboard navigation.
   3162      */
   3163     private static boolean isNavigationKey(KeyEvent keyEvent) {
   3164         switch (keyEvent.getKeyCode()) {
   3165         case KeyEvent.KEYCODE_DPAD_LEFT:
   3166         case KeyEvent.KEYCODE_DPAD_RIGHT:
   3167         case KeyEvent.KEYCODE_DPAD_UP:
   3168         case KeyEvent.KEYCODE_DPAD_DOWN:
   3169         case KeyEvent.KEYCODE_DPAD_CENTER:
   3170         case KeyEvent.KEYCODE_PAGE_UP:
   3171         case KeyEvent.KEYCODE_PAGE_DOWN:
   3172         case KeyEvent.KEYCODE_MOVE_HOME:
   3173         case KeyEvent.KEYCODE_MOVE_END:
   3174         case KeyEvent.KEYCODE_TAB:
   3175         case KeyEvent.KEYCODE_SPACE:
   3176         case KeyEvent.KEYCODE_ENTER:
   3177             return true;
   3178         }
   3179         return false;
   3180     }
   3181 
   3182     /**
   3183      * Returns true if the key is used for typing.
   3184      * @param keyEvent The key event.
   3185      * @return True if the key is used for typing.
   3186      */
   3187     private static boolean isTypingKey(KeyEvent keyEvent) {
   3188         return keyEvent.getUnicodeChar() > 0;
   3189     }
   3190 
   3191     /**
   3192      * See if the key event means we should leave touch mode (and leave touch mode if so).
   3193      * @param event The key event.
   3194      * @return Whether this key event should be consumed (meaning the act of
   3195      *   leaving touch mode alone is considered the event).
   3196      */
   3197     private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
   3198         // Only relevant in touch mode.
   3199         if (!mAttachInfo.mInTouchMode) {
   3200             return false;
   3201         }
   3202 
   3203         // Only consider leaving touch mode on DOWN or MULTIPLE actions, never on UP.
   3204         final int action = event.getAction();
   3205         if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) {
   3206             return false;
   3207         }
   3208 
   3209         // Don't leave touch mode if the IME told us not to.
   3210         if ((event.getFlags() & KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
   3211             return false;
   3212         }
   3213 
   3214         // If the key can be used for keyboard navigation then leave touch mode
   3215         // and select a focused view if needed (in ensureTouchMode).
   3216         // When a new focused view is selected, we consume the navigation key because
   3217         // navigation doesn't make much sense unless a view already has focus so
   3218         // the key's purpose is to set focus.
   3219         if (isNavigationKey(event)) {
   3220             return ensureTouchMode(false);
   3221         }
   3222 
   3223         // If the key can be used for typing then leave touch mode
   3224         // and select a focused view if needed (in ensureTouchMode).
   3225         // Always allow the view to process the typing key.
   3226         if (isTypingKey(event)) {
   3227             ensureTouchMode(false);
   3228             return false;
   3229         }
   3230 
   3231         return false;
   3232     }
   3233 
   3234     int enqueuePendingEvent(Object event, boolean sendDone) {
   3235         int seq = mPendingEventSeq+1;
   3236         if (seq < 0) seq = 0;
   3237         mPendingEventSeq = seq;
   3238         mPendingEvents.put(seq, event);
   3239         return sendDone ? seq : -seq;
   3240     }
   3241 
   3242     Object retrievePendingEvent(int seq) {
   3243         if (seq < 0) seq = -seq;
   3244         Object event = mPendingEvents.get(seq);
   3245         if (event != null) {
   3246             mPendingEvents.remove(seq);
   3247         }
   3248         return event;
   3249     }
   3250 
   3251     private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
   3252         if (ViewDebug.DEBUG_LATENCY) {
   3253             mInputEventDeliverTimeNanos = System.nanoTime();
   3254         }
   3255 
   3256         if (mInputEventConsistencyVerifier != null) {
   3257             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
   3258         }
   3259 
   3260         // If there is no view, then the event will not be handled.
   3261         if (mView == null || !mAdded) {
   3262             finishKeyEvent(event, sendDone, false);
   3263             return;
   3264         }
   3265 
   3266         if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
   3267 
   3268         // Perform predispatching before the IME.
   3269         if (mView.dispatchKeyEventPreIme(event)) {
   3270             finishKeyEvent(event, sendDone, true);
   3271             return;
   3272         }
   3273 
   3274         // Dispatch to the IME before propagating down the view hierarchy.
   3275         // The IME will eventually call back into handleFinishedEvent.
   3276         if (mLastWasImTarget) {
   3277             InputMethodManager imm = InputMethodManager.peekInstance();
   3278             if (imm != null) {
   3279                 int seq = enqueuePendingEvent(event, sendDone);
   3280                 if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
   3281                         + seq + " event=" + event);
   3282                 imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
   3283                 return;
   3284             }
   3285         }
   3286 
   3287         // Not dispatching to IME, continue with post IME actions.
   3288         deliverKeyEventPostIme(event, sendDone);
   3289     }
   3290 
   3291     private void handleFinishedEvent(int seq, boolean handled) {
   3292         final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
   3293         if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq
   3294                 + " handled=" + handled + " event=" + event);
   3295         if (event != null) {
   3296             final boolean sendDone = seq >= 0;
   3297             if (handled) {
   3298                 finishKeyEvent(event, sendDone, true);
   3299             } else {
   3300                 deliverKeyEventPostIme(event, sendDone);
   3301             }
   3302         }
   3303     }
   3304 
   3305     private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) {
   3306         if (ViewDebug.DEBUG_LATENCY) {
   3307             mInputEventDeliverPostImeTimeNanos = System.nanoTime();
   3308         }
   3309 
   3310         // If the view went away, then the event will not be handled.
   3311         if (mView == null || !mAdded) {
   3312             finishKeyEvent(event, sendDone, false);
   3313             return;
   3314         }
   3315 
   3316         // If the key's purpose is to exit touch mode then we consume it and consider it handled.
   3317         if (checkForLeavingTouchModeAndConsume(event)) {
   3318             finishKeyEvent(event, sendDone, true);
   3319             return;
   3320         }
   3321 
   3322         // Make sure the fallback event policy sees all keys that will be delivered to the
   3323         // view hierarchy.
   3324         mFallbackEventHandler.preDispatchKeyEvent(event);
   3325 
   3326         // Deliver the key to the view hierarchy.
   3327         if (mView.dispatchKeyEvent(event)) {
   3328             finishKeyEvent(event, sendDone, true);
   3329             return;
   3330         }
   3331 
   3332         // If the Control modifier is held, try to interpret the key as a shortcut.
   3333         if (event.getAction() == KeyEvent.ACTION_DOWN
   3334                 && event.isCtrlPressed()
   3335                 && event.getRepeatCount() == 0
   3336                 && !KeyEvent.isModifierKey(event.getKeyCode())) {
   3337             if (mView.dispatchKeyShortcutEvent(event)) {
   3338                 finishKeyEvent(event, sendDone, true);
   3339                 return;
   3340             }
   3341         }
   3342 
   3343         // Apply the fallback event policy.
   3344         if (mFallbackEventHandler.dispatchKeyEvent(event)) {
   3345             finishKeyEvent(event, sendDone, true);
   3346             return;
   3347         }
   3348 
   3349         // Handle automatic focus changes.
   3350         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   3351             int direction = 0;
   3352             switch (event.getKeyCode()) {
   3353             case KeyEvent.KEYCODE_DPAD_LEFT:
   3354                 if (event.hasNoModifiers()) {
   3355                     direction = View.FOCUS_LEFT;
   3356                 }
   3357                 break;
   3358             case KeyEvent.KEYCODE_DPAD_RIGHT:
   3359                 if (event.hasNoModifiers()) {
   3360                     direction = View.FOCUS_RIGHT;
   3361                 }
   3362                 break;
   3363             case KeyEvent.KEYCODE_DPAD_UP:
   3364                 if (event.hasNoModifiers()) {
   3365                     direction = View.FOCUS_UP;
   3366                 }
   3367                 break;
   3368             case KeyEvent.KEYCODE_DPAD_DOWN:
   3369                 if (event.hasNoModifiers()) {
   3370                     direction = View.FOCUS_DOWN;
   3371                 }
   3372                 break;
   3373             case KeyEvent.KEYCODE_TAB:
   3374                 if (event.hasNoModifiers()) {
   3375                     direction = View.FOCUS_FORWARD;
   3376                 } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
   3377                     direction = View.FOCUS_BACKWARD;
   3378                 }
   3379                 break;
   3380             }
   3381 
   3382             if (direction != 0) {
   3383                 View focused = mView != null ? mView.findFocus() : null;
   3384                 if (focused != null) {
   3385                     View v = focused.focusSearch(direction);
   3386                     if (v != null && v != focused) {
   3387                         // do the math the get the interesting rect
   3388                         // of previous focused into the coord system of
   3389                         // newly focused view
   3390                         focused.getFocusedRect(mTempRect);
   3391                         if (mView instanceof ViewGroup) {
   3392                             ((ViewGroup) mView).offsetDescendantRectToMyCoords(
   3393                                     focused, mTempRect);
   3394                             ((ViewGroup) mView).offsetRectIntoDescendantCoords(
   3395                                     v, mTempRect);
   3396                         }
   3397                         if (v.requestFocus(direction, mTempRect)) {
   3398                             playSoundEffect(
   3399                                     SoundEffectConstants.getContantForFocusDirection(direction));
   3400                             finishKeyEvent(event, sendDone, true);
   3401                             return;
   3402                         }
   3403                     }
   3404 
   3405                     // Give the focused view a last chance to handle the dpad key.
   3406                     if (mView.dispatchUnhandledMove(focused, direction)) {
   3407                         finishKeyEvent(event, sendDone, true);
   3408                         return;
   3409                     }
   3410                 }
   3411             }
   3412         }
   3413 
   3414         // Key was unhandled.
   3415         finishKeyEvent(event, sendDone, false);
   3416     }
   3417 
   3418     private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) {
   3419         if (sendDone) {
   3420             finishInputEvent(event, handled);
   3421         }
   3422     }
   3423 
   3424     /* drag/drop */
   3425     void setLocalDragState(Object obj) {
   3426         mLocalDragState = obj;
   3427     }
   3428 
   3429     private void handleDragEvent(DragEvent event) {
   3430         // From the root, only drag start/end/location are dispatched.  entered/exited
   3431         // are determined and dispatched by the viewgroup hierarchy, who then report
   3432         // that back here for ultimate reporting back to the framework.
   3433         if (mView != null && mAdded) {
   3434             final int what = event.mAction;
   3435 
   3436             if (what == DragEvent.ACTION_DRAG_EXITED) {
   3437                 // A direct EXITED event means that the window manager knows we've just crossed
   3438                 // a window boundary, so the current drag target within this one must have
   3439                 // just been exited.  Send it the usual notifications and then we're done
   3440                 // for now.
   3441                 mView.dispatchDragEvent(event);
   3442             } else {
   3443                 // Cache the drag description when the operation starts, then fill it in
   3444                 // on subsequent calls as a convenience
   3445                 if (what == DragEvent.ACTION_DRAG_STARTED) {
   3446                     mCurrentDragView = null;    // Start the current-recipient tracking
   3447                     mDragDescription = event.mClipDescription;
   3448                 } else {
   3449                     event.mClipDescription = mDragDescription;
   3450                 }
   3451 
   3452                 // For events with a [screen] location, translate into window coordinates
   3453                 if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) {
   3454                     mDragPoint.set(event.mX, event.mY);
   3455                     if (mTranslator != null) {
   3456                         mTranslator.translatePointInScreenToAppWindow(mDragPoint);
   3457                     }
   3458 
   3459                     if (mCurScrollY != 0) {
   3460                         mDragPoint.offset(0, mCurScrollY);
   3461                     }
   3462 
   3463                     event.mX = mDragPoint.x;
   3464                     event.mY = mDragPoint.y;
   3465                 }
   3466 
   3467                 // Remember who the current drag target is pre-dispatch
   3468                 final View prevDragView = mCurrentDragView;
   3469 
   3470                 // Now dispatch the drag/drop event
   3471                 boolean result = mView.dispatchDragEvent(event);
   3472 
   3473                 // If we changed apparent drag target, tell the OS about it
   3474                 if (prevDragView != mCurrentDragView) {
   3475                     try {
   3476                         if (prevDragView != null) {
   3477                             sWindowSession.dragRecipientExited(mWindow);
   3478                         }
   3479                         if (mCurrentDragView != null) {
   3480                             sWindowSession.dragRecipientEntered(mWindow);
   3481                         }
   3482                     } catch (RemoteException e) {
   3483                         Slog.e(TAG, "Unable to note drag target change");
   3484                     }
   3485                 }
   3486 
   3487                 // Report the drop result when we're done
   3488                 if (what == DragEvent.ACTION_DROP) {
   3489                     mDragDescription = null;
   3490                     try {
   3491                         Log.i(TAG, "Reporting drop result: " + result);
   3492                         sWindowSession.reportDropResult(mWindow, result);
   3493                     } catch (RemoteException e) {
   3494                         Log.e(TAG, "Unable to report drop result");
   3495                     }
   3496                 }
   3497 
   3498                 // When the drag operation ends, release any local state object
   3499                 // that may have been in use
   3500                 if (what == DragEvent.ACTION_DRAG_ENDED) {
   3501                     setLocalDragState(null);
   3502                 }
   3503             }
   3504         }
   3505         event.recycle();
   3506     }
   3507 
   3508     public void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) {
   3509         if (mSeq != args.seq) {
   3510             // The sequence has changed, so we need to update our value and make
   3511             // sure to do a traversal afterward so the window manager is given our
   3512             // most recent data.
   3513             mSeq = args.seq;
   3514             mAttachInfo.mForceReportNewAttributes = true;
   3515             scheduleTraversals();
   3516         }
   3517         if (mView == null) return;
   3518         if (args.localChanges != 0) {
   3519             if (mAttachInfo != null) {
   3520                 mAttachInfo.mSystemUiVisibility =
   3521                         (mAttachInfo.mSystemUiVisibility&~args.localChanges)
   3522                         | (args.localValue&args.localChanges);
   3523             }
   3524             mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
   3525             mAttachInfo.mRecomputeGlobalAttributes = true;
   3526             scheduleTraversals();
   3527         }
   3528         mView.dispatchSystemUiVisibilityChanged(args.globalVisibility);
   3529     }
   3530 
   3531     public void getLastTouchPoint(Point outLocation) {
   3532         outLocation.x = (int) mLastTouchPoint.x;
   3533         outLocation.y = (int) mLastTouchPoint.y;
   3534     }
   3535 
   3536     public void setDragFocus(View newDragTarget) {
   3537         if (mCurrentDragView != newDragTarget) {
   3538             mCurrentDragView = newDragTarget;
   3539         }
   3540     }
   3541 
   3542     private AudioManager getAudioManager() {
   3543         if (mView == null) {
   3544             throw new IllegalStateException("getAudioManager called when there is no mView");
   3545         }
   3546         if (mAudioManager == null) {
   3547             mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE);
   3548         }
   3549         return mAudioManager;
   3550     }
   3551 
   3552     public AccessibilityInteractionController getAccessibilityInteractionController() {
   3553         if (mView == null) {
   3554             throw new IllegalStateException("getAccessibilityInteractionController"
   3555                     + " called when there is no mView");
   3556         }
   3557         if (mAccessibilityInteractionController == null) {
   3558             mAccessibilityInteractionController = new AccessibilityInteractionController();
   3559         }
   3560         return mAccessibilityInteractionController;
   3561     }
   3562 
   3563     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
   3564             boolean insetsPending) throws RemoteException {
   3565 
   3566         float appScale = mAttachInfo.mApplicationScale;
   3567         boolean restore = false;
   3568         if (params != null && mTranslator != null) {
   3569             restore = true;
   3570             params.backup();
   3571             mTranslator.translateWindowLayout(params);
   3572         }
   3573         if (params != null) {
   3574             if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
   3575         }
   3576         mPendingConfiguration.seq = 0;
   3577         //Log.d(TAG, ">>>>>> CALLING relayout");
   3578         if (params != null && mOrigWindowType != params.type) {
   3579             // For compatibility with old apps, don't crash here.
   3580             if (mTargetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
   3581                 Slog.w(TAG, "Window type can not be changed after "
   3582                         + "the window is added; ignoring change of " + mView);
   3583                 params.type = mOrigWindowType;
   3584             }
   3585         }
   3586         int relayoutResult = sWindowSession.relayout(
   3587                 mWindow, mSeq, params,
   3588                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
   3589                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
   3590                 viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0,
   3591                 mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
   3592                 mPendingConfiguration, mSurface);
   3593         //Log.d(TAG, "<<<<<< BACK FROM relayout");
   3594         if (restore) {
   3595             params.restore();
   3596         }
   3597 
   3598         if (mTranslator != null) {
   3599             mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
   3600             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
   3601             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
   3602         }
   3603         return relayoutResult;
   3604     }
   3605 
   3606     /**
   3607      * {@inheritDoc}
   3608      */
   3609     public void playSoundEffect(int effectId) {
   3610         checkThread();
   3611 
   3612         try {
   3613             final AudioManager audioManager = getAudioManager();
   3614 
   3615             switch (effectId) {
   3616                 case SoundEffectConstants.CLICK:
   3617                     audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
   3618                     return;
   3619                 case SoundEffectConstants.NAVIGATION_DOWN:
   3620                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
   3621                     return;
   3622                 case SoundEffectConstants.NAVIGATION_LEFT:
   3623                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
   3624                     return;
   3625                 case SoundEffectConstants.NAVIGATION_RIGHT:
   3626                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
   3627                     return;
   3628                 case SoundEffectConstants.NAVIGATION_UP:
   3629                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
   3630                     return;
   3631                 default:
   3632                     throw new IllegalArgumentException("unknown effect id " + effectId +
   3633                             " not defined in " + SoundEffectConstants.class.getCanonicalName());
   3634             }
   3635         } catch (IllegalStateException e) {
   3636             // Exception thrown by getAudioManager() when mView is null
   3637             Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
   3638             e.printStackTrace();
   3639         }
   3640     }
   3641 
   3642     /**
   3643      * {@inheritDoc}
   3644      */
   3645     public boolean performHapticFeedback(int effectId, boolean always) {
   3646         try {
   3647             return sWindowSession.performHapticFeedback(mWindow, effectId, always);
   3648         } catch (RemoteException e) {
   3649             return false;
   3650         }
   3651     }
   3652 
   3653     /**
   3654      * {@inheritDoc}
   3655      */
   3656     public View focusSearch(View focused, int direction) {
   3657         checkThread();
   3658         if (!(mView instanceof ViewGroup)) {
   3659             return null;
   3660         }
   3661         return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
   3662     }
   3663 
   3664     public void debug() {
   3665         mView.debug();
   3666     }
   3667 
   3668     public void dumpGfxInfo(PrintWriter pw, int[] info) {
   3669         if (mView != null) {
   3670             getGfxInfo(mView, info);
   3671         } else {
   3672             info[0] = info[1] = 0;
   3673         }
   3674     }
   3675 
   3676     private void getGfxInfo(View view, int[] info) {
   3677         DisplayList displayList = view.mDisplayList;
   3678         info[0]++;
   3679         if (displayList != null) {
   3680             info[1] += displayList.getSize();
   3681         }
   3682 
   3683         if (view instanceof ViewGroup) {
   3684             ViewGroup group = (ViewGroup) view;
   3685 
   3686             int count = group.getChildCount();
   3687             for (int i = 0; i < count; i++) {
   3688                 getGfxInfo(group.getChildAt(i), info);
   3689             }
   3690         }
   3691     }
   3692 
   3693     public void die(boolean immediate) {
   3694         if (immediate) {
   3695             doDie();
   3696         } else {
   3697             sendEmptyMessage(DIE);
   3698         }
   3699     }
   3700 
   3701     void doDie() {
   3702         checkThread();
   3703         if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
   3704         synchronized (this) {
   3705             if (mAdded) {
   3706                 mAdded = false;
   3707                 dispatchDetachedFromWindow();
   3708             }
   3709 
   3710             if (mAdded && !mFirst) {
   3711                 destroyHardwareRenderer();
   3712 
   3713                 int viewVisibility = mView.getVisibility();
   3714                 boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
   3715                 if (mWindowAttributesChanged || viewVisibilityChanged) {
   3716                     // If layout params have been changed, first give them
   3717                     // to the window manager to make sure it has the correct
   3718                     // animation info.
   3719                     try {
   3720                         if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
   3721                                 & WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
   3722                             sWindowSession.finishDrawing(mWindow);
   3723                         }
   3724                     } catch (RemoteException e) {
   3725                     }
   3726                 }
   3727 
   3728                 mSurface.release();
   3729             }
   3730         }
   3731     }
   3732 
   3733     public void requestUpdateConfiguration(Configuration config) {
   3734         Message msg = obtainMessage(UPDATE_CONFIGURATION, config);
   3735         sendMessage(msg);
   3736     }
   3737 
   3738     private void destroyHardwareRenderer() {
   3739         if (mAttachInfo.mHardwareRenderer != null) {
   3740             mAttachInfo.mHardwareRenderer.destroy(true);
   3741             mAttachInfo.mHardwareRenderer = null;
   3742             mAttachInfo.mHardwareAccelerated = false;
   3743         }
   3744     }
   3745 
   3746     public void dispatchFinishedEvent(int seq, boolean handled) {
   3747         Message msg = obtainMessage(FINISHED_EVENT);
   3748         msg.arg1 = seq;
   3749         msg.arg2 = handled ? 1 : 0;
   3750         sendMessage(msg);
   3751     }
   3752 
   3753     public void dispatchResized(int w, int h, Rect coveredInsets,
   3754             Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
   3755         if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w
   3756                 + " h=" + h + " coveredInsets=" + coveredInsets.toShortString()
   3757                 + " visibleInsets=" + visibleInsets.toShortString()
   3758                 + " reportDraw=" + reportDraw);
   3759         Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
   3760         if (mTranslator != null) {
   3761             mTranslator.translateRectInScreenToAppWindow(coveredInsets);
   3762             mTranslator.translateRectInScreenToAppWindow(visibleInsets);
   3763             w *= mTranslator.applicationInvertedScale;
   3764             h *= mTranslator.applicationInvertedScale;
   3765         }
   3766         msg.arg1 = w;
   3767         msg.arg2 = h;
   3768         ResizedInfo ri = new ResizedInfo();
   3769         ri.coveredInsets = new Rect(coveredInsets);
   3770         ri.visibleInsets = new Rect(visibleInsets);
   3771         ri.newConfig = newConfig;
   3772         msg.obj = ri;
   3773         sendMessage(msg);
   3774     }
   3775 
   3776     private long mInputEventReceiveTimeNanos;
   3777     private long mInputEventDeliverTimeNanos;
   3778     private long mInputEventDeliverPostImeTimeNanos;
   3779     private InputQueue.FinishedCallback mFinishedCallback;
   3780 
   3781     private final InputHandler mInputHandler = new InputHandler() {
   3782         public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
   3783             startInputEvent(finishedCallback);
   3784             dispatchKey(event, true);
   3785         }
   3786 
   3787         public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
   3788             startInputEvent(finishedCallback);
   3789             dispatchMotion(event, true);
   3790         }
   3791     };
   3792 
   3793     /**
   3794      * Utility class used to queue up input events which are then handled during
   3795      * performTraversals(). Doing it this way allows us to ensure that we are up to date with
   3796      * all input events just prior to drawing, instead of placing those events on the regular
   3797      * handler queue, potentially behind a drawing event.
   3798      */
   3799     static class InputEventMessage {
   3800         Message mMessage;
   3801         InputEventMessage mNext;
   3802 
   3803         private static final Object sPoolSync = new Object();
   3804         private static InputEventMessage sPool;
   3805         private static int sPoolSize = 0;
   3806 
   3807         private static final int MAX_POOL_SIZE = 10;
   3808 
   3809         private InputEventMessage(Message m) {
   3810             mMessage = m;
   3811             mNext = null;
   3812         }
   3813 
   3814         /**
   3815          * Return a new Message instance from the global pool. Allows us to
   3816          * avoid allocating new objects in many cases.
   3817          */
   3818         public static InputEventMessage obtain(Message msg) {
   3819             synchronized (sPoolSync) {
   3820                 if (sPool != null) {
   3821                     InputEventMessage m = sPool;
   3822                     sPool = m.mNext;
   3823                     m.mNext = null;
   3824                     sPoolSize--;
   3825                     m.mMessage = msg;
   3826                     return m;
   3827                 }
   3828             }
   3829             return new InputEventMessage(msg);
   3830         }
   3831 
   3832         /**
   3833          * Return the message to the pool.
   3834          */
   3835         public void recycle() {
   3836             mMessage.recycle();
   3837             synchronized (sPoolSync) {
   3838                 if (sPoolSize < MAX_POOL_SIZE) {
   3839                     mNext = sPool;
   3840                     sPool = this;
   3841                     sPoolSize++;
   3842                 }
   3843             }
   3844 
   3845         }
   3846     }
   3847 
   3848     /**
   3849      * Place the input event message at the end of the current pending list
   3850      */
   3851     private void enqueueInputEvent(Message msg, long when) {
   3852         InputEventMessage inputMessage = InputEventMessage.obtain(msg);
   3853         if (mPendingInputEvents == null) {
   3854             mPendingInputEvents = inputMessage;
   3855         } else {
   3856             InputEventMessage currMessage = mPendingInputEvents;
   3857             while (currMessage.mNext != null) {
   3858                 currMessage = currMessage.mNext;
   3859             }
   3860             currMessage.mNext = inputMessage;
   3861         }
   3862         sendEmptyMessageAtTime(PROCESS_INPUT_EVENTS, when);
   3863     }
   3864 
   3865     public void dispatchKey(KeyEvent event) {
   3866         dispatchKey(event, false);
   3867     }
   3868 
   3869     private void dispatchKey(KeyEvent event, boolean sendDone) {
   3870         //noinspection ConstantConditions
   3871         if (false && event.getAction() == KeyEvent.ACTION_DOWN) {
   3872             if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
   3873                 if (DBG) Log.d("keydisp", "===================================================");
   3874                 if (DBG) Log.d("keydisp", "Focused view Hierarchy is:");
   3875 
   3876                 debug();
   3877 
   3878                 if (DBG) Log.d("keydisp", "===================================================");
   3879             }
   3880         }
   3881 
   3882         Message msg = obtainMessage(DISPATCH_KEY);
   3883         msg.obj = event;
   3884         msg.arg1 = sendDone ? 1 : 0;
   3885 
   3886         if (LOCAL_LOGV) Log.v(
   3887             TAG, "sending key " + event + " to " + mView);
   3888 
   3889         enqueueInputEvent(msg, event.getEventTime());
   3890     }
   3891 
   3892     private void dispatchMotion(MotionEvent event, boolean sendDone) {
   3893         int source = event.getSource();
   3894         if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
   3895             dispatchPointer(event, sendDone);
   3896         } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
   3897             dispatchTrackball(event, sendDone);
   3898         } else {
   3899             dispatchGenericMotion(event, sendDone);
   3900         }
   3901     }
   3902 
   3903     private void dispatchPointer(MotionEvent event, boolean sendDone) {
   3904         Message msg = obtainMessage(DISPATCH_POINTER);
   3905         msg.obj = event;
   3906         msg.arg1 = sendDone ? 1 : 0;
   3907         enqueueInputEvent(msg, event.getEventTime());
   3908     }
   3909 
   3910     private void dispatchTrackball(MotionEvent event, boolean sendDone) {
   3911         Message msg = obtainMessage(DISPATCH_TRACKBALL);
   3912         msg.obj = event;
   3913         msg.arg1 = sendDone ? 1 : 0;
   3914         enqueueInputEvent(msg, event.getEventTime());
   3915     }
   3916 
   3917     private void dispatchGenericMotion(MotionEvent event, boolean sendDone) {
   3918         Message msg = obtainMessage(DISPATCH_GENERIC_MOTION);
   3919         msg.obj = event;
   3920         msg.arg1 = sendDone ? 1 : 0;
   3921         enqueueInputEvent(msg, event.getEventTime());
   3922     }
   3923 
   3924     public void dispatchAppVisibility(boolean visible) {
   3925         Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
   3926         msg.arg1 = visible ? 1 : 0;
   3927         sendMessage(msg);
   3928     }
   3929 
   3930     public void dispatchGetNewSurface() {
   3931         Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE);
   3932         sendMessage(msg);
   3933     }
   3934 
   3935     public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
   3936         Message msg = Message.obtain();
   3937         msg.what = WINDOW_FOCUS_CHANGED;
   3938         msg.arg1 = hasFocus ? 1 : 0;
   3939         msg.arg2 = inTouchMode ? 1 : 0;
   3940         sendMessage(msg);
   3941     }
   3942 
   3943     public void dispatchCloseSystemDialogs(String reason) {
   3944         Message msg = Message.obtain();
   3945         msg.what = CLOSE_SYSTEM_DIALOGS;
   3946         msg.obj = reason;
   3947         sendMessage(msg);
   3948     }
   3949 
   3950     public void dispatchDragEvent(DragEvent event) {
   3951         final int what;
   3952         if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
   3953             what = DISPATCH_DRAG_LOCATION_EVENT;
   3954             removeMessages(what);
   3955         } else {
   3956             what = DISPATCH_DRAG_EVENT;
   3957         }
   3958         Message msg = obtainMessage(what, event);
   3959         sendMessage(msg);
   3960     }
   3961 
   3962     public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
   3963             int localValue, int localChanges) {
   3964         SystemUiVisibilityInfo args = new SystemUiVisibilityInfo();
   3965         args.seq = seq;
   3966         args.globalVisibility = globalVisibility;
   3967         args.localValue = localValue;
   3968         args.localChanges = localChanges;
   3969         sendMessage(obtainMessage(DISPATCH_SYSTEM_UI_VISIBILITY, args));
   3970     }
   3971 
   3972     /**
   3973      * The window is getting focus so if there is anything focused/selected
   3974      * send an {@link AccessibilityEvent} to announce that.
   3975      */
   3976     private void sendAccessibilityEvents() {
   3977         if (!mAccessibilityManager.isEnabled()) {
   3978             return;
   3979         }
   3980         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   3981         View focusedView = mView.findFocus();
   3982         if (focusedView != null && focusedView != mView) {
   3983             focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
   3984         }
   3985     }
   3986 
   3987     /**
   3988      * Post a callback to send a
   3989      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
   3990      * This event is send at most once every
   3991      * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
   3992      */
   3993     private void postSendWindowContentChangedCallback() {
   3994         if (mSendWindowContentChangedAccessibilityEvent == null) {
   3995             mSendWindowContentChangedAccessibilityEvent =
   3996                 new SendWindowContentChangedAccessibilityEvent();
   3997         }
   3998         if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) {
   3999             mSendWindowContentChangedAccessibilityEvent.mIsPending = true;
   4000             postDelayed(mSendWindowContentChangedAccessibilityEvent,
   4001                     ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
   4002         }
   4003     }
   4004 
   4005     /**
   4006      * Remove a posted callback to send a
   4007      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
   4008      */
   4009     private void removeSendWindowContentChangedCallback() {
   4010         if (mSendWindowContentChangedAccessibilityEvent != null) {
   4011             removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
   4012         }
   4013     }
   4014 
   4015     public boolean showContextMenuForChild(View originalView) {
   4016         return false;
   4017     }
   4018 
   4019     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
   4020         return null;
   4021     }
   4022 
   4023     public void createContextMenu(ContextMenu menu) {
   4024     }
   4025 
   4026     public void childDrawableStateChanged(View child) {
   4027     }
   4028 
   4029     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
   4030         if (mView == null) {
   4031             return false;
   4032         }
   4033         mAccessibilityManager.sendAccessibilityEvent(event);
   4034         return true;
   4035     }
   4036 
   4037     void checkThread() {
   4038         if (mThread != Thread.currentThread()) {
   4039             throw new CalledFromWrongThreadException(
   4040                     "Only the original thread that created a view hierarchy can touch its views.");
   4041         }
   4042     }
   4043 
   4044     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   4045         // ViewAncestor never intercepts touch event, so this can be a no-op
   4046     }
   4047 
   4048     public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
   4049             boolean immediate) {
   4050         return scrollToRectOrFocus(rectangle, immediate);
   4051     }
   4052 
   4053     class TakenSurfaceHolder extends BaseSurfaceHolder {
   4054         @Override
   4055         public boolean onAllowLockCanvas() {
   4056             return mDrawingAllowed;
   4057         }
   4058 
   4059         @Override
   4060         public void onRelayoutContainer() {
   4061             // Not currently interesting -- from changing between fixed and layout size.
   4062         }
   4063 
   4064         public void setFormat(int format) {
   4065             ((RootViewSurfaceTaker)mView).setSurfaceFormat(format);
   4066         }
   4067 
   4068         public void setType(int type) {
   4069             ((RootViewSurfaceTaker)mView).setSurfaceType(type);
   4070         }
   4071 
   4072         @Override
   4073         public void onUpdateSurface() {
   4074             // We take care of format and type changes on our own.
   4075             throw new IllegalStateException("Shouldn't be here");
   4076         }
   4077 
   4078         public boolean isCreating() {
   4079             return mIsCreating;
   4080         }
   4081 
   4082         @Override
   4083         public void setFixedSize(int width, int height) {
   4084             throw new UnsupportedOperationException(
   4085                     "Currently only support sizing from layout");
   4086         }
   4087 
   4088         public void setKeepScreenOn(boolean screenOn) {
   4089             ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
   4090         }
   4091     }
   4092 
   4093     static class InputMethodCallback extends IInputMethodCallback.Stub {
   4094         private WeakReference<ViewRootImpl> mViewAncestor;
   4095 
   4096         public InputMethodCallback(ViewRootImpl viewAncestor) {
   4097             mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
   4098         }
   4099 
   4100         public void finishedEvent(int seq, boolean handled) {
   4101             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4102             if (viewAncestor != null) {
   4103                 viewAncestor.dispatchFinishedEvent(seq, handled);
   4104             }
   4105         }
   4106 
   4107         public void sessionCreated(IInputMethodSession session) {
   4108             // Stub -- not for use in the client.
   4109         }
   4110     }
   4111 
   4112     static class W extends IWindow.Stub {
   4113         private final WeakReference<ViewRootImpl> mViewAncestor;
   4114 
   4115         W(ViewRootImpl viewAncestor) {
   4116             mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
   4117         }
   4118 
   4119         public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
   4120                 boolean reportDraw, Configuration newConfig) {
   4121             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4122             if (viewAncestor != null) {
   4123                 viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw,
   4124                         newConfig);
   4125             }
   4126         }
   4127 
   4128         public void dispatchAppVisibility(boolean visible) {
   4129             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4130             if (viewAncestor != null) {
   4131                 viewAncestor.dispatchAppVisibility(visible);
   4132             }
   4133         }
   4134 
   4135         public void dispatchGetNewSurface() {
   4136             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4137             if (viewAncestor != null) {
   4138                 viewAncestor.dispatchGetNewSurface();
   4139             }
   4140         }
   4141 
   4142         public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
   4143             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4144             if (viewAncestor != null) {
   4145                 viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
   4146             }
   4147         }
   4148 
   4149         private static int checkCallingPermission(String permission) {
   4150             try {
   4151                 return ActivityManagerNative.getDefault().checkPermission(
   4152                         permission, Binder.getCallingPid(), Binder.getCallingUid());
   4153             } catch (RemoteException e) {
   4154                 return PackageManager.PERMISSION_DENIED;
   4155             }
   4156         }
   4157 
   4158         public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
   4159             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4160             if (viewAncestor != null) {
   4161                 final View view = viewAncestor.mView;
   4162                 if (view != null) {
   4163                     if (checkCallingPermission(Manifest.permission.DUMP) !=
   4164                             PackageManager.PERMISSION_GRANTED) {
   4165                         throw new SecurityException("Insufficient permissions to invoke"
   4166                                 + " executeCommand() from pid=" + Binder.getCallingPid()
   4167                                 + ", uid=" + Binder.getCallingUid());
   4168                     }
   4169 
   4170                     OutputStream clientStream = null;
   4171                     try {
   4172                         clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out);
   4173                         ViewDebug.dispatchCommand(view, command, parameters, clientStream);
   4174                     } catch (IOException e) {
   4175                         e.printStackTrace();
   4176                     } finally {
   4177                         if (clientStream != null) {
   4178                             try {
   4179                                 clientStream.close();
   4180                             } catch (IOException e) {
   4181                                 e.printStackTrace();
   4182                             }
   4183                         }
   4184                     }
   4185                 }
   4186             }
   4187         }
   4188 
   4189         public void closeSystemDialogs(String reason) {
   4190             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4191             if (viewAncestor != null) {
   4192                 viewAncestor.dispatchCloseSystemDialogs(reason);
   4193             }
   4194         }
   4195 
   4196         public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
   4197                 boolean sync) {
   4198             if (sync) {
   4199                 try {
   4200                     sWindowSession.wallpaperOffsetsComplete(asBinder());
   4201                 } catch (RemoteException e) {
   4202                 }
   4203             }
   4204         }
   4205 
   4206         public void dispatchWallpaperCommand(String action, int x, int y,
   4207                 int z, Bundle extras, boolean sync) {
   4208             if (sync) {
   4209                 try {
   4210                     sWindowSession.wallpaperCommandComplete(asBinder(), null);
   4211                 } catch (RemoteException e) {
   4212                 }
   4213             }
   4214         }
   4215 
   4216         /* Drag/drop */
   4217         public void dispatchDragEvent(DragEvent event) {
   4218             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4219             if (viewAncestor != null) {
   4220                 viewAncestor.dispatchDragEvent(event);
   4221             }
   4222         }
   4223 
   4224         public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
   4225                 int localValue, int localChanges) {
   4226             final ViewRootImpl viewAncestor = mViewAncestor.get();
   4227             if (viewAncestor != null) {
   4228                 viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility,
   4229                         localValue, localChanges);
   4230             }
   4231         }
   4232     }
   4233 
   4234     /**
   4235      * Maintains state information for a single trackball axis, generating
   4236      * discrete (DPAD) movements based on raw trackball motion.
   4237      */
   4238     static final class TrackballAxis {
   4239         /**
   4240          * The maximum amount of acceleration we will apply.
   4241          */
   4242         static final float MAX_ACCELERATION = 20;
   4243 
   4244         /**
   4245          * The maximum amount of time (in milliseconds) between events in order
   4246          * for us to consider the user to be doing fast trackball movements,
   4247          * and thus apply an acceleration.
   4248          */
   4249         static final long FAST_MOVE_TIME = 150;
   4250 
   4251         /**
   4252          * Scaling factor to the time (in milliseconds) between events to how
   4253          * much to multiple/divide the current acceleration.  When movement
   4254          * is < FAST_MOVE_TIME this multiplies the acceleration; when >
   4255          * FAST_MOVE_TIME it divides it.
   4256          */
   4257         static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
   4258 
   4259         float position;
   4260         float absPosition;
   4261         float acceleration = 1;
   4262         long lastMoveTime = 0;
   4263         int step;
   4264         int dir;
   4265         int nonAccelMovement;
   4266 
   4267         void reset(int _step) {
   4268             position = 0;
   4269             acceleration = 1;
   4270             lastMoveTime = 0;
   4271             step = _step;
   4272             dir = 0;
   4273         }
   4274 
   4275         /**
   4276          * Add trackball movement into the state.  If the direction of movement
   4277          * has been reversed, the state is reset before adding the
   4278          * movement (so that you don't have to compensate for any previously
   4279          * collected movement before see the result of the movement in the
   4280          * new direction).
   4281          *
   4282          * @return Returns the absolute value of the amount of movement
   4283          * collected so far.
   4284          */
   4285         float collect(float off, long time, String axis) {
   4286             long normTime;
   4287             if (off > 0) {
   4288                 normTime = (long)(off * FAST_MOVE_TIME);
   4289                 if (dir < 0) {
   4290                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
   4291                     position = 0;
   4292                     step = 0;
   4293                     acceleration = 1;
   4294                     lastMoveTime = 0;
   4295                 }
   4296                 dir = 1;
   4297             } else if (off < 0) {
   4298                 normTime = (long)((-off) * FAST_MOVE_TIME);
   4299                 if (dir > 0) {
   4300                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
   4301                     position = 0;
   4302                     step = 0;
   4303                     acceleration = 1;
   4304                     lastMoveTime = 0;
   4305                 }
   4306                 dir = -1;
   4307             } else {
   4308                 normTime = 0;
   4309             }
   4310 
   4311             // The number of milliseconds between each movement that is
   4312             // considered "normal" and will not result in any acceleration
   4313             // or deceleration, scaled by the offset we have here.
   4314             if (normTime > 0) {
   4315                 long delta = time - lastMoveTime;
   4316                 lastMoveTime = time;
   4317                 float acc = acceleration;
   4318                 if (delta < normTime) {
   4319                     // The user is scrolling rapidly, so increase acceleration.
   4320                     float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
   4321                     if (scale > 1) acc *= scale;
   4322                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
   4323                             + off + " normTime=" + normTime + " delta=" + delta
   4324                             + " scale=" + scale + " acc=" + acc);
   4325                     acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
   4326                 } else {
   4327                     // The user is scrolling slowly, so decrease acceleration.
   4328                     float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
   4329                     if (scale > 1) acc /= scale;
   4330                     if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
   4331                             + off + " normTime=" + normTime + " delta=" + delta
   4332                             + " scale=" + scale + " acc=" + acc);
   4333                     acceleration = acc > 1 ? acc : 1;
   4334                 }
   4335             }
   4336             position += off;
   4337             return (absPosition = Math.abs(position));
   4338         }
   4339 
   4340         /**
   4341          * Generate the number of discrete movement events appropriate for
   4342          * the currently collected trackball movement.
   4343          *
   4344          * @param precision The minimum movement required to generate the
   4345          * first discrete movement.
   4346          *
   4347          * @return Returns the number of discrete movements, either positive
   4348          * or negative, or 0 if there is not enough trackball movement yet
   4349          * for a discrete movement.
   4350          */
   4351         int generate(float precision) {
   4352             int movement = 0;
   4353             nonAccelMovement = 0;
   4354             do {
   4355                 final int dir = position >= 0 ? 1 : -1;
   4356                 switch (step) {
   4357                     // If we are going to execute the first step, then we want
   4358                     // to do this as soon as possible instead of waiting for
   4359                     // a full movement, in order to make things look responsive.
   4360                     case 0:
   4361                         if (absPosition < precision) {
   4362                             return movement;
   4363                         }
   4364                         movement += dir;
   4365                         nonAccelMovement += dir;
   4366                         step = 1;
   4367                         break;
   4368                     // If we have generated the first movement, then we need
   4369                     // to wait for the second complete trackball motion before
   4370                     // generating the second discrete movement.
   4371                     case 1:
   4372                         if (absPosition < 2) {
   4373                             return movement;
   4374                         }
   4375                         movement += dir;
   4376                         nonAccelMovement += dir;
   4377                         position += dir > 0 ? -2 : 2;
   4378                         absPosition = Math.abs(position);
   4379                         step = 2;
   4380                         break;
   4381                     // After the first two, we generate discrete movements
   4382                     // consistently with the trackball, applying an acceleration
   4383                     // if the trackball is moving quickly.  This is a simple
   4384                     // acceleration on top of what we already compute based
   4385                     // on how quickly the wheel is being turned, to apply
   4386                     // a longer increasing acceleration to continuous movement
   4387                     // in one direction.
   4388                     default:
   4389                         if (absPosition < 1) {
   4390                             return movement;
   4391                         }
   4392                         movement += dir;
   4393                         position += dir >= 0 ? -1 : 1;
   4394                         absPosition = Math.abs(position);
   4395                         float acc = acceleration;
   4396                         acc *= 1.1f;
   4397                         acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
   4398                         break;
   4399                 }
   4400             } while (true);
   4401         }
   4402     }
   4403 
   4404     public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
   4405         public CalledFromWrongThreadException(String msg) {
   4406             super(msg);
   4407         }
   4408     }
   4409 
   4410     private SurfaceHolder mHolder = new SurfaceHolder() {
   4411         // we only need a SurfaceHolder for opengl. it would be nice
   4412         // to implement everything else though, especially the callback
   4413         // support (opengl doesn't make use of it right now, but eventually
   4414         // will).
   4415         public Surface getSurface() {
   4416             return mSurface;
   4417         }
   4418 
   4419         public boolean isCreating() {
   4420             return false;
   4421         }
   4422 
   4423         public void addCallback(Callback callback) {
   4424         }
   4425 
   4426         public void removeCallback(Callback callback) {
   4427         }
   4428 
   4429         public void setFixedSize(int width, int height) {
   4430         }
   4431 
   4432         public void setSizeFromLayout() {
   4433         }
   4434 
   4435         public void setFormat(int format) {
   4436         }
   4437 
   4438         public void setType(int type) {
   4439         }
   4440 
   4441         public void setKeepScreenOn(boolean screenOn) {
   4442         }
   4443 
   4444         public Canvas lockCanvas() {
   4445             return null;
   4446         }
   4447 
   4448         public Canvas lockCanvas(Rect dirty) {
   4449             return null;
   4450         }
   4451 
   4452         public void unlockCanvasAndPost(Canvas canvas) {
   4453         }
   4454         public Rect getSurfaceFrame() {
   4455             return null;
   4456         }
   4457     };
   4458 
   4459     static RunQueue getRunQueue() {
   4460         RunQueue rq = sRunQueues.get();
   4461         if (rq != null) {
   4462             return rq;
   4463         }
   4464         rq = new RunQueue();
   4465         sRunQueues.set(rq);
   4466         return rq;
   4467     }
   4468 
   4469     /**
   4470      * @hide
   4471      */
   4472     static final class RunQueue {
   4473         private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
   4474 
   4475         void post(Runnable action) {
   4476             postDelayed(action, 0);
   4477         }
   4478 
   4479         void postDelayed(Runnable action, long delayMillis) {
   4480             HandlerAction handlerAction = new HandlerAction();
   4481             handlerAction.action = action;
   4482             handlerAction.delay = delayMillis;
   4483 
   4484             synchronized (mActions) {
   4485                 mActions.add(handlerAction);
   4486             }
   4487         }
   4488 
   4489         void removeCallbacks(Runnable action) {
   4490             final HandlerAction handlerAction = new HandlerAction();
   4491             handlerAction.action = action;
   4492 
   4493             synchronized (mActions) {
   4494                 final ArrayList<HandlerAction> actions = mActions;
   4495 
   4496                 while (actions.remove(handlerAction)) {
   4497                     // Keep going
   4498                 }
   4499             }
   4500         }
   4501 
   4502         void executeActions(Handler handler) {
   4503             synchronized (mActions) {
   4504                 final ArrayList<HandlerAction> actions = mActions;
   4505                 final int count = actions.size();
   4506 
   4507                 for (int i = 0; i < count; i++) {
   4508                     final HandlerAction handlerAction = actions.get(i);
   4509                     handler.postDelayed(handlerAction.action, handlerAction.delay);
   4510                 }
   4511 
   4512                 actions.clear();
   4513             }
   4514         }
   4515 
   4516         private static class HandlerAction {
   4517             Runnable action;
   4518             long delay;
   4519 
   4520             @Override
   4521             public boolean equals(Object o) {
   4522                 if (this == o) return true;
   4523                 if (o == null || getClass() != o.getClass()) return false;
   4524 
   4525                 HandlerAction that = (HandlerAction) o;
   4526                 return !(action != null ? !action.equals(that.action) : that.action != null);
   4527 
   4528             }
   4529 
   4530             @Override
   4531             public int hashCode() {
   4532                 int result = action != null ? action.hashCode() : 0;
   4533                 result = 31 * result + (int) (delay ^ (delay >>> 32));
   4534                 return result;
   4535             }
   4536         }
   4537     }
   4538 
   4539     /**
   4540      * Class for managing the accessibility interaction connection
   4541      * based on the global accessibility state.
   4542      */
   4543     final class AccessibilityInteractionConnectionManager
   4544             implements AccessibilityStateChangeListener {
   4545         public void onAccessibilityStateChanged(boolean enabled) {
   4546             if (enabled) {
   4547                 ensureConnection();
   4548             } else {
   4549                 ensureNoConnection();
   4550             }
   4551         }
   4552 
   4553         public void ensureConnection() {
   4554             final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
   4555             if (!registered) {
   4556                 mAttachInfo.mAccessibilityWindowId =
   4557                     mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
   4558                             new AccessibilityInteractionConnection(ViewRootImpl.this));
   4559             }
   4560         }
   4561 
   4562         public void ensureNoConnection() {
   4563             final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
   4564             if (registered) {
   4565                 mAttachInfo.mAccessibilityWindowId = View.NO_ID;
   4566                 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
   4567             }
   4568         }
   4569     }
   4570 
   4571     /**
   4572      * This class is an interface this ViewAncestor provides to the
   4573      * AccessibilityManagerService to the latter can interact with
   4574      * the view hierarchy in this ViewAncestor.
   4575      */
   4576     static final class AccessibilityInteractionConnection
   4577             extends IAccessibilityInteractionConnection.Stub {
   4578         private final WeakReference<ViewRootImpl> mRootImpl;
   4579 
   4580         AccessibilityInteractionConnection(ViewRootImpl viewAncestor) {
   4581             mRootImpl = new WeakReference<ViewRootImpl>(viewAncestor);
   4582         }
   4583 
   4584         public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
   4585                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
   4586                 int interrogatingPid, long interrogatingTid) {
   4587             ViewRootImpl viewRootImpl = mRootImpl.get();
   4588             if (viewRootImpl != null) {
   4589                 viewRootImpl.getAccessibilityInteractionController()
   4590                     .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
   4591                         interactionId, callback, interrogatingPid, interrogatingTid);
   4592             }
   4593         }
   4594 
   4595         public void performAccessibilityAction(int accessibilityId, int action,
   4596                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
   4597                 int interogatingPid, long interrogatingTid) {
   4598             ViewRootImpl viewRootImpl = mRootImpl.get();
   4599             if (viewRootImpl != null) {
   4600                 viewRootImpl.getAccessibilityInteractionController()
   4601                     .performAccessibilityActionClientThread(accessibilityId, action, interactionId,
   4602                             callback, interogatingPid, interrogatingTid);
   4603             }
   4604         }
   4605 
   4606         public void findAccessibilityNodeInfoByViewId(int viewId,
   4607                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
   4608                 int interrogatingPid, long interrogatingTid) {
   4609             ViewRootImpl viewRootImpl = mRootImpl.get();
   4610             if (viewRootImpl != null) {
   4611                 viewRootImpl.getAccessibilityInteractionController()
   4612                     .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback,
   4613                             interrogatingPid, interrogatingTid);
   4614             }
   4615         }
   4616 
   4617         public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId,
   4618                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
   4619                 int interrogatingPid, long interrogatingTid) {
   4620             ViewRootImpl viewRootImpl = mRootImpl.get();
   4621             if (viewRootImpl != null) {
   4622                 viewRootImpl.getAccessibilityInteractionController()
   4623                     .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId,
   4624                             interactionId, callback, interrogatingPid, interrogatingTid);
   4625             }
   4626         }
   4627     }
   4628 
   4629     /**
   4630      * Class for managing accessibility interactions initiated from the system
   4631      * and targeting the view hierarchy. A *ClientThread method is to be
   4632      * called from the interaction connection this ViewAncestor gives the
   4633      * system to talk to it and a corresponding *UiThread method that is executed
   4634      * on the UI thread.
   4635      */
   4636     final class AccessibilityInteractionController {
   4637         private static final int POOL_SIZE = 5;
   4638 
   4639         private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
   4640             new ArrayList<AccessibilityNodeInfo>();
   4641 
   4642         // Reusable poolable arguments for interacting with the view hierarchy
   4643         // to fit more arguments than Message and to avoid sharing objects between
   4644         // two messages since several threads can send messages concurrently.
   4645         private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool(
   4646                 new PoolableManager<SomeArgs>() {
   4647                     public SomeArgs newInstance() {
   4648                         return new SomeArgs();
   4649                     }
   4650 
   4651                     public void onAcquired(SomeArgs info) {
   4652                         /* do nothing */
   4653                     }
   4654 
   4655                     public void onReleased(SomeArgs info) {
   4656                         info.clear();
   4657                     }
   4658                 }, POOL_SIZE)
   4659         );
   4660 
   4661         public class SomeArgs implements Poolable<SomeArgs> {
   4662             private SomeArgs mNext;
   4663             private boolean mIsPooled;
   4664 
   4665             public Object arg1;
   4666             public Object arg2;
   4667             public int argi1;
   4668             public int argi2;
   4669             public int argi3;
   4670 
   4671             public SomeArgs getNextPoolable() {
   4672                 return mNext;
   4673             }
   4674 
   4675             public boolean isPooled() {
   4676                 return mIsPooled;
   4677             }
   4678 
   4679             public void setNextPoolable(SomeArgs args) {
   4680                 mNext = args;
   4681             }
   4682 
   4683             public void setPooled(boolean isPooled) {
   4684                 mIsPooled = isPooled;
   4685             }
   4686 
   4687             private void clear() {
   4688                 arg1 = null;
   4689                 arg2 = null;
   4690                 argi1 = 0;
   4691                 argi2 = 0;
   4692                 argi3 = 0;
   4693             }
   4694         }
   4695 
   4696         public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
   4697                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
   4698                 int interrogatingPid, long interrogatingTid) {
   4699             Message message = Message.obtain();
   4700             message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
   4701             message.arg1 = accessibilityId;
   4702             message.arg2 = interactionId;
   4703             message.obj = callback;
   4704             // If the interrogation is performed by the same thread as the main UI
   4705             // thread in this process, set the message as a static reference so
   4706             // after this call completes the same thread but in the interrogating
   4707             // client can handle the message to generate the result.
   4708             if (interrogatingPid == Process.myPid()
   4709                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
   4710                 message.setTarget(ViewRootImpl.this);
   4711                 AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
   4712             } else {
   4713                 sendMessage(message);
   4714             }
   4715         }
   4716 
   4717         public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
   4718             final int accessibilityId = message.arg1;
   4719             final int interactionId = message.arg2;
   4720             final IAccessibilityInteractionConnectionCallback callback =
   4721                 (IAccessibilityInteractionConnectionCallback) message.obj;
   4722 
   4723             AccessibilityNodeInfo info = null;
   4724             try {
   4725                 View target = findViewByAccessibilityId(accessibilityId);
   4726                 if (target != null) {
   4727                     info = target.createAccessibilityNodeInfo();
   4728                 }
   4729             } finally {
   4730                 try {
   4731                     callback.setFindAccessibilityNodeInfoResult(info, interactionId);
   4732                 } catch (RemoteException re) {
   4733                     /* ignore - the other side will time out */
   4734                 }
   4735             }
   4736         }
   4737 
   4738         public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId,
   4739                 IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
   4740                 long interrogatingTid) {
   4741             Message message = Message.obtain();
   4742             message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
   4743             message.arg1 = viewId;
   4744             message.arg2 = interactionId;
   4745             message.obj = callback;
   4746             // If the interrogation is performed by the same thread as the main UI
   4747             // thread in this process, set the message as a static reference so
   4748             // after this call completes the same thread but in the interrogating
   4749             // client can handle the message to generate the result.
   4750             if (interrogatingPid == Process.myPid()
   4751                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
   4752                 message.setTarget(ViewRootImpl.this);
   4753                 AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
   4754             } else {
   4755                 sendMessage(message);
   4756             }
   4757         }
   4758 
   4759         public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
   4760             final int viewId = message.arg1;
   4761             final int interactionId = message.arg2;
   4762             final IAccessibilityInteractionConnectionCallback callback =
   4763                 (IAccessibilityInteractionConnectionCallback) message.obj;
   4764 
   4765             AccessibilityNodeInfo info = null;
   4766             try {
   4767                 View root = ViewRootImpl.this.mView;
   4768                 View target = root.findViewById(viewId);
   4769                 if (target != null && target.getVisibility() == View.VISIBLE) {
   4770                     info = target.createAccessibilityNodeInfo();
   4771                 }
   4772             } finally {
   4773                 try {
   4774                     callback.setFindAccessibilityNodeInfoResult(info, interactionId);
   4775                 } catch (RemoteException re) {
   4776                     /* ignore - the other side will time out */
   4777                 }
   4778             }
   4779         }
   4780 
   4781         public void findAccessibilityNodeInfosByViewTextClientThread(String text,
   4782                 int accessibilityViewId, int interactionId,
   4783                 IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
   4784                 long interrogatingTid) {
   4785             Message message = Message.obtain();
   4786             message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
   4787             SomeArgs args = mPool.acquire();
   4788             args.arg1 = text;
   4789             args.argi1 = accessibilityViewId;
   4790             args.argi2 = interactionId;
   4791             args.arg2 = callback;
   4792             message.obj = args;
   4793             // If the interrogation is performed by the same thread as the main UI
   4794             // thread in this process, set the message as a static reference so
   4795             // after this call completes the same thread but in the interrogating
   4796             // client can handle the message to generate the result.
   4797             if (interrogatingPid == Process.myPid()
   4798                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
   4799                 message.setTarget(ViewRootImpl.this);
   4800                 AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
   4801             } else {
   4802                 sendMessage(message);
   4803             }
   4804         }
   4805 
   4806         public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
   4807             SomeArgs args = (SomeArgs) message.obj;
   4808             final String text = (String) args.arg1;
   4809             final int accessibilityViewId = args.argi1;
   4810             final int interactionId = args.argi2;
   4811             final IAccessibilityInteractionConnectionCallback callback =
   4812                 (IAccessibilityInteractionConnectionCallback) args.arg2;
   4813             mPool.release(args);
   4814 
   4815             List<AccessibilityNodeInfo> infos = null;
   4816             try {
   4817                 ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
   4818                 foundViews.clear();
   4819 
   4820                 View root = null;
   4821                 if (accessibilityViewId != View.NO_ID) {
   4822                     root = findViewByAccessibilityId(accessibilityViewId);
   4823                 } else {
   4824                     root = ViewRootImpl.this.mView;
   4825                 }
   4826 
   4827                 if (root == null || root.getVisibility() != View.VISIBLE) {
   4828                     return;
   4829                 }
   4830 
   4831                 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
   4832                         | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
   4833                 if (foundViews.isEmpty()) {
   4834                     return;
   4835                 }
   4836 
   4837                 infos = mTempAccessibilityNodeInfoList;
   4838                 infos.clear();
   4839 
   4840                 final int viewCount = foundViews.size();
   4841                 for (int i = 0; i < viewCount; i++) {
   4842                     View foundView = foundViews.get(i);
   4843                     if (foundView.getVisibility() == View.VISIBLE) {
   4844                         infos.add(foundView.createAccessibilityNodeInfo());
   4845                     }
   4846                  }
   4847             } finally {
   4848                 try {
   4849                     callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
   4850                 } catch (RemoteException re) {
   4851                     /* ignore - the other side will time out */
   4852                 }
   4853             }
   4854         }
   4855 
   4856         public void performAccessibilityActionClientThread(int accessibilityId, int action,
   4857                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
   4858                 int interogatingPid, long interrogatingTid) {
   4859             Message message = Message.obtain();
   4860             message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
   4861             SomeArgs args = mPool.acquire();
   4862             args.argi1 = accessibilityId;
   4863             args.argi2 = action;
   4864             args.argi3 = interactionId;
   4865             args.arg1 = callback;
   4866             message.obj = args;
   4867             // If the interrogation is performed by the same thread as the main UI
   4868             // thread in this process, set the message as a static reference so
   4869             // after this call completes the same thread but in the interrogating
   4870             // client can handle the message to generate the result.
   4871             if (interogatingPid == Process.myPid()
   4872                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
   4873                 message.setTarget(ViewRootImpl.this);
   4874                 AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
   4875             } else {
   4876                 sendMessage(message);
   4877             }
   4878         }
   4879 
   4880         public void perfromAccessibilityActionUiThread(Message message) {
   4881             SomeArgs args = (SomeArgs) message.obj;
   4882             final int accessibilityId = args.argi1;
   4883             final int action = args.argi2;
   4884             final int interactionId = args.argi3;
   4885             final IAccessibilityInteractionConnectionCallback callback =
   4886                 (IAccessibilityInteractionConnectionCallback) args.arg1;
   4887             mPool.release(args);
   4888 
   4889             boolean succeeded = false;
   4890             try {
   4891                 switch (action) {
   4892                     case AccessibilityNodeInfo.ACTION_FOCUS: {
   4893                         succeeded = performActionFocus(accessibilityId);
   4894                     } break;
   4895                     case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
   4896                         succeeded = performActionClearFocus(accessibilityId);
   4897                     } break;
   4898                     case AccessibilityNodeInfo.ACTION_SELECT: {
   4899                         succeeded = performActionSelect(accessibilityId);
   4900                     } break;
   4901                     case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
   4902                         succeeded = performActionClearSelection(accessibilityId);
   4903                     } break;
   4904                 }
   4905             } finally {
   4906                 try {
   4907                     callback.setPerformAccessibilityActionResult(succeeded, interactionId);
   4908                 } catch (RemoteException re) {
   4909                     /* ignore - the other side will time out */
   4910                 }
   4911             }
   4912         }
   4913 
   4914         private boolean performActionFocus(int accessibilityId) {
   4915             View target = findViewByAccessibilityId(accessibilityId);
   4916             if (target == null || target.getVisibility() != View.VISIBLE) {
   4917                 return false;
   4918             }
   4919             // Get out of touch mode since accessibility wants to move focus around.
   4920             ensureTouchMode(false);
   4921             return target.requestFocus();
   4922         }
   4923 
   4924         private boolean performActionClearFocus(int accessibilityId) {
   4925             View target = findViewByAccessibilityId(accessibilityId);
   4926             if (target == null || target.getVisibility() != View.VISIBLE) {
   4927                 return false;
   4928             }
   4929             if (!target.isFocused()) {
   4930                 return false;
   4931             }
   4932             target.clearFocus();
   4933             return !target.isFocused();
   4934         }
   4935 
   4936         private boolean performActionSelect(int accessibilityId) {
   4937             View target = findViewByAccessibilityId(accessibilityId);
   4938             if (target == null || target.getVisibility() != View.VISIBLE) {
   4939                 return false;
   4940             }
   4941             if (target.isSelected()) {
   4942                 return false;
   4943             }
   4944             target.setSelected(true);
   4945             return target.isSelected();
   4946         }
   4947 
   4948         private boolean performActionClearSelection(int accessibilityId) {
   4949             View target = findViewByAccessibilityId(accessibilityId);
   4950             if (target == null || target.getVisibility() != View.VISIBLE) {
   4951                 return false;
   4952             }
   4953             if (!target.isSelected()) {
   4954                 return false;
   4955             }
   4956             target.setSelected(false);
   4957             return !target.isSelected();
   4958         }
   4959 
   4960         private View findViewByAccessibilityId(int accessibilityId) {
   4961             View root = ViewRootImpl.this.mView;
   4962             if (root == null) {
   4963                 return null;
   4964             }
   4965             View foundView = root.findViewByAccessibilityId(accessibilityId);
   4966             if (foundView != null && foundView.getVisibility() != View.VISIBLE) {
   4967                 return null;
   4968             }
   4969             return foundView;
   4970         }
   4971     }
   4972 
   4973     private class SendWindowContentChangedAccessibilityEvent implements Runnable {
   4974         public volatile boolean mIsPending;
   4975 
   4976         public void run() {
   4977             if (mView != null) {
   4978                 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
   4979                 mIsPending = false;
   4980             }
   4981         }
   4982     }
   4983 }
   4984