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