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