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