auto import from //depot/cupcake/@135843
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
new file mode 100644
index 0000000..48570d2
--- /dev/null
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -0,0 +1,8731 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.LocalPowerManager.CHEEK_EVENT;
+import static android.os.LocalPowerManager.OTHER_EVENT;
+import static android.os.LocalPowerManager.TOUCH_EVENT;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_GPU;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_HARDWARE;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_PUSH_BUFFERS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.policy.PolicyManager;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodClient;
+import com.android.internal.view.IInputMethodManager;
+import com.android.server.KeyInputQueue.QueuedEvent;
+import com.android.server.am.BatteryStatsService;
+
+import android.Manifest;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.BatteryStats;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.LocalPowerManager;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Power;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.TokenWatcher;
+import android.provider.Settings;
+import android.util.Config;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseIntArray;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.IApplicationToken;
+import android.view.IOnKeyguardExitResult;
+import android.view.IRotationWatcher;
+import android.view.IWindow;
+import android.view.IWindowManager;
+import android.view.IWindowSession;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.RawInputEvent;
+import android.view.Surface;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManager.LayoutParams;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Transformation;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/** {@hide} */
+public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor {
+    static final String TAG = "WindowManager";
+    static final boolean DEBUG = false;
+    static final boolean DEBUG_FOCUS = false;
+    static final boolean DEBUG_ANIM = false;
+    static final boolean DEBUG_LAYERS = false;
+    static final boolean DEBUG_INPUT = false;
+    static final boolean DEBUG_INPUT_METHOD = false;
+    static final boolean DEBUG_VISIBILITY = false;
+    static final boolean DEBUG_ORIENTATION = false;
+    static final boolean DEBUG_APP_TRANSITIONS = false;
+    static final boolean DEBUG_STARTING_WINDOW = false;
+    static final boolean DEBUG_REORDER = false;
+    static final boolean SHOW_TRANSACTIONS = false;
+    
+    static final boolean PROFILE_ORIENTATION = false;
+    static final boolean BLUR = true;
+    static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+    
+    static final int LOG_WM_NO_SURFACE_MEMORY = 31000;
+    
+    /** How long to wait for first key repeat, in milliseconds */
+    static final int KEY_REPEAT_FIRST_DELAY = 750;
+    
+    /** How long to wait for subsequent key repeats, in milliseconds */
+    static final int KEY_REPEAT_DELAY = 50;
+
+    /** How much to multiply the policy's type layer, to reserve room
+     * for multiple windows of the same type and Z-ordering adjustment
+     * with TYPE_LAYER_OFFSET. */
+    static final int TYPE_LAYER_MULTIPLIER = 10000;
+    
+    /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
+     * or below others in the same layer. */
+    static final int TYPE_LAYER_OFFSET = 1000;
+    
+    /** How much to increment the layer for each window, to reserve room
+     * for effect surfaces between them.
+     */
+    static final int WINDOW_LAYER_MULTIPLIER = 5;
+    
+    /** The maximum length we will accept for a loaded animation duration:
+     * this is 10 seconds.
+     */
+    static final int MAX_ANIMATION_DURATION = 10*1000;
+
+    /** Amount of time (in milliseconds) to animate the dim surface from one
+     * value to another, when no window animation is driving it.
+     */
+    static final int DEFAULT_DIM_DURATION = 200;
+
+    /** Adjustment to time to perform a dim, to make it more dramatic.
+     */
+    static final int DIM_DURATION_MULTIPLIER = 6;
+    
+    static final int UPDATE_FOCUS_NORMAL = 0;
+    static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
+    static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
+    static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
+    
+    private static final String SYSTEM_SECURE = "ro.secure";
+
+    /**
+     * Condition waited on by {@link #reenableKeyguard} to know the call to
+     * the window policy has finished.
+     */
+    private boolean mWaitingUntilKeyguardReenabled = false;
+
+
+    final TokenWatcher mKeyguardDisabled = new TokenWatcher(
+            new Handler(), "WindowManagerService.mKeyguardDisabled") {
+        public void acquired() {
+            mPolicy.enableKeyguard(false);
+        }
+        public void released() {
+            synchronized (mKeyguardDisabled) {
+                mPolicy.enableKeyguard(true);
+                mWaitingUntilKeyguardReenabled = false;
+                mKeyguardDisabled.notifyAll();
+            }
+        }
+    };
+
+    final Context mContext;
+
+    final boolean mHaveInputMethods;
+    
+    final boolean mLimitedAlphaCompositing;
+    
+    final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
+
+    final IActivityManager mActivityManager;
+    
+    final IBatteryStats mBatteryStats;
+    
+    /**
+     * All currently active sessions with clients.
+     */
+    final HashSet<Session> mSessions = new HashSet<Session>();
+    
+    /**
+     * Mapping from an IWindow IBinder to the server's Window object.
+     * This is also used as the lock for all of our state.
+     */
+    final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>();
+
+    /**
+     * Mapping from a token IBinder to a WindowToken object.
+     */
+    final HashMap<IBinder, WindowToken> mTokenMap =
+            new HashMap<IBinder, WindowToken>();
+
+    /**
+     * The same tokens as mTokenMap, stored in a list for efficient iteration
+     * over them.
+     */
+    final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>();
+    
+    /**
+     * Window tokens that are in the process of exiting, but still
+     * on screen for animations.
+     */
+    final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>();
+
+    /**
+     * Z-ordered (bottom-most first) list of all application tokens, for
+     * controlling the ordering of windows in different applications.  This
+     * contains WindowToken objects.
+     */
+    final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();
+
+    /**
+     * Application tokens that are in the process of exiting, but still
+     * on screen for animations.
+     */
+    final ArrayList<AppWindowToken> mExitingAppTokens = new ArrayList<AppWindowToken>();
+
+    /**
+     * List of window tokens that have finished starting their application,
+     * and now need to have the policy remove their windows.
+     */
+    final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();
+
+    /**
+     * Z-ordered (bottom-most first) list of all Window objects.
+     */
+    final ArrayList mWindows = new ArrayList();
+
+    /**
+     * Windows that are being resized.  Used so we can tell the client about
+     * the resize after closing the transaction in which we resized the
+     * underlying surface.
+     */
+    final ArrayList<WindowState> mResizingWindows = new ArrayList<WindowState>();
+
+    /**
+     * Windows whose animations have ended and now must be removed.
+     */
+    final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>();
+
+    /**
+     * Windows whose surface should be destroyed.
+     */
+    final ArrayList<WindowState> mDestroySurface = new ArrayList<WindowState>();
+
+    /**
+     * Windows that have lost input focus and are waiting for the new
+     * focus window to be displayed before they are told about this.
+     */
+    ArrayList<WindowState> mLosingFocus = new ArrayList<WindowState>();
+
+    /**
+     * This is set when we have run out of memory, and will either be an empty
+     * list or contain windows that need to be force removed.
+     */
+    ArrayList<WindowState> mForceRemoves;
+    
+    IInputMethodManager mInputMethodManager;
+    
+    SurfaceSession mFxSession;
+    Surface mDimSurface;
+    boolean mDimShown;
+    float mDimCurrentAlpha;
+    float mDimTargetAlpha;
+    float mDimDeltaPerMs;
+    long mLastDimAnimTime;
+    Surface mBlurSurface;
+    boolean mBlurShown;
+    
+    int mTransactionSequence = 0;
+    
+    final float[] mTmpFloats = new float[9];
+
+    boolean mSafeMode;
+    boolean mDisplayEnabled = false;
+    boolean mSystemBooted = false;
+    int mRotation = 0;
+    int mRequestedRotation = 0;
+    int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    ArrayList<IRotationWatcher> mRotationWatchers
+            = new ArrayList<IRotationWatcher>();
+    
+    boolean mLayoutNeeded = true;
+    boolean mAnimationPending = false;
+    boolean mDisplayFrozen = false;
+    boolean mWindowsFreezingScreen = false;
+    long mFreezeGcPending = 0;
+    int mAppsFreezingScreen = 0;
+
+    // This is held as long as we have the screen frozen, to give us time to
+    // perform a rotation animation when turning off shows the lock screen which
+    // changes the orientation.
+    PowerManager.WakeLock mScreenFrozenLock;
+    
+    // State management of app transitions.  When we are preparing for a
+    // transition, mNextAppTransition will be the kind of transition to
+    // perform or TRANSIT_NONE if we are not waiting.  If we are waiting,
+    // mOpeningApps and mClosingApps are the lists of tokens that will be
+    // made visible or hidden at the next transition.
+    int mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+    boolean mAppTransitionReady = false;
+    boolean mAppTransitionTimeout = false;
+    boolean mStartingIconInTransition = false;
+    boolean mSkipAppTransitionAnimation = false;
+    final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
+    final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
+    
+    //flag to detect fat touch events
+    boolean mFatTouch = false;
+    Display mDisplay;
+    
+    H mH = new H();
+
+    WindowState mCurrentFocus = null;
+    WindowState mLastFocus = null;
+    
+    // This just indicates the window the input method is on top of, not
+    // necessarily the window its input is going to.
+    WindowState mInputMethodTarget = null;
+    WindowState mUpcomingInputMethodTarget = null;
+    boolean mInputMethodTargetWaitingAnim;
+    int mInputMethodAnimLayerAdjustment;
+    
+    WindowState mInputMethodWindow = null;
+    final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
+
+    AppWindowToken mFocusedApp = null;
+
+    PowerManagerService mPowerManager;
+    
+    float mWindowAnimationScale = 1.0f;
+    float mTransitionAnimationScale = 1.0f;
+    
+    final KeyWaiter mKeyWaiter = new KeyWaiter();
+    final KeyQ mQueue;
+    final InputDispatcherThread mInputThread;
+
+    // Who is holding the screen on.
+    Session mHoldingScreenOn;
+    
+    /**
+     * Whether the UI is currently running in touch mode (not showing
+     * navigational focus because the user is directly pressing the screen).
+     */
+    boolean mInTouchMode = false;
+
+    private ViewServer mViewServer;
+
+    final Rect mTempRect = new Rect();
+    
+    public static WindowManagerService main(Context context,
+            PowerManagerService pm, boolean haveInputMethods) {
+        WMThread thr = new WMThread(context, pm, haveInputMethods);
+        thr.start();
+        
+        synchronized (thr) {
+            while (thr.mService == null) {
+                try {
+                    thr.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        
+        return thr.mService;
+    }
+    
+    static class WMThread extends Thread {
+        WindowManagerService mService;
+        
+        private final Context mContext;
+        private final PowerManagerService mPM;
+        private final boolean mHaveInputMethods;
+        
+        public WMThread(Context context, PowerManagerService pm,
+                boolean haveInputMethods) {
+            super("WindowManager");
+            mContext = context;
+            mPM = pm;
+            mHaveInputMethods = haveInputMethods;
+        }
+        
+        public void run() {
+            Looper.prepare();
+            WindowManagerService s = new WindowManagerService(mContext, mPM,
+                    mHaveInputMethods);
+            android.os.Process.setThreadPriority(
+                    android.os.Process.THREAD_PRIORITY_DISPLAY);
+            
+            synchronized (this) {
+                mService = s;
+                notifyAll();
+            }
+            
+            Looper.loop();
+        }
+    }
+
+    static class PolicyThread extends Thread {
+        private final WindowManagerPolicy mPolicy;
+        private final WindowManagerService mService;
+        private final Context mContext;
+        private final PowerManagerService mPM;
+        boolean mRunning = false;
+        
+        public PolicyThread(WindowManagerPolicy policy,
+                WindowManagerService service, Context context,
+                PowerManagerService pm) {
+            super("WindowManagerPolicy");
+            mPolicy = policy;
+            mService = service;
+            mContext = context;
+            mPM = pm;
+        }
+        
+        public void run() {
+            Looper.prepare();
+            //Looper.myLooper().setMessageLogging(new LogPrinter(
+            //        Log.VERBOSE, "WindowManagerPolicy"));
+            android.os.Process.setThreadPriority(
+                    android.os.Process.THREAD_PRIORITY_FOREGROUND);
+            mPolicy.init(mContext, mService, mPM);
+            
+            synchronized (this) {
+                mRunning = true;
+                notifyAll();
+            }
+            
+            Looper.loop();
+        }
+    }
+
+    private WindowManagerService(Context context, PowerManagerService pm,
+            boolean haveInputMethods) {
+        mContext = context;
+        mHaveInputMethods = haveInputMethods;
+        mLimitedAlphaCompositing = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_sf_limitedAlpha);
+        
+        mPowerManager = pm;
+        mPowerManager.setPolicy(mPolicy);
+        PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mScreenFrozenLock = pmc.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                "SCREEN_FROZEN");
+        mScreenFrozenLock.setReferenceCounted(false);
+
+        mActivityManager = ActivityManagerNative.getDefault();
+        mBatteryStats = BatteryStatsService.getService();
+
+        // Get persisted window scale setting
+        mWindowAnimationScale = Settings.System.getFloat(context.getContentResolver(),
+                Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
+        mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
+                Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+        
+        mQueue = new KeyQ();
+
+        mInputThread = new InputDispatcherThread();
+        
+        PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
+        thr.start();
+        
+        synchronized (thr) {
+            while (!thr.mRunning) {
+                try {
+                    thr.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        
+        mInputThread.start();
+        
+        // Add ourself to the Watchdog monitors.
+        Watchdog.getInstance().addMonitor(this);
+    }
+
+    @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            // The window manager only throws security exceptions, so let's
+            // log all others.
+            if (!(e instanceof SecurityException)) {
+                Log.e(TAG, "Window Manager Crash", e);
+            }
+            throw e;
+        }
+    }
+
+    private void placeWindowAfter(Object pos, WindowState window) {
+        final int i = mWindows.indexOf(pos);
+        if (localLOGV || DEBUG_FOCUS) Log.v(
+            TAG, "Adding window " + window + " at "
+            + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
+        mWindows.add(i+1, window);
+    }
+
+    private void placeWindowBefore(Object pos, WindowState window) {
+        final int i = mWindows.indexOf(pos);
+        if (localLOGV || DEBUG_FOCUS) Log.v(
+            TAG, "Adding window " + window + " at "
+            + i + " of " + mWindows.size() + " (before " + pos + ")");
+        mWindows.add(i, window);
+    }
+
+    //This method finds out the index of a window that has the same app token as
+    //win. used for z ordering the windows in mWindows
+    private int findIdxBasedOnAppTokens(WindowState win) {
+        //use a local variable to cache mWindows
+        ArrayList localmWindows = mWindows;
+        int jmax = localmWindows.size();
+        if(jmax == 0) {
+            return -1;
+        }
+        for(int j = (jmax-1); j >= 0; j--) {
+            WindowState wentry = (WindowState)localmWindows.get(j);
+            if(wentry.mAppToken == win.mAppToken) {
+                return j;
+            }
+        }
+        return -1;
+    }
+    
+    private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
+        final IWindow client = win.mClient;
+        final WindowToken token = win.mToken;
+        final ArrayList localmWindows = mWindows;
+        
+        final int N = localmWindows.size();
+        final WindowState attached = win.mAttachedWindow;
+        int i;
+        if (attached == null) {
+            int tokenWindowsPos = token.windows.size();
+            if (token.appWindowToken != null) {
+                int index = tokenWindowsPos-1;
+                if (index >= 0) {
+                    // If this application has existing windows, we
+                    // simply place the new window on top of them... but
+                    // keep the starting window on top.
+                    if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
+                        // Base windows go behind everything else.
+                        placeWindowBefore(token.windows.get(0), win);
+                        tokenWindowsPos = 0;
+                    } else {
+                        AppWindowToken atoken = win.mAppToken;
+                        if (atoken != null &&
+                                token.windows.get(index) == atoken.startingWindow) {
+                            placeWindowBefore(token.windows.get(index), win);
+                            tokenWindowsPos--;
+                        } else {
+                            int newIdx =  findIdxBasedOnAppTokens(win);
+                            if(newIdx != -1) {
+                                //there is a window above this one associated with the same 
+                                //apptoken note that the window could be a floating window 
+                                //that was created later or a window at the top of the list of 
+                                //windows associated with this token.
+                                localmWindows.add(newIdx+1, win);
+                            } 
+                        }
+                    }
+                } else {
+                    if (localLOGV) Log.v(
+                        TAG, "Figuring out where to add app window "
+                        + client.asBinder() + " (token=" + token + ")");
+                    // Figure out where the window should go, based on the
+                    // order of applications.
+                    final int NA = mAppTokens.size();
+                    Object pos = null;
+                    for (i=NA-1; i>=0; i--) {
+                        AppWindowToken t = mAppTokens.get(i);
+                        if (t == token) {
+                            i--;
+                            break;
+                        }
+                        if (t.windows.size() > 0) {
+                            pos = t.windows.get(0);
+                        }
+                    }
+                    // We now know the index into the apps.  If we found
+                    // an app window above, that gives us the position; else
+                    // we need to look some more.
+                    if (pos != null) {
+                        // Move behind any windows attached to this one.
+                        WindowToken atoken = 
+                            mTokenMap.get(((WindowState)pos).mClient.asBinder());
+                        if (atoken != null) {
+                            final int NC = atoken.windows.size();
+                            if (NC > 0) {
+                                WindowState bottom = atoken.windows.get(0);
+                                if (bottom.mSubLayer < 0) {
+                                    pos = bottom;
+                                }
+                            }
+                        }
+                        placeWindowBefore(pos, win);
+                    } else {
+                        while (i >= 0) {
+                            AppWindowToken t = mAppTokens.get(i);
+                            final int NW = t.windows.size();
+                            if (NW > 0) {
+                                pos = t.windows.get(NW-1);
+                                break;
+                            }
+                            i--;
+                        }
+                        if (pos != null) {
+                            // Move in front of any windows attached to this
+                            // one.
+                            WindowToken atoken =
+                                mTokenMap.get(((WindowState)pos).mClient.asBinder());
+                            if (atoken != null) {
+                                final int NC = atoken.windows.size();
+                                if (NC > 0) {
+                                    WindowState top = atoken.windows.get(NC-1);
+                                    if (top.mSubLayer >= 0) {
+                                        pos = top;
+                                    }
+                                }
+                            }
+                            placeWindowAfter(pos, win);
+                        } else {
+                            // Just search for the start of this layer.
+                            final int myLayer = win.mBaseLayer;
+                            for (i=0; i<N; i++) {
+                                WindowState w = (WindowState)localmWindows.get(i);
+                                if (w.mBaseLayer > myLayer) {
+                                    break;
+                                }
+                            }
+                            if (localLOGV || DEBUG_FOCUS) Log.v(
+                                TAG, "Adding window " + win + " at "
+                                + i + " of " + N);
+                            localmWindows.add(i, win);
+                        }
+                    }
+                }
+            } else {
+                // Figure out where window should go, based on layer.
+                final int myLayer = win.mBaseLayer;
+                for (i=N-1; i>=0; i--) {
+                    if (((WindowState)localmWindows.get(i)).mBaseLayer <= myLayer) {
+                        i++;
+                        break;
+                    }
+                }
+                if (i < 0) i = 0;
+                if (localLOGV || DEBUG_FOCUS) Log.v(
+                    TAG, "Adding window " + win + " at "
+                    + i + " of " + N);
+                localmWindows.add(i, win);
+            }
+            if (addToToken) {
+                token.windows.add(tokenWindowsPos, win);
+            }
+
+        } else {
+            // Figure out this window's ordering relative to the window
+            // it is attached to.
+            final int NA = token.windows.size();
+            final int sublayer = win.mSubLayer;
+            int largestSublayer = Integer.MIN_VALUE;
+            WindowState windowWithLargestSublayer = null;
+            for (i=0; i<NA; i++) {
+                WindowState w = token.windows.get(i);
+                final int wSublayer = w.mSubLayer;
+                if (wSublayer >= largestSublayer) {
+                    largestSublayer = wSublayer;
+                    windowWithLargestSublayer = w;
+                }
+                if (sublayer < 0) {
+                    // For negative sublayers, we go below all windows
+                    // in the same sublayer.
+                    if (wSublayer >= sublayer) {
+                        if (addToToken) {
+                            token.windows.add(i, win);
+                        }
+                        placeWindowBefore(
+                            wSublayer >= 0 ? attached : w, win);
+                        break;
+                    }
+                } else {
+                    // For positive sublayers, we go above all windows
+                    // in the same sublayer.
+                    if (wSublayer > sublayer) {
+                        if (addToToken) {
+                            token.windows.add(i, win);
+                        }
+                        placeWindowBefore(w, win);
+                        break;
+                    }
+                }
+            }
+            if (i >= NA) {
+                if (addToToken) {
+                    token.windows.add(win);
+                }
+                if (sublayer < 0) {
+                    placeWindowBefore(attached, win);
+                } else {
+                    placeWindowAfter(largestSublayer >= 0
+                                     ? windowWithLargestSublayer
+                                     : attached,
+                                     win);
+                }
+            }
+        }
+        
+        if (win.mAppToken != null && addToToken) {
+            win.mAppToken.allAppWindows.add(win);
+        }
+    }
+    
+    static boolean canBeImeTarget(WindowState w) {
+        final int fl = w.mAttrs.flags
+                & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
+        if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) {
+            return w.isVisibleOrAdding();
+        }
+        return false;
+    }
+    
+    int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
+        final ArrayList localmWindows = mWindows;
+        final int N = localmWindows.size();
+        WindowState w = null;
+        int i = N;
+        while (i > 0) {
+            i--;
+            w = (WindowState)localmWindows.get(i);
+            
+            //Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x"
+            //        + Integer.toHexString(w.mAttrs.flags));
+            if (canBeImeTarget(w)) {
+                //Log.i(TAG, "Putting input method here!");
+                
+                // Yet more tricksyness!  If this window is a "starting"
+                // window, we do actually want to be on top of it, but
+                // it is not -really- where input will go.  So if the caller
+                // is not actually looking to move the IME, look down below
+                // for a real window to target...
+                if (!willMove
+                        && w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
+                        && i > 0) {
+                    WindowState wb = (WindowState)localmWindows.get(i-1);
+                    if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
+                        i--;
+                        w = wb;
+                    }
+                }
+                break;
+            }
+        }
+        
+        mUpcomingInputMethodTarget = w;
+        
+        if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target="
+                + w + " willMove=" + willMove);
+        
+        if (willMove && w != null) {
+            final WindowState curTarget = mInputMethodTarget;
+            if (curTarget != null && curTarget.mAppToken != null) {
+                
+                // Now some fun for dealing with window animations that
+                // modify the Z order.  We need to look at all windows below
+                // the current target that are in this app, finding the highest
+                // visible one in layering.
+                AppWindowToken token = curTarget.mAppToken;
+                WindowState highestTarget = null;
+                int highestPos = 0;
+                if (token.animating || token.animation != null) {
+                    int pos = 0;
+                    pos = localmWindows.indexOf(curTarget);
+                    while (pos >= 0) {
+                        WindowState win = (WindowState)localmWindows.get(pos);
+                        if (win.mAppToken != token) {
+                            break;
+                        }
+                        if (!win.mRemoved) {
+                            if (highestTarget == null || win.mAnimLayer >
+                                    highestTarget.mAnimLayer) {
+                                highestTarget = win;
+                                highestPos = pos;
+                            }
+                        }
+                        pos--;
+                    }
+                }
+                
+                if (highestTarget != null) {
+                    if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" 
+                            + mNextAppTransition + " " + highestTarget
+                            + " animating=" + highestTarget.isAnimating()
+                            + " layer=" + highestTarget.mAnimLayer
+                            + " new layer=" + w.mAnimLayer);
+                    
+                    if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+                        // If we are currently setting up for an animation,
+                        // hold everything until we can find out what will happen.
+                        mInputMethodTargetWaitingAnim = true;
+                        mInputMethodTarget = highestTarget;
+                        return highestPos + 1;
+                    } else if (highestTarget.isAnimating() &&
+                            highestTarget.mAnimLayer > w.mAnimLayer) {
+                        // If the window we are currently targeting is involved
+                        // with an animation, and it is on top of the next target
+                        // we will be over, then hold off on moving until
+                        // that is done.
+                        mInputMethodTarget = highestTarget;
+                        return highestPos + 1;
+                    }
+                }
+            }
+        }
+        
+        //Log.i(TAG, "Placing input method @" + (i+1));
+        if (w != null) {
+            if (willMove) {
+                RuntimeException e = new RuntimeException();
+                e.fillInStackTrace();
+                if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+                        + mInputMethodTarget + " to " + w, e);
+                mInputMethodTarget = w;
+                if (w.mAppToken != null) {
+                    setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment);
+                } else {
+                    setInputMethodAnimLayerAdjustment(0);
+                }
+            }
+            return i+1;
+        }
+        if (willMove) {
+            RuntimeException e = new RuntimeException();
+            e.fillInStackTrace();
+            if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+                    + mInputMethodTarget + " to null", e);
+            mInputMethodTarget = null;
+            setInputMethodAnimLayerAdjustment(0);
+        }
+        return -1;
+    }
+    
+    void addInputMethodWindowToListLocked(WindowState win) {
+        int pos = findDesiredInputMethodWindowIndexLocked(true);
+        if (pos >= 0) {
+            win.mTargetAppToken = mInputMethodTarget.mAppToken;
+            mWindows.add(pos, win);
+            moveInputMethodDialogsLocked(pos+1);
+            return;
+        }
+        win.mTargetAppToken = null;
+        addWindowToListInOrderLocked(win, true);
+        moveInputMethodDialogsLocked(pos);
+    }
+    
+    void setInputMethodAnimLayerAdjustment(int adj) {
+        if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj);
+        mInputMethodAnimLayerAdjustment = adj;
+        WindowState imw = mInputMethodWindow;
+        if (imw != null) {
+            imw.mAnimLayer = imw.mLayer + adj;
+            if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw
+                    + " anim layer: " + imw.mAnimLayer);
+            int wi = imw.mChildWindows.size();
+            while (wi > 0) {
+                wi--;
+                WindowState cw = (WindowState)imw.mChildWindows.get(wi);
+                cw.mAnimLayer = cw.mLayer + adj;
+                if (DEBUG_LAYERS) Log.v(TAG, "IM win " + cw
+                        + " anim layer: " + cw.mAnimLayer);
+            }
+        }
+        int di = mInputMethodDialogs.size();
+        while (di > 0) {
+            di --;
+            imw = mInputMethodDialogs.get(di);
+            imw.mAnimLayer = imw.mLayer + adj;
+            if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw
+                    + " anim layer: " + imw.mAnimLayer);
+        }
+    }
+    
+    private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
+        int wpos = mWindows.indexOf(win);
+        if (wpos >= 0) {
+            if (wpos < interestingPos) interestingPos--;
+            mWindows.remove(wpos);
+            int NC = win.mChildWindows.size();
+            while (NC > 0) {
+                NC--;
+                WindowState cw = (WindowState)win.mChildWindows.get(NC);
+                int cpos = mWindows.indexOf(cw);
+                if (cpos >= 0) {
+                    if (cpos < interestingPos) interestingPos--;
+                    mWindows.remove(cpos);
+                }
+            }
+        }
+        return interestingPos;
+    }
+    
+    private void reAddWindowToListInOrderLocked(WindowState win) {
+        addWindowToListInOrderLocked(win, false);
+        // This is a hack to get all of the child windows added as well
+        // at the right position.  Child windows should be rare and
+        // this case should be rare, so it shouldn't be that big a deal.
+        int wpos = mWindows.indexOf(win);
+        if (wpos >= 0) {
+            mWindows.remove(wpos);
+            reAddWindowLocked(wpos, win);
+        }
+    }
+    
+    void logWindowList(String prefix) {
+        int N = mWindows.size();
+        while (N > 0) {
+            N--;
+            Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N));
+        }
+    }
+    
+    void moveInputMethodDialogsLocked(int pos) {
+        ArrayList<WindowState> dialogs = mInputMethodDialogs;
+        
+        final int N = dialogs.size();
+        if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos);
+        for (int i=0; i<N; i++) {
+            pos = tmpRemoveWindowLocked(pos, dialogs.get(i));
+        }
+        if (DEBUG_INPUT_METHOD) {
+            Log.v(TAG, "Window list w/pos=" + pos);
+            logWindowList("  ");
+        }
+        
+        if (pos >= 0) {
+            final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
+            if (pos < mWindows.size()) {
+                WindowState wp = (WindowState)mWindows.get(pos);
+                if (wp == mInputMethodWindow) {
+                    pos++;
+                }
+            }
+            if (DEBUG_INPUT_METHOD) Log.v(TAG, "Adding " + N + " dialogs at pos=" + pos);
+            for (int i=0; i<N; i++) {
+                WindowState win = dialogs.get(i);
+                win.mTargetAppToken = targetAppToken;
+                pos = reAddWindowLocked(pos, win);
+            }
+            if (DEBUG_INPUT_METHOD) {
+                Log.v(TAG, "Final window list:");
+                logWindowList("  ");
+            }
+            return;
+        }
+        for (int i=0; i<N; i++) {
+            WindowState win = dialogs.get(i);
+            win.mTargetAppToken = null;
+            reAddWindowToListInOrderLocked(win);
+            if (DEBUG_INPUT_METHOD) {
+                Log.v(TAG, "No IM target, final list:");
+                logWindowList("  ");
+            }
+        }
+    }
+    
+    boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) {
+        final WindowState imWin = mInputMethodWindow;
+        final int DN = mInputMethodDialogs.size();
+        if (imWin == null && DN == 0) {
+            return false;
+        }
+        
+        int imPos = findDesiredInputMethodWindowIndexLocked(true);
+        if (imPos >= 0) {
+            // In this case, the input method windows are to be placed
+            // immediately above the window they are targeting.
+            
+            // First check to see if the input method windows are already
+            // located here, and contiguous.
+            final int N = mWindows.size();
+            WindowState firstImWin = imPos < N
+                    ? (WindowState)mWindows.get(imPos) : null;
+                    
+            // Figure out the actual input method window that should be
+            // at the bottom of their stack.
+            WindowState baseImWin = imWin != null
+                    ? imWin : mInputMethodDialogs.get(0);
+            if (baseImWin.mChildWindows.size() > 0) {
+                WindowState cw = (WindowState)baseImWin.mChildWindows.get(0);
+                if (cw.mSubLayer < 0) baseImWin = cw;
+            }
+            
+            if (firstImWin == baseImWin) {
+                // The windows haven't moved...  but are they still contiguous?
+                // First find the top IM window.
+                int pos = imPos+1;
+                while (pos < N) {
+                    if (!((WindowState)mWindows.get(pos)).mIsImWindow) {
+                        break;
+                    }
+                    pos++;
+                }
+                pos++;
+                // Now there should be no more input method windows above.
+                while (pos < N) {
+                    if (((WindowState)mWindows.get(pos)).mIsImWindow) {
+                        break;
+                    }
+                    pos++;
+                }
+                if (pos >= N) {
+                    // All is good!
+                    return false;
+                }
+            }
+            
+            if (imWin != null) {
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "Moving IM from " + imPos);
+                    logWindowList("  ");
+                }
+                imPos = tmpRemoveWindowLocked(imPos, imWin);
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "List after moving with new pos " + imPos + ":");
+                    logWindowList("  ");
+                }
+                imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
+                reAddWindowLocked(imPos, imWin);
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "List after moving IM to " + imPos + ":");
+                    logWindowList("  ");
+                }
+                if (DN > 0) moveInputMethodDialogsLocked(imPos+1);
+            } else {
+                moveInputMethodDialogsLocked(imPos);
+            }
+            
+        } else {
+            // In this case, the input method windows go in a fixed layer,
+            // because they aren't currently associated with a focus window.
+            
+            if (imWin != null) {
+                if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos);
+                tmpRemoveWindowLocked(0, imWin);
+                imWin.mTargetAppToken = null;
+                reAddWindowToListInOrderLocked(imWin);
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "List with no IM target:");
+                    logWindowList("  ");
+                }
+                if (DN > 0) moveInputMethodDialogsLocked(-1);;
+            } else {
+                moveInputMethodDialogsLocked(-1);;
+            }
+            
+        }
+        
+        if (needAssignLayers) {
+            assignLayersLocked();
+        }
+        
+        return true;
+    }
+    
+    void adjustInputMethodDialogsLocked() {
+        moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
+    }
+    
+    public int addWindow(Session session, IWindow client,
+            WindowManager.LayoutParams attrs, int viewVisibility,
+            Rect outContentInsets) {
+        int res = mPolicy.checkAddPermission(attrs);
+        if (res != WindowManagerImpl.ADD_OKAY) {
+            return res;
+        }
+        
+        boolean reportNewConfig = false;
+        WindowState attachedWindow = null;
+        WindowState win = null;
+        
+        synchronized(mWindowMap) {
+            // Instantiating a Display requires talking with the simulator,
+            // so don't do it until we know the system is mostly up and
+            // running.
+            if (mDisplay == null) {
+                WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+                mDisplay = wm.getDefaultDisplay();
+                mQueue.setDisplay(mDisplay);
+                reportNewConfig = true;
+            }
+            
+            if (mWindowMap.containsKey(client.asBinder())) {
+                Log.w(TAG, "Window " + client + " is already added");
+                return WindowManagerImpl.ADD_DUPLICATE_ADD;
+            }
+
+            if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {
+                attachedWindow = windowForClientLocked(null, attrs.token); 
+                if (attachedWindow == null) {
+                    Log.w(TAG, "Attempted to add window with token that is not a window: "
+                          + attrs.token + ".  Aborting.");
+                    return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
+                }
+                if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
+                        && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
+                    Log.w(TAG, "Attempted to add window with token that is a sub-window: "
+                            + attrs.token + ".  Aborting.");
+                    return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
+                }
+            }
+
+            boolean addToken = false;
+            WindowToken token = mTokenMap.get(attrs.token);
+            if (token == null) {
+                if (attrs.type >= FIRST_APPLICATION_WINDOW
+                        && attrs.type <= LAST_APPLICATION_WINDOW) {
+                    Log.w(TAG, "Attempted to add application window with unknown token "
+                          + attrs.token + ".  Aborting.");
+                    return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+                }
+                if (attrs.type == TYPE_INPUT_METHOD) {
+                    Log.w(TAG, "Attempted to add input method window with unknown token "
+                          + attrs.token + ".  Aborting.");
+                    return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+                }
+                token = new WindowToken(attrs.token, -1, false);
+                addToken = true;
+            } else if (attrs.type >= FIRST_APPLICATION_WINDOW
+                    && attrs.type <= LAST_APPLICATION_WINDOW) {
+                AppWindowToken atoken = token.appWindowToken;
+                if (atoken == null) {
+                    Log.w(TAG, "Attempted to add window with non-application token "
+                          + token + ".  Aborting.");
+                    return WindowManagerImpl.ADD_NOT_APP_TOKEN;
+                } else if (atoken.removed) {
+                    Log.w(TAG, "Attempted to add window with exiting application token "
+                          + token + ".  Aborting.");
+                    return WindowManagerImpl.ADD_APP_EXITING;
+                }
+                if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
+                    // No need for this guy!
+                    if (localLOGV) Log.v(
+                            TAG, "**** NO NEED TO START: " + attrs.getTitle());
+                    return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;
+                }
+            } else if (attrs.type == TYPE_INPUT_METHOD) {
+                if (token.windowType != TYPE_INPUT_METHOD) {
+                    Log.w(TAG, "Attempted to add input method window with bad token "
+                            + attrs.token + ".  Aborting.");
+                      return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+                }
+            }
+
+            win = new WindowState(session, client, token,
+                    attachedWindow, attrs, viewVisibility);
+            if (win.mDeathRecipient == null) {
+                // Client has apparently died, so there is no reason to
+                // continue.
+                Log.w(TAG, "Adding window client " + client.asBinder()
+                        + " that is dead, aborting.");
+                return WindowManagerImpl.ADD_APP_EXITING;
+            }
+
+            mPolicy.adjustWindowParamsLw(win.mAttrs);
+            
+            res = mPolicy.prepareAddWindowLw(win, attrs);
+            if (res != WindowManagerImpl.ADD_OKAY) {
+                return res;
+            }
+
+            // From now on, no exceptions or errors allowed!
+
+            res = WindowManagerImpl.ADD_OKAY;
+            
+            final long origId = Binder.clearCallingIdentity();
+            
+            if (addToken) {
+                mTokenMap.put(attrs.token, token);
+                mTokenList.add(token);
+            }
+            win.attach();
+            mWindowMap.put(client.asBinder(), win);
+
+            if (attrs.type == TYPE_APPLICATION_STARTING &&
+                    token.appWindowToken != null) {
+                token.appWindowToken.startingWindow = win;
+            }
+
+            boolean imMayMove = true;
+            
+            if (attrs.type == TYPE_INPUT_METHOD) {
+                mInputMethodWindow = win;
+                addInputMethodWindowToListLocked(win);
+                imMayMove = false;
+            } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {
+                mInputMethodDialogs.add(win);
+                addWindowToListInOrderLocked(win, true);
+                adjustInputMethodDialogsLocked();
+                imMayMove = false;
+            } else {
+                addWindowToListInOrderLocked(win, true);
+            }
+            
+            win.mEnterAnimationPending = true;
+            
+            mPolicy.getContentInsetHintLw(attrs, outContentInsets);
+            
+            if (mInTouchMode) {
+                res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE;
+            }
+            if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) {
+                res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE;
+            }
+            
+            if (win.canReceiveKeys()) {
+                if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS)) {
+                    imMayMove = false;
+                }
+            }
+            
+            if (imMayMove) {
+                moveInputMethodWindowsIfNeededLocked(false);                
+            }
+            
+            assignLayersLocked();
+            // Don't do layout here, the window must call
+            // relayout to be displayed, so we'll do it there.
+            
+            //dump();
+
+            if (localLOGV) Log.v(
+                TAG, "New client " + client.asBinder()
+                + ": window=" + win);
+        }
+
+        // sendNewConfiguration() checks caller permissions so we must call it with
+        // privilege.  updateOrientationFromAppTokens() clears and resets the caller
+        // identity anyway, so it's safe to just clear & restore around this whole
+        // block.
+        final long origId = Binder.clearCallingIdentity();
+        if (reportNewConfig) {
+            sendNewConfiguration();
+        } else {
+            // Update Orientation after adding a window, only if the window needs to be
+            // displayed right away
+            if (win.isVisibleOrAdding()) {
+                if (updateOrientationFromAppTokens(null) != null) {
+                    sendNewConfiguration();
+                }
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+        
+        return res;
+    }
+    
+    public void removeWindow(Session session, IWindow client) {
+        synchronized(mWindowMap) {
+            WindowState win = windowForClientLocked(session, client);
+            if (win == null) {
+                return;
+            }
+            removeWindowLocked(session, win);
+        }
+    }
+    
+    public void removeWindowLocked(Session session, WindowState win) {
+
+        if (localLOGV || DEBUG_FOCUS) Log.v(
+            TAG, "Remove " + win + " client="
+            + Integer.toHexString(System.identityHashCode(
+                win.mClient.asBinder()))
+            + ", surface=" + win.mSurface);
+
+        final long origId = Binder.clearCallingIdentity();
+        
+        if (DEBUG_APP_TRANSITIONS) Log.v(
+                TAG, "Remove " + win + ": mSurface=" + win.mSurface
+                + " mExiting=" + win.mExiting
+                + " isAnimating=" + win.isAnimating()
+                + " app-animation="
+                + (win.mAppToken != null ? win.mAppToken.animation : null)
+                + " inPendingTransaction="
+                + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+                + " mDisplayFrozen=" + mDisplayFrozen);
+        // Visibility of the removed window. Will be used later to update orientation later on.
+        boolean wasVisible = false;
+        // First, see if we need to run an animation.  If we do, we have
+        // to hold off on removing the window until the animation is done.
+        // If the display is frozen, just remove immediately, since the
+        // animation wouldn't be seen.
+        if (win.mSurface != null && !mDisplayFrozen) {
+            // If we are not currently running the exit animation, we
+            // need to see about starting one.
+            if (wasVisible=win.isWinVisibleLw()) {
+                
+                int transit = WindowManagerPolicy.TRANSIT_EXIT;
+                if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
+                    transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+                }
+                // Try starting an animation.
+                if (applyAnimationLocked(win, transit, false)) {
+                    win.mExiting = true;
+                }
+            }
+            if (win.mExiting || win.isAnimating()) {
+                // The exit animation is running... wait for it!
+                //Log.i(TAG, "*** Running exit animation...");
+                win.mExiting = true;
+                win.mRemoveOnExit = true;
+                mLayoutNeeded = true;
+                updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+                performLayoutAndPlaceSurfacesLocked();
+                if (win.mAppToken != null) {
+                    win.mAppToken.updateReportedVisibilityLocked();
+                }
+                //dump();
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
+        }
+
+        removeWindowInnerLocked(session, win);
+        // Removing a visible window will effect the computed orientation
+        // So just update orientation if needed.
+        if (wasVisible) {
+            if (updateOrientationFromAppTokens(null) != null) {
+                sendNewConfiguration();
+            }
+        }
+        updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+        Binder.restoreCallingIdentity(origId);
+    }
+    
+    private void removeWindowInnerLocked(Session session, WindowState win) {
+        mKeyWaiter.releasePendingPointerLocked(win.mSession);
+        mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+        
+        win.mRemoved = true;
+        
+        if (mInputMethodTarget == win) {
+            moveInputMethodWindowsIfNeededLocked(false);
+        }
+        
+        mPolicy.removeWindowLw(win);
+        win.removeLocked();
+
+        mWindowMap.remove(win.mClient.asBinder());
+        mWindows.remove(win);
+
+        if (mInputMethodWindow == win) {
+            mInputMethodWindow = null;
+        } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+            mInputMethodDialogs.remove(win);
+        }
+        
+        final WindowToken token = win.mToken;
+        final AppWindowToken atoken = win.mAppToken;
+        token.windows.remove(win);
+        if (atoken != null) {
+            atoken.allAppWindows.remove(win);
+        }
+        if (localLOGV) Log.v(
+                TAG, "**** Removing window " + win + ": count="
+                + token.windows.size());
+        if (token.windows.size() == 0) {
+            if (!token.explicit) {
+                mTokenMap.remove(token.token);
+                mTokenList.remove(token);
+            } else if (atoken != null) {
+                atoken.firstWindowDrawn = false;
+            }
+        }
+
+        if (atoken != null) {
+            if (atoken.startingWindow == win) {
+                atoken.startingWindow = null;
+            } else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
+                // If this is the last window and we had requested a starting
+                // transition window, well there is no point now.
+                atoken.startingData = null;
+            } else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) {
+                // If this is the last window except for a starting transition
+                // window, we need to get rid of the starting transition.
+                if (DEBUG_STARTING_WINDOW) {
+                    Log.v(TAG, "Schedule remove starting " + token
+                            + ": no more real windows");
+                }
+                Message m = mH.obtainMessage(H.REMOVE_STARTING, atoken);
+                mH.sendMessage(m);
+            }
+        }
+        
+        if (!mInLayout) {
+            assignLayersLocked();
+            mLayoutNeeded = true;
+            performLayoutAndPlaceSurfacesLocked();
+            if (win.mAppToken != null) {
+                win.mAppToken.updateReportedVisibilityLocked();
+            }
+        }
+    }
+
+    private void setTransparentRegionWindow(Session session, IWindow client, Region region) {
+        long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mWindowMap) {
+                WindowState w = windowForClientLocked(session, client);
+                if ((w != null) && (w.mSurface != null)) {
+                    Surface.openTransaction();
+                    try {
+                        w.mSurface.setTransparentRegionHint(region);
+                    } finally {
+                        Surface.closeTransaction();
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    void setInsetsWindow(Session session, IWindow client,
+            int touchableInsets, Rect contentInsets, 
+            Rect visibleInsets) {
+        long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mWindowMap) {
+                WindowState w = windowForClientLocked(session, client);
+                if (w != null) {
+                    w.mGivenInsetsPending = false;
+                    w.mGivenContentInsets.set(contentInsets);
+                    w.mGivenVisibleInsets.set(visibleInsets);
+                    w.mTouchableInsets = touchableInsets;
+                    mLayoutNeeded = true;
+                    performLayoutAndPlaceSurfacesLocked();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+    
+    public void getWindowDisplayFrame(Session session, IWindow client,
+            Rect outDisplayFrame) {
+        synchronized(mWindowMap) {
+            WindowState win = windowForClientLocked(session, client);
+            if (win == null) {
+                outDisplayFrame.setEmpty();
+                return;
+            }
+            outDisplayFrame.set(win.mDisplayFrame);
+        }
+    }
+
+    public int relayoutWindow(Session session, IWindow client,
+            WindowManager.LayoutParams attrs, int requestedWidth,
+            int requestedHeight, int viewVisibility, boolean insetsPending,
+            Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
+            Surface outSurface) {
+        boolean displayed = false;
+        boolean inTouchMode;
+        Configuration newConfig = null;
+        long origId = Binder.clearCallingIdentity();
+        
+        synchronized(mWindowMap) {
+            WindowState win = windowForClientLocked(session, client);
+            if (win == null) {
+                return 0;
+            }
+            win.mRequestedWidth = requestedWidth;
+            win.mRequestedHeight = requestedHeight;
+
+            if (attrs != null) {
+                mPolicy.adjustWindowParamsLw(attrs);
+            }
+            
+            int attrChanges = 0;
+            int flagChanges = 0;
+            if (attrs != null) {
+                flagChanges = win.mAttrs.flags ^= attrs.flags;
+                attrChanges = win.mAttrs.copyFrom(attrs);
+            }
+
+            if (localLOGV) Log.v(
+                TAG, "Relayout given client " + client.asBinder()
+                + " (" + win.mAttrs.getTitle() + ")");
+
+
+            if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
+                win.mAlpha = attrs.alpha;
+            }
+
+            final boolean scaledWindow =
+                ((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0);
+
+            if (scaledWindow) {
+                // requested{Width|Height} Surface's physical size
+                // attrs.{width|height} Size on screen
+                win.mHScale = (attrs.width  != requestedWidth)  ?
+                        (attrs.width  / (float)requestedWidth) : 1.0f;
+                win.mVScale = (attrs.height != requestedHeight) ?
+                        (attrs.height / (float)requestedHeight) : 1.0f;
+            }
+
+            boolean imMayMove = (flagChanges&(
+                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;
+            
+            boolean focusMayChange = win.mViewVisibility != viewVisibility
+                    || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
+                    || (!win.mRelayoutCalled);
+            
+            win.mRelayoutCalled = true;
+            final int oldVisibility = win.mViewVisibility;
+            win.mViewVisibility = viewVisibility;
+            if (viewVisibility == View.VISIBLE &&
+                    (win.mAppToken == null || !win.mAppToken.clientHidden)) {
+                displayed = !win.isVisibleLw();
+                if (win.mExiting) {
+                    win.mExiting = false;
+                    win.mAnimation = null;
+                }
+                if (win.mDestroying) {
+                    win.mDestroying = false;
+                    mDestroySurface.remove(win);
+                }
+                if (oldVisibility == View.GONE) {
+                    win.mEnterAnimationPending = true;
+                }
+                if (displayed && win.mSurface != null && !win.mDrawPending
+                        && !win.mCommitDrawPending && !mDisplayFrozen) {
+                    applyEnterAnimationLocked(win);
+                }
+                if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
+                    // To change the format, we need to re-build the surface.
+                    win.destroySurfaceLocked();
+                    displayed = true;
+                }
+                try {
+                    Surface surface = win.createSurfaceLocked();
+                    if (surface != null) {
+                        outSurface.copyFrom(surface);
+                    } else {
+                        outSurface.clear();
+                    }
+                } catch (Exception e) {
+                    Log.w(TAG, "Exception thrown when creating surface for client "
+                         + client + " (" + win.mAttrs.getTitle() + ")",
+                         e);
+                    Binder.restoreCallingIdentity(origId);
+                    return 0;
+                }
+                if (displayed) {
+                    focusMayChange = true;
+                }
+                if (win.mAttrs.type == TYPE_INPUT_METHOD
+                        && mInputMethodWindow == null) {
+                    mInputMethodWindow = win;
+                    imMayMove = true;
+                }
+            } else {
+                win.mEnterAnimationPending = false;
+                if (win.mSurface != null) {
+                    // If we are not currently running the exit animation, we
+                    // need to see about starting one.
+                    if (!win.mExiting) {
+                        // Try starting an animation; if there isn't one, we
+                        // can destroy the surface right away.
+                        int transit = WindowManagerPolicy.TRANSIT_EXIT;
+                        if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
+                            transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+                        }
+                        if (win.isWinVisibleLw() &&
+                              applyAnimationLocked(win, transit, false)) {
+                            win.mExiting = true;
+                            mKeyWaiter.finishedKey(session, client, true,
+                                    KeyWaiter.RETURN_NOTHING);
+                        } else if (win.isAnimating()) {
+                            // Currently in a hide animation... turn this into
+                            // an exit.
+                            win.mExiting = true;
+                        } else {
+                            if (mInputMethodWindow == win) {
+                                mInputMethodWindow = null;
+                            }
+                            win.destroySurfaceLocked();
+                        }
+                    }
+                }
+                outSurface.clear();
+            }
+
+            boolean assignLayers = false;
+            
+            if (focusMayChange) {
+                //System.out.println("Focus may change: " + win.mAttrs.getTitle());
+                if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+                    assignLayers = true;
+                    imMayMove = false;
+                }
+                //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
+            }
+            
+            if (imMayMove) {
+                if (moveInputMethodWindowsIfNeededLocked(false)) {
+                    assignLayers = true;
+                }
+            }
+                
+            mLayoutNeeded = true;
+            win.mGivenInsetsPending = insetsPending;
+            if (assignLayers) {
+                assignLayersLocked();
+            }
+            newConfig = updateOrientationFromAppTokensLocked(null);
+            performLayoutAndPlaceSurfacesLocked();
+            if (win.mAppToken != null) {
+                win.mAppToken.updateReportedVisibilityLocked();
+            }
+            outFrame.set(win.mFrame);
+            outContentInsets.set(win.mContentInsets);
+            outVisibleInsets.set(win.mVisibleInsets);
+            if (localLOGV) Log.v(
+                TAG, "Relayout given client " + client.asBinder()
+                + ", requestedWidth=" + requestedWidth 
+                + ", requestedHeight=" + requestedHeight
+                + ", viewVisibility=" + viewVisibility
+                + "\nRelayout returning frame=" + outFrame
+                + ", surface=" + outSurface);
+
+            if (localLOGV || DEBUG_FOCUS) Log.v(
+                TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
+
+            inTouchMode = mInTouchMode;
+        }
+
+        if (newConfig != null) {
+            sendNewConfiguration();
+        }
+        
+        Binder.restoreCallingIdentity(origId);
+        
+        return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
+                | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);
+    }
+
+    public void finishDrawingWindow(Session session, IWindow client) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized(mWindowMap) {
+            WindowState win = windowForClientLocked(session, client);
+            if (win != null && win.finishDrawingLocked()) {
+                mLayoutNeeded = true;
+                performLayoutAndPlaceSurfacesLocked();
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
+        if (DEBUG_ANIM) Log.v(TAG, "Loading animations: params package="
+                + (lp != null ? lp.packageName : null)
+                + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
+        if (lp != null && lp.windowAnimations != 0) {
+            // If this is a system resource, don't try to load it from the
+            // application resources.  It is nice to avoid loading application
+            // resources if we can.
+            String packageName = lp.packageName != null ? lp.packageName : "android";
+            int resId = lp.windowAnimations;
+            if ((resId&0xFF000000) == 0x01000000) {
+                packageName = "android";
+            }
+            if (DEBUG_ANIM) Log.v(TAG, "Loading animations: picked package="
+                    + packageName);
+            return AttributeCache.instance().get(packageName, resId,
+                    com.android.internal.R.styleable.WindowAnimation);
+        }
+        return null;
+    }
+    
+    private void applyEnterAnimationLocked(WindowState win) {
+        int transit = WindowManagerPolicy.TRANSIT_SHOW;
+        if (win.mEnterAnimationPending) {
+            win.mEnterAnimationPending = false;
+            transit = WindowManagerPolicy.TRANSIT_ENTER;
+        }
+
+        applyAnimationLocked(win, transit, true);
+    }
+
+    private boolean applyAnimationLocked(WindowState win,
+            int transit, boolean isEntrance) {
+        if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
+            // If we are trying to apply an animation, but already running
+            // an animation of the same type, then just leave that one alone.
+            return true;
+        }
+        
+        // Only apply an animation if the display isn't frozen.  If it is
+        // frozen, there is no reason to animate and it can cause strange
+        // artifacts when we unfreeze the display if some different animation
+        // is running.
+        if (!mDisplayFrozen) {
+            int anim = mPolicy.selectAnimationLw(win, transit);
+            int attr = -1;
+            Animation a = null;
+            if (anim != 0) {
+                a = AnimationUtils.loadAnimation(mContext, anim);
+            } else {
+                switch (transit) {
+                    case WindowManagerPolicy.TRANSIT_ENTER:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_EXIT:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_SHOW:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_HIDE:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
+                        break;
+                }
+                if (attr >= 0) {
+                    a = loadAnimation(win.mAttrs, attr);
+                }
+            }
+            if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: win=" + win
+                    + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
+                    + " mAnimation=" + win.mAnimation
+                    + " isEntrance=" + isEntrance);
+            if (a != null) {
+                if (DEBUG_ANIM) {
+                    RuntimeException e = new RuntimeException();
+                    e.fillInStackTrace();
+                    Log.v(TAG, "Loaded animation " + a + " for " + win, e);
+                }
+                win.setAnimation(a);
+                win.mAnimationIsEntrance = isEntrance;
+            }
+        } else {
+            win.clearAnimation();
+        }
+
+        return win.mAnimation != null;
+    }
+
+    private Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
+        int anim = 0;
+        Context context = mContext;
+        if (animAttr >= 0) {
+            AttributeCache.Entry ent = getCachedAnimations(lp);
+            if (ent != null) {
+                context = ent.context;
+                anim = ent.array.getResourceId(animAttr, 0);
+            }
+        }
+        if (anim != 0) {
+            return AnimationUtils.loadAnimation(context, anim);
+        }
+        return null;
+    }
+    
+    private boolean applyAnimationLocked(AppWindowToken wtoken,
+            WindowManager.LayoutParams lp, int transit, boolean enter) {
+        // Only apply an animation if the display isn't frozen.  If it is
+        // frozen, there is no reason to animate and it can cause strange
+        // artifacts when we unfreeze the display if some different animation
+        // is running.
+        if (!mDisplayFrozen) {
+            int animAttr = 0;
+            switch (transit) {
+                case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+                    animAttr = enter
+                            ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation
+                            : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
+                    break;
+                case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+                    animAttr = enter
+                            ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
+                            : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
+                    break;
+                case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+                    animAttr = enter
+                            ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation
+                            : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
+                    break;
+                case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
+                    animAttr = enter
+                            ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation
+                            : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
+                    break;
+                case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+                    animAttr = enter
+                            ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation
+                            : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
+                    break;
+                case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
+                    animAttr = enter
+                            ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
+                            : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
+                    break;
+            }
+            Animation a = loadAnimation(lp, animAttr);
+            if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken
+                    + " anim=" + a
+                    + " animAttr=0x" + Integer.toHexString(animAttr)
+                    + " transit=" + transit);
+            if (a != null) {
+                if (DEBUG_ANIM) {
+                    RuntimeException e = new RuntimeException();
+                    e.fillInStackTrace();
+                    Log.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
+                }
+                wtoken.setAnimation(a);
+            }
+        } else {
+            wtoken.clearAnimation();
+        }
+
+        return wtoken.animation != null;
+    }
+
+    // -------------------------------------------------------------
+    // Application Window Tokens
+    // -------------------------------------------------------------
+
+    public void validateAppTokens(List tokens) {
+        int v = tokens.size()-1;
+        int m = mAppTokens.size()-1;
+        while (v >= 0 && m >= 0) {
+            AppWindowToken wtoken = mAppTokens.get(m);
+            if (wtoken.removed) {
+                m--;
+                continue;
+            }
+            if (tokens.get(v) != wtoken.token) {
+                Log.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
+                      + " @ " + v + ", internal is " + wtoken.token + " @ " + m);
+            }
+            v--;
+            m--;
+        }
+        while (v >= 0) {
+            Log.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v);
+            v--;
+        }
+        while (m >= 0) {
+            AppWindowToken wtoken = mAppTokens.get(m);
+            if (!wtoken.removed) {
+                Log.w(TAG, "Invalid internal token: " + wtoken.token + " @ " + m);
+            }
+            m--;
+        }
+    }
+
+    boolean checkCallingPermission(String permission, String func) {
+        // Quick check: if the calling permission is me, it's all okay.
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return true;
+        }
+        
+        if (mContext.checkCallingPermission(permission)
+                == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        }
+        String msg = "Permission Denial: " + func + " from pid="
+                + Binder.getCallingPid()
+                + ", uid=" + Binder.getCallingUid()
+                + " requires " + permission;
+        Log.w(TAG, msg);
+        return false;
+    }
+    
+    AppWindowToken findAppWindowToken(IBinder token) {
+        WindowToken wtoken = mTokenMap.get(token);
+        if (wtoken == null) {
+            return null;
+        }
+        return wtoken.appWindowToken;
+    }
+    
+    public void addWindowToken(IBinder token, int type) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "addWindowToken()")) {
+            return;
+        }
+        
+        synchronized(mWindowMap) {
+            WindowToken wtoken = mTokenMap.get(token);
+            if (wtoken != null) {
+                Log.w(TAG, "Attempted to add existing input method token: " + token);
+                return;
+            }
+            wtoken = new WindowToken(token, type, true);
+            mTokenMap.put(token, wtoken);
+            mTokenList.add(wtoken);
+        }
+    }
+    
+    public void removeWindowToken(IBinder token) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "removeWindowToken()")) {
+            return;
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+        synchronized(mWindowMap) {
+            WindowToken wtoken = mTokenMap.remove(token);
+            mTokenList.remove(wtoken);
+            if (wtoken != null) {
+                boolean delayed = false;
+                if (!wtoken.hidden) {
+                    wtoken.hidden = true;
+                    
+                    final int N = wtoken.windows.size();
+                    boolean changed = false;
+                    
+                    for (int i=0; i<N; i++) {
+                        WindowState win = wtoken.windows.get(i);
+
+                        if (win.isAnimating()) {
+                            delayed = true;
+                        }
+                        
+                        if (win.isVisibleNow()) {
+                            applyAnimationLocked(win,
+                                    WindowManagerPolicy.TRANSIT_EXIT, false);
+                            mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+                                    KeyWaiter.RETURN_NOTHING);
+                            changed = true;
+                        }
+                    }
+
+                    if (changed) {
+                        mLayoutNeeded = true;
+                        performLayoutAndPlaceSurfacesLocked();
+                        updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+                    }
+                    
+                    if (delayed) {
+                        mExitingTokens.add(wtoken);
+                    }
+                }
+                
+            } else {
+                Log.w(TAG, "Attempted to remove non-existing token: " + token);
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    public void addAppToken(int addPos, IApplicationToken token,
+            int groupId, int requestedOrientation, boolean fullscreen) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "addAppToken()")) {
+            return;
+        }
+        
+        synchronized(mWindowMap) {
+            AppWindowToken wtoken = findAppWindowToken(token.asBinder());
+            if (wtoken != null) {
+                Log.w(TAG, "Attempted to add existing app token: " + token);
+                return;
+            }
+            wtoken = new AppWindowToken(token);
+            wtoken.groupId = groupId;
+            wtoken.appFullscreen = fullscreen;
+            wtoken.requestedOrientation = requestedOrientation;
+            mAppTokens.add(addPos, wtoken);
+            if (Config.LOGV) Log.v(TAG, "Adding new app token: " + wtoken);
+            mTokenMap.put(token.asBinder(), wtoken);
+            mTokenList.add(wtoken);
+            
+            // Application tokens start out hidden.
+            wtoken.hidden = true;
+            wtoken.hiddenRequested = true;
+            
+            //dump();
+        }
+    }
+   
+    public void setAppGroupId(IBinder token, int groupId) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setAppStartingIcon()")) {
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            AppWindowToken wtoken = findAppWindowToken(token);
+            if (wtoken == null) {
+                Log.w(TAG, "Attempted to set group id of non-existing app token: " + token);
+                return;
+            }
+            wtoken.groupId = groupId;
+        }
+    }
+    
+    public int getOrientationFromWindowsLocked() {
+        int pos = mWindows.size() - 1;
+        while (pos >= 0) {
+            WindowState wtoken = (WindowState) mWindows.get(pos);
+            pos--;
+            if (wtoken.mAppToken != null) {
+                // We hit an application window. so the orientation will be determined by the
+                // app window. No point in continuing further.
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            }
+            if (!wtoken.isVisibleLw()) {
+                continue;
+            }
+            int req = wtoken.mAttrs.screenOrientation;
+            if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
+                    (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+                continue;
+            } else {
+                return req;
+            }
+        }
+        return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    }
+    
+    public int getOrientationFromAppTokensLocked() {
+            int pos = mAppTokens.size() - 1;
+            int curGroup = 0;
+            int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            boolean haveGroup = false;
+            while (pos >= 0) {
+                AppWindowToken wtoken = mAppTokens.get(pos);
+                pos--;
+                if (!haveGroup) {
+                    // We ignore any hidden applications on the top.
+                    if (wtoken.hiddenRequested || wtoken.willBeHidden) {
+                        continue;
+                    }
+                    haveGroup = true;
+                    curGroup = wtoken.groupId;
+                    lastOrientation = wtoken.requestedOrientation;
+                } else if (curGroup != wtoken.groupId) {
+                    // If we have hit a new application group, and the bottom
+                    // of the previous group didn't explicitly say to use
+                    // the orientation behind it, then we'll stick with the
+                    // user's orientation.
+                    if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                        return lastOrientation;
+                    }
+                }
+                int or = wtoken.requestedOrientation;
+                // If this application is fullscreen, then just take whatever
+                // orientation it has and ignores whatever is under it.
+                if (wtoken.appFullscreen) {
+                    return or;
+                }
+                // If this application has requested an explicit orientation,
+                // then use it.
+                if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||
+                        or == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ||
+                        or == ActivityInfo.SCREEN_ORIENTATION_SENSOR ||
+                        or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR ||
+                        or == ActivityInfo.SCREEN_ORIENTATION_USER) {
+                    return or;
+                }
+            }
+            return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    }
+    
+    public Configuration updateOrientationFromAppTokens(
+        IBinder freezeThisOneIfNeeded) {
+        Configuration config;
+        long ident = Binder.clearCallingIdentity();
+        synchronized(mWindowMap) {
+            config = updateOrientationFromAppTokensLocked(freezeThisOneIfNeeded);
+        }
+        if (config != null) {
+            mLayoutNeeded = true;
+            performLayoutAndPlaceSurfacesLocked();
+        }
+        Binder.restoreCallingIdentity(ident);
+        return config;
+    }
+    
+    /*
+     * The orientation is computed from non-application windows first. If none of
+     * the non-application windows specify orientation, the orientation is computed from
+     * application tokens. 
+     * @see android.view.IWindowManager#updateOrientationFromAppTokens(
+     * android.os.IBinder)
+     */
+    public Configuration updateOrientationFromAppTokensLocked(
+            IBinder freezeThisOneIfNeeded) {
+        boolean changed = false;
+        Configuration config = null;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            int req = getOrientationFromWindowsLocked();
+            if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
+                req = getOrientationFromAppTokensLocked();
+            }
+            
+            if (req != mForcedAppOrientation) {
+                changed = true;
+                mForcedAppOrientation = req;
+                //send a message to Policy indicating orientation change to take
+                //action like disabling/enabling sensors etc.,
+                mPolicy.setCurrentOrientation(req);
+            }
+            
+            if (changed) {
+                changed = setRotationUncheckedLocked(
+                        WindowManagerPolicy.USE_LAST_ROTATION);
+                if (changed) {
+                    if (freezeThisOneIfNeeded != null) {
+                        AppWindowToken wtoken = findAppWindowToken(
+                                freezeThisOneIfNeeded);
+                        if (wtoken != null) {
+                            startAppFreezingScreenLocked(wtoken,
+                                    ActivityInfo.CONFIG_ORIENTATION);
+                        }
+                    }
+                    return computeNewConfiguration();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        
+        return null;
+    }
+    
+    public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setAppOrientation()")) {
+            return;
+        }
+        
+        synchronized(mWindowMap) {
+            AppWindowToken wtoken = findAppWindowToken(token.asBinder());
+            if (wtoken == null) {
+                Log.w(TAG, "Attempted to set orientation of non-existing app token: " + token);
+                return;
+            }
+            
+            wtoken.requestedOrientation = requestedOrientation;
+        }
+    }
+    
+    public int getAppOrientation(IApplicationToken token) {
+        synchronized(mWindowMap) {
+            AppWindowToken wtoken = findAppWindowToken(token.asBinder());
+            if (wtoken == null) {
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            }
+            
+            return wtoken.requestedOrientation;
+        }
+    }
+    
+    public void setFocusedApp(IBinder token, boolean moveFocusNow) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setFocusedApp()")) {
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            boolean changed = false;
+            if (token == null) {
+                if (DEBUG_FOCUS) Log.v(TAG, "Clearing focused app, was " + mFocusedApp);
+                changed = mFocusedApp != null;
+                mFocusedApp = null;
+                mKeyWaiter.tickle();
+            } else {
+                AppWindowToken newFocus = findAppWindowToken(token);
+                if (newFocus == null) {
+                    Log.w(TAG, "Attempted to set focus to non-existing app token: " + token);
+                    return;
+                }
+                changed = mFocusedApp != newFocus;
+                mFocusedApp = newFocus;
+                if (DEBUG_FOCUS) Log.v(TAG, "Set focused app to: " + mFocusedApp);
+                mKeyWaiter.tickle();
+            }
+
+            if (moveFocusNow && changed) {
+                final long origId = Binder.clearCallingIdentity();
+                updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    public void prepareAppTransition(int transit) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "prepareAppTransition()")) {
+            return;
+        }
+        
+        synchronized(mWindowMap) {
+            if (DEBUG_APP_TRANSITIONS) Log.v(
+                    TAG, "Prepare app transition: transit=" + transit
+                    + " mNextAppTransition=" + mNextAppTransition);
+            if (!mDisplayFrozen) {
+                if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
+                    mNextAppTransition = transit;
+                }
+                mAppTransitionReady = false;
+                mAppTransitionTimeout = false;
+                mStartingIconInTransition = false;
+                mSkipAppTransitionAnimation = false;
+                mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+                mH.sendMessageDelayed(mH.obtainMessage(H.APP_TRANSITION_TIMEOUT),
+                        5000);
+            }
+        }
+    }
+
+    public int getPendingAppTransition() {
+        return mNextAppTransition;
+    }
+    
+    public void executeAppTransition() {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "executeAppTransition()")) {
+            return;
+        }
+        
+        synchronized(mWindowMap) {
+            if (DEBUG_APP_TRANSITIONS) Log.v(
+                    TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition);
+            if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+                mAppTransitionReady = true;
+                final long origId = Binder.clearCallingIdentity();
+                performLayoutAndPlaceSurfacesLocked();
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    public void setAppStartingWindow(IBinder token, String pkg,
+            int theme, CharSequence nonLocalizedLabel, int labelRes, int icon,
+            IBinder transferFrom, boolean createIfNeeded) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setAppStartingIcon()")) {
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            if (DEBUG_STARTING_WINDOW) Log.v(
+                    TAG, "setAppStartingIcon: token=" + token + " pkg=" + pkg
+                    + " transferFrom=" + transferFrom);
+            
+            AppWindowToken wtoken = findAppWindowToken(token);
+            if (wtoken == null) {
+                Log.w(TAG, "Attempted to set icon of non-existing app token: " + token);
+                return;
+            }
+
+            // If the display is frozen, we won't do anything until the
+            // actual window is displayed so there is no reason to put in
+            // the starting window.
+            if (mDisplayFrozen) {
+                return;
+            }
+            
+            if (wtoken.startingData != null) {
+                return;
+            }
+            
+            if (transferFrom != null) {
+                AppWindowToken ttoken = findAppWindowToken(transferFrom);
+                if (ttoken != null) {
+                    WindowState startingWindow = ttoken.startingWindow;
+                    if (startingWindow != null) {
+                        if (mStartingIconInTransition) {
+                            // In this case, the starting icon has already
+                            // been displayed, so start letting windows get
+                            // shown immediately without any more transitions.
+                            mSkipAppTransitionAnimation = true;
+                        }
+                        if (DEBUG_STARTING_WINDOW) Log.v(TAG,
+                                "Moving existing starting from " + ttoken
+                                + " to " + wtoken);
+                        final long origId = Binder.clearCallingIdentity();
+                        
+                        // Transfer the starting window over to the new
+                        // token.
+                        wtoken.startingData = ttoken.startingData;
+                        wtoken.startingView = ttoken.startingView;
+                        wtoken.startingWindow = startingWindow;
+                        ttoken.startingData = null;
+                        ttoken.startingView = null;
+                        ttoken.startingWindow = null;
+                        ttoken.startingMoved = true;
+                        startingWindow.mToken = wtoken;
+                        startingWindow.mAppToken = wtoken;
+                        mWindows.remove(startingWindow);
+                        ttoken.windows.remove(startingWindow);
+                        ttoken.allAppWindows.remove(startingWindow);
+                        addWindowToListInOrderLocked(startingWindow, true);
+                        wtoken.allAppWindows.add(startingWindow);
+                        
+                        // Propagate other interesting state between the
+                        // tokens.  If the old token is displayed, we should
+                        // immediately force the new one to be displayed.  If
+                        // it is animating, we need to move that animation to
+                        // the new one.
+                        if (ttoken.allDrawn) {
+                            wtoken.allDrawn = true;
+                        }
+                        if (ttoken.firstWindowDrawn) {
+                            wtoken.firstWindowDrawn = true;
+                        }
+                        if (!ttoken.hidden) {
+                            wtoken.hidden = false;
+                            wtoken.hiddenRequested = false;
+                            wtoken.willBeHidden = false;
+                        }
+                        if (wtoken.clientHidden != ttoken.clientHidden) {
+                            wtoken.clientHidden = ttoken.clientHidden;
+                            wtoken.sendAppVisibilityToClients();
+                        }
+                        if (ttoken.animation != null) {
+                            wtoken.animation = ttoken.animation;
+                            wtoken.animating = ttoken.animating;
+                            wtoken.animLayerAdjustment = ttoken.animLayerAdjustment;
+                            ttoken.animation = null;
+                            ttoken.animLayerAdjustment = 0;
+                            wtoken.updateLayers();
+                            ttoken.updateLayers();
+                        }
+                        
+                        updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+                        assignLayersLocked();
+                        mLayoutNeeded = true;
+                        performLayoutAndPlaceSurfacesLocked();
+                        Binder.restoreCallingIdentity(origId);
+                        return;
+                    } else if (ttoken.startingData != null) {
+                        // The previous app was getting ready to show a
+                        // starting window, but hasn't yet done so.  Steal it!
+                        if (DEBUG_STARTING_WINDOW) Log.v(TAG,
+                                "Moving pending starting from " + ttoken
+                                + " to " + wtoken);
+                        wtoken.startingData = ttoken.startingData;
+                        ttoken.startingData = null;
+                        ttoken.startingMoved = true;
+                        Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
+                        // Note: we really want to do sendMessageAtFrontOfQueue() because we
+                        // want to process the message ASAP, before any other queued
+                        // messages.
+                        mH.sendMessageAtFrontOfQueue(m);
+                        return;
+                    }
+                }
+            }
+
+            // There is no existing starting window, and the caller doesn't
+            // want us to create one, so that's it!
+            if (!createIfNeeded) {
+                return;
+            }
+            
+            mStartingIconInTransition = true;
+            wtoken.startingData = new StartingData(
+                    pkg, theme, nonLocalizedLabel,
+                    labelRes, icon);
+            Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
+            // Note: we really want to do sendMessageAtFrontOfQueue() because we
+            // want to process the message ASAP, before any other queued
+            // messages.
+            mH.sendMessageAtFrontOfQueue(m);
+        }
+    }
+
+    public void setAppWillBeHidden(IBinder token) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setAppWillBeHidden()")) {
+            return;
+        }
+
+        AppWindowToken wtoken;
+
+        synchronized(mWindowMap) {
+            wtoken = findAppWindowToken(token);
+            if (wtoken == null) {
+                Log.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token);
+                return;
+            }
+            wtoken.willBeHidden = true;
+        }
+    }
+    
+    boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
+            boolean visible, int transit, boolean performLayout) {
+        boolean delayed = false;
+
+        if (wtoken.clientHidden == visible) {
+            wtoken.clientHidden = !visible;
+            wtoken.sendAppVisibilityToClients();
+        }
+        
+        wtoken.willBeHidden = false;
+        if (wtoken.hidden == visible) {
+            final int N = wtoken.allAppWindows.size();
+            boolean changed = false;
+            if (DEBUG_APP_TRANSITIONS) Log.v(
+                TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
+                + " performLayout=" + performLayout);
+            
+            boolean runningAppAnimation = false;
+            
+            if (transit != WindowManagerPolicy.TRANSIT_NONE) {
+                if (wtoken.animation == sDummyAnimation) {
+                    wtoken.animation = null;
+                }
+                applyAnimationLocked(wtoken, lp, transit, visible);
+                changed = true;
+                if (wtoken.animation != null) {
+                    delayed = runningAppAnimation = true;
+                }
+            }
+            
+            for (int i=0; i<N; i++) {
+                WindowState win = wtoken.allAppWindows.get(i);
+                if (win == wtoken.startingWindow) {
+                    continue;
+                }
+
+                if (win.isAnimating()) {
+                    delayed = true;
+                }
+                
+                //Log.i(TAG, "Window " + win + ": vis=" + win.isVisible());
+                //win.dump("  ");
+                if (visible) {
+                    if (!win.isVisibleNow()) {
+                        if (!runningAppAnimation) {
+                            applyAnimationLocked(win,
+                                    WindowManagerPolicy.TRANSIT_ENTER, true);
+                        }
+                        changed = true;
+                    }
+                } else if (win.isVisibleNow()) {
+                    if (!runningAppAnimation) {
+                        applyAnimationLocked(win,
+                                WindowManagerPolicy.TRANSIT_EXIT, false);
+                    }
+                    mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+                            KeyWaiter.RETURN_NOTHING);
+                    changed = true;
+                }
+            }
+
+            wtoken.hidden = wtoken.hiddenRequested = !visible;
+            if (!visible) {
+                unsetAppFreezingScreenLocked(wtoken, true, true);
+            } else {
+                // If we are being set visible, and the starting window is
+                // not yet displayed, then make sure it doesn't get displayed.
+                WindowState swin = wtoken.startingWindow;
+                if (swin != null && (swin.mDrawPending
+                        || swin.mCommitDrawPending)) {
+                    swin.mPolicyVisibility = false;
+                    swin.mPolicyVisibilityAfterAnim = false;
+                 }
+            }
+            
+            if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "setTokenVisibilityLocked: " + wtoken
+                      + ": hidden=" + wtoken.hidden + " hiddenRequested="
+                      + wtoken.hiddenRequested);
+            
+            if (changed && performLayout) {
+                mLayoutNeeded = true;
+                updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+                assignLayersLocked();
+                performLayoutAndPlaceSurfacesLocked();
+            }
+        }
+
+        if (wtoken.animation != null) {
+            delayed = true;
+        }
+        
+        return delayed;
+    }
+
+    public void setAppVisibility(IBinder token, boolean visible) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setAppVisibility()")) {
+            return;
+        }
+
+        AppWindowToken wtoken;
+
+        synchronized(mWindowMap) {
+            wtoken = findAppWindowToken(token);
+            if (wtoken == null) {
+                Log.w(TAG, "Attempted to set visibility of non-existing app token: " + token);
+                return;
+            }
+
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
+                RuntimeException e = new RuntimeException();
+                e.fillInStackTrace();
+                Log.v(TAG, "setAppVisibility(" + token + ", " + visible
+                        + "): mNextAppTransition=" + mNextAppTransition
+                        + " hidden=" + wtoken.hidden
+                        + " hiddenRequested=" + wtoken.hiddenRequested, e);
+            }
+            
+            // If we are preparing an app transition, then delay changing
+            // the visibility of this token until we execute that transition.
+            if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+                // Already in requested state, don't do anything more.
+                if (wtoken.hiddenRequested != visible) {
+                    return;
+                }
+                wtoken.hiddenRequested = !visible;
+                
+                if (DEBUG_APP_TRANSITIONS) Log.v(
+                        TAG, "Setting dummy animation on: " + wtoken);
+                wtoken.setDummyAnimation();
+                mOpeningApps.remove(wtoken);
+                mClosingApps.remove(wtoken);
+                wtoken.inPendingTransaction = true;
+                if (visible) {
+                    mOpeningApps.add(wtoken);
+                    wtoken.allDrawn = false;
+                    wtoken.startingDisplayed = false;
+                    wtoken.startingMoved = false;
+                    
+                    if (wtoken.clientHidden) {
+                        // In the case where we are making an app visible
+                        // but holding off for a transition, we still need
+                        // to tell the client to make its windows visible so
+                        // they get drawn.  Otherwise, we will wait on
+                        // performing the transition until all windows have
+                        // been drawn, they never will be, and we are sad.
+                        wtoken.clientHidden = false;
+                        wtoken.sendAppVisibilityToClients();
+                    }
+                } else {
+                    mClosingApps.add(wtoken);
+                }
+                return;
+            }
+            
+            final long origId = Binder.clearCallingIdentity();
+            setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_NONE, true);
+            wtoken.updateReportedVisibilityLocked();
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    void unsetAppFreezingScreenLocked(AppWindowToken wtoken,
+            boolean unfreezeSurfaceNow, boolean force) {
+        if (wtoken.freezingScreen) {
+            if (DEBUG_ORIENTATION) Log.v(TAG, "Clear freezing of " + wtoken
+                    + " force=" + force);
+            final int N = wtoken.allAppWindows.size();
+            boolean unfrozeWindows = false;
+            for (int i=0; i<N; i++) {
+                WindowState w = wtoken.allAppWindows.get(i);
+                if (w.mAppFreezing) {
+                    w.mAppFreezing = false;
+                    if (w.mSurface != null && !w.mOrientationChanging) {
+                        w.mOrientationChanging = true;
+                    }
+                    unfrozeWindows = true;
+                }
+            }
+            if (force || unfrozeWindows) {
+                if (DEBUG_ORIENTATION) Log.v(TAG, "No longer freezing: " + wtoken);
+                wtoken.freezingScreen = false;
+                mAppsFreezingScreen--;
+            }
+            if (unfreezeSurfaceNow) {
+                if (unfrozeWindows) {
+                    mLayoutNeeded = true;
+                    performLayoutAndPlaceSurfacesLocked();
+                }
+                if (mAppsFreezingScreen == 0 && !mWindowsFreezingScreen) {
+                    stopFreezingDisplayLocked();
+                }
+            }
+        }
+    }
+    
+    public void startAppFreezingScreenLocked(AppWindowToken wtoken,
+            int configChanges) {
+        if (DEBUG_ORIENTATION) {
+            RuntimeException e = new RuntimeException();
+            e.fillInStackTrace();
+            Log.i(TAG, "Set freezing of " + wtoken.appToken
+                    + ": hidden=" + wtoken.hidden + " freezing="
+                    + wtoken.freezingScreen, e);
+        }
+        if (!wtoken.hiddenRequested) {
+            if (!wtoken.freezingScreen) {
+                wtoken.freezingScreen = true;
+                mAppsFreezingScreen++;
+                if (mAppsFreezingScreen == 1) {
+                    startFreezingDisplayLocked();
+                    mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+                    mH.sendMessageDelayed(mH.obtainMessage(H.APP_FREEZE_TIMEOUT),
+                            5000);
+                }
+            }
+            final int N = wtoken.allAppWindows.size();
+            for (int i=0; i<N; i++) {
+                WindowState w = wtoken.allAppWindows.get(i);
+                w.mAppFreezing = true;
+            }
+        }
+    }
+    
+    public void startAppFreezingScreen(IBinder token, int configChanges) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setAppFreezingScreen()")) {
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            if (configChanges == 0 && !mDisplayFrozen) {
+                if (DEBUG_ORIENTATION) Log.v(TAG, "Skipping set freeze of " + token);
+                return;
+            }
+            
+            AppWindowToken wtoken = findAppWindowToken(token);
+            if (wtoken == null || wtoken.appToken == null) {
+                Log.w(TAG, "Attempted to freeze screen with non-existing app token: " + wtoken);
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            startAppFreezingScreenLocked(wtoken, configChanges);
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+    
+    public void stopAppFreezingScreen(IBinder token, boolean force) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "setAppFreezingScreen()")) {
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            AppWindowToken wtoken = findAppWindowToken(token);
+            if (wtoken == null || wtoken.appToken == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            if (DEBUG_ORIENTATION) Log.v(TAG, "Clear freezing of " + token
+                    + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen);
+            unsetAppFreezingScreenLocked(wtoken, true, force);
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+    
+    public void removeAppToken(IBinder token) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "removeAppToken()")) {
+            return;
+        }
+
+        AppWindowToken wtoken = null;
+        AppWindowToken startingToken = null;
+        boolean delayed = false;
+
+        final long origId = Binder.clearCallingIdentity();
+        synchronized(mWindowMap) {
+            WindowToken basewtoken = mTokenMap.remove(token);
+            mTokenList.remove(basewtoken);
+            if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
+                if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "Removing app token: " + wtoken);
+                delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_NONE, true);
+                wtoken.inPendingTransaction = false;
+                mOpeningApps.remove(wtoken);
+                if (mClosingApps.contains(wtoken)) {
+                    delayed = true;
+                } else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+                    mClosingApps.add(wtoken);
+                    delayed = true;
+                }
+                if (DEBUG_APP_TRANSITIONS) Log.v(
+                        TAG, "Removing app " + wtoken + " delayed=" + delayed
+                        + " animation=" + wtoken.animation
+                        + " animating=" + wtoken.animating);
+                if (delayed) {
+                    // set the token aside because it has an active animation to be finished
+                    mExitingAppTokens.add(wtoken);
+                }
+                mAppTokens.remove(wtoken);
+                wtoken.removed = true;
+                if (wtoken.startingData != null) {
+                    startingToken = wtoken;
+                }
+                unsetAppFreezingScreenLocked(wtoken, true, true);
+                if (mFocusedApp == wtoken) {
+                    if (DEBUG_FOCUS) Log.v(TAG, "Removing focused app token:" + wtoken);
+                    mFocusedApp = null;
+                    updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+                    mKeyWaiter.tickle();
+                }
+            } else {
+                Log.w(TAG, "Attempted to remove non-existing app token: " + token);
+            }
+            
+            if (!delayed && wtoken != null) {
+                wtoken.updateReportedVisibilityLocked();
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+
+        if (startingToken != null) {
+            if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Schedule remove starting "
+                    + startingToken + ": app token removed");
+            Message m = mH.obtainMessage(H.REMOVE_STARTING, startingToken);
+            mH.sendMessage(m);
+        }
+    }
+
+    private boolean tmpRemoveAppWindowsLocked(WindowToken token) {
+        final int NW = token.windows.size();
+        for (int i=0; i<NW; i++) {
+            WindowState win = token.windows.get(i);
+            mWindows.remove(win);
+            int j = win.mChildWindows.size();
+            while (j > 0) {
+                j--;
+                mWindows.remove(win.mChildWindows.get(j));
+            }
+        }
+        return NW > 0;
+    }
+
+    void dumpAppTokensLocked() {
+        for (int i=mAppTokens.size()-1; i>=0; i--) {
+            Log.v(TAG, "  #" + i + ": " + mAppTokens.get(i).token);
+        }
+    }
+    
+    void dumpWindowsLocked() {
+        for (int i=mWindows.size()-1; i>=0; i--) {
+            Log.v(TAG, "  #" + i + ": " + mWindows.get(i));
+        }
+    }
+    
+    private int findWindowOffsetLocked(int tokenPos) {
+        final int NW = mWindows.size();
+
+        if (tokenPos >= mAppTokens.size()) {
+            int i = NW;
+            while (i > 0) {
+                i--;
+                WindowState win = (WindowState)mWindows.get(i);
+                if (win.getAppToken() != null) {
+                    return i+1;
+                }
+            }
+        }
+
+        while (tokenPos > 0) {
+            // Find the first app token below the new position that has
+            // a window displayed.
+            final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
+            if (DEBUG_REORDER) Log.v(TAG, "Looking for lower windows @ "
+                    + tokenPos + " -- " + wtoken.token);
+            int i = wtoken.windows.size();
+            while (i > 0) {
+                i--;
+                WindowState win = wtoken.windows.get(i);
+                int j = win.mChildWindows.size();
+                while (j > 0) {
+                    j--;
+                    WindowState cwin = (WindowState)win.mChildWindows.get(j);
+                    if (cwin.mSubLayer >= 0 ) {
+                        for (int pos=NW-1; pos>=0; pos--) {
+                            if (mWindows.get(pos) == cwin) {
+                                if (DEBUG_REORDER) Log.v(TAG,
+                                        "Found child win @" + (pos+1));
+                                return pos+1;
+                            }
+                        }
+                    }
+                }
+                for (int pos=NW-1; pos>=0; pos--) {
+                    if (mWindows.get(pos) == win) {
+                        if (DEBUG_REORDER) Log.v(TAG, "Found win @" + (pos+1));
+                        return pos+1;
+                    }
+                }
+            }
+            tokenPos--;
+        }
+
+        return 0;
+    }
+
+    private final int reAddWindowLocked(int index, WindowState win) {
+        final int NCW = win.mChildWindows.size();
+        boolean added = false;
+        for (int j=0; j<NCW; j++) {
+            WindowState cwin = (WindowState)win.mChildWindows.get(j);
+            if (!added && cwin.mSubLayer >= 0) {
+                mWindows.add(index, win);
+                index++;
+                added = true;
+            }
+            mWindows.add(index, cwin);
+            index++;
+        }
+        if (!added) {
+            mWindows.add(index, win);
+            index++;
+        }
+        return index;
+    }
+    
+    private final int reAddAppWindowsLocked(int index, WindowToken token) {
+        final int NW = token.windows.size();
+        for (int i=0; i<NW; i++) {
+            index = reAddWindowLocked(index, token.windows.get(i));
+        }
+        return index;
+    }
+
+    public void moveAppToken(int index, IBinder token) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "moveAppToken()")) {
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            if (DEBUG_REORDER) Log.v(TAG, "Initial app tokens:");
+            if (DEBUG_REORDER) dumpAppTokensLocked();
+            final AppWindowToken wtoken = findAppWindowToken(token);
+            if (wtoken == null || !mAppTokens.remove(wtoken)) {
+                Log.w(TAG, "Attempting to reorder token that doesn't exist: "
+                      + token + " (" + wtoken + ")");
+                return;
+            }
+            mAppTokens.add(index, wtoken);
+            if (DEBUG_REORDER) Log.v(TAG, "Moved " + token + " to " + index + ":");
+            if (DEBUG_REORDER) dumpAppTokensLocked();
+            
+            final long origId = Binder.clearCallingIdentity();
+            if (DEBUG_REORDER) Log.v(TAG, "Removing windows in " + token + ":");
+            if (DEBUG_REORDER) dumpWindowsLocked();
+            if (tmpRemoveAppWindowsLocked(wtoken)) {
+                if (DEBUG_REORDER) Log.v(TAG, "Adding windows back in:");
+                if (DEBUG_REORDER) dumpWindowsLocked();
+                reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);
+                if (DEBUG_REORDER) Log.v(TAG, "Final window list:");
+                if (DEBUG_REORDER) dumpWindowsLocked();
+                updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+                assignLayersLocked();
+                mLayoutNeeded = true;
+                performLayoutAndPlaceSurfacesLocked();
+            }
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private void removeAppTokensLocked(List<IBinder> tokens) {
+        // XXX This should be done more efficiently!
+        // (take advantage of the fact that both lists should be
+        // ordered in the same way.)
+        int N = tokens.size();
+        for (int i=0; i<N; i++) {
+            IBinder token = tokens.get(i);
+            final AppWindowToken wtoken = findAppWindowToken(token);
+            if (!mAppTokens.remove(wtoken)) {
+                Log.w(TAG, "Attempting to reorder token that doesn't exist: "
+                      + token + " (" + wtoken + ")");
+                i--;
+                N--;
+            }
+        }
+    }
+
+    private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {
+        // First remove all of the windows from the list.
+        final int N = tokens.size();
+        int i;
+        for (i=0; i<N; i++) {
+            WindowToken token = mTokenMap.get(tokens.get(i));
+            if (token != null) {
+                tmpRemoveAppWindowsLocked(token);
+            }
+        }
+
+        // Where to start adding?
+        int pos = findWindowOffsetLocked(tokenPos);
+
+        // And now add them back at the correct place.
+        for (i=0; i<N; i++) {
+            WindowToken token = mTokenMap.get(tokens.get(i));
+            if (token != null) {
+                pos = reAddAppWindowsLocked(pos, token);
+            }
+        }
+
+        updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+        assignLayersLocked();
+        mLayoutNeeded = true;
+        performLayoutAndPlaceSurfacesLocked();
+
+        //dump();
+    }
+
+    public void moveAppTokensToTop(List<IBinder> tokens) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "moveAppTokensToTop()")) {
+            return;
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+        synchronized(mWindowMap) {
+            removeAppTokensLocked(tokens);
+            final int N = tokens.size();
+            for (int i=0; i<N; i++) {
+                AppWindowToken wt = findAppWindowToken(tokens.get(i));
+                if (wt != null) {
+                    mAppTokens.add(wt);
+                }
+            }
+            moveAppWindowsLocked(tokens, mAppTokens.size());
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    public void moveAppTokensToBottom(List<IBinder> tokens) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "moveAppTokensToBottom()")) {
+            return;
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+        synchronized(mWindowMap) {
+            removeAppTokensLocked(tokens);
+            final int N = tokens.size();
+            int pos = 0;
+            for (int i=0; i<N; i++) {
+                AppWindowToken wt = findAppWindowToken(tokens.get(i));
+                if (wt != null) {
+                    mAppTokens.add(pos, wt);
+                    pos++;
+                }
+            }
+            moveAppWindowsLocked(tokens, 0);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    // -------------------------------------------------------------
+    // Misc IWindowSession methods
+    // -------------------------------------------------------------
+    
+    public void disableKeyguard(IBinder token, String tag) {
+        if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+            != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+        }
+        mKeyguardDisabled.acquire(token, tag);
+    }
+
+    public void reenableKeyguard(IBinder token) {
+        if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+            != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+        }
+        synchronized (mKeyguardDisabled) {
+            mKeyguardDisabled.release(token);
+
+            if (!mKeyguardDisabled.isAcquired()) {
+                // if we are the last one to reenable the keyguard wait until
+                // we have actaully finished reenabling until returning
+                mWaitingUntilKeyguardReenabled = true;
+                while (mWaitingUntilKeyguardReenabled) {
+                    try {
+                        mKeyguardDisabled.wait();
+                    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @see android.app.KeyguardManager#exitKeyguardSecurely
+     */
+    public void exitKeyguardSecurely(final IOnKeyguardExitResult callback) {
+        if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+            != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+        }
+        mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() {
+            public void onKeyguardExitResult(boolean success) {
+                try {
+                    callback.onKeyguardExitResult(success);
+                } catch (RemoteException e) {
+                    // Client has died, we don't care.
+                }
+            }
+        });
+    }
+
+    public boolean inKeyguardRestrictedInputMode() {
+        return mPolicy.inKeyguardRestrictedKeyInputMode();
+    }
+    
+    static float fixScale(float scale) {
+        if (scale < 0) scale = 0;
+        else if (scale > 20) scale = 20;
+        return Math.abs(scale);
+    }
+    
+    public void setAnimationScale(int which, float scale) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
+                "setAnimationScale()")) {
+            return;
+        }
+
+        if (scale < 0) scale = 0;
+        else if (scale > 20) scale = 20;
+        scale = Math.abs(scale);
+        switch (which) {
+            case 0: mWindowAnimationScale = fixScale(scale); break;
+            case 1: mTransitionAnimationScale = fixScale(scale); break;
+        }
+        
+        // Persist setting
+        mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
+    }
+    
+    public void setAnimationScales(float[] scales) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
+                "setAnimationScale()")) {
+            return;
+        }
+
+        if (scales != null) {
+            if (scales.length >= 1) {
+                mWindowAnimationScale = fixScale(scales[0]);
+            }
+            if (scales.length >= 2) {
+                mTransitionAnimationScale = fixScale(scales[1]);
+            }
+        }
+        
+        // Persist setting
+        mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
+    }
+    
+    public float getAnimationScale(int which) {
+        switch (which) {
+            case 0: return mWindowAnimationScale;
+            case 1: return mTransitionAnimationScale;
+        }
+        return 0;
+    }
+    
+    public float[] getAnimationScales() {
+        return new float[] { mWindowAnimationScale, mTransitionAnimationScale };
+    }
+    
+    public int getSwitchState(int sw) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "getSwitchState()")) {
+            return -1;
+        }
+        return KeyInputQueue.getSwitchState(sw);
+    }
+    
+    public int getSwitchStateForDevice(int devid, int sw) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "getSwitchStateForDevice()")) {
+            return -1;
+        }
+        return KeyInputQueue.getSwitchState(devid, sw);
+    }
+    
+    public int getScancodeState(int sw) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "getScancodeState()")) {
+            return -1;
+        }
+        return KeyInputQueue.getScancodeState(sw);
+    }
+    
+    public int getScancodeStateForDevice(int devid, int sw) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "getScancodeStateForDevice()")) {
+            return -1;
+        }
+        return KeyInputQueue.getScancodeState(devid, sw);
+    }
+    
+    public int getKeycodeState(int sw) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "getKeycodeState()")) {
+            return -1;
+        }
+        return KeyInputQueue.getKeycodeState(sw);
+    }
+    
+    public int getKeycodeStateForDevice(int devid, int sw) {
+        if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+                "getKeycodeStateForDevice()")) {
+            return -1;
+        }
+        return KeyInputQueue.getKeycodeState(devid, sw);
+    }
+    
+    public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
+        return KeyInputQueue.hasKeys(keycodes, keyExists);
+    }
+    
+    public void enableScreenAfterBoot() {
+        synchronized(mWindowMap) {
+            if (mSystemBooted) {
+                return;
+            }
+            mSystemBooted = true;
+        }
+        
+        performEnableScreen();
+    }
+    
+    public void enableScreenIfNeededLocked() {
+        if (mDisplayEnabled) {
+            return;
+        }
+        if (!mSystemBooted) {
+            return;
+        }
+        mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN));
+    }
+    
+    public void performEnableScreen() {
+        synchronized(mWindowMap) {
+            if (mDisplayEnabled) {
+                return;
+            }
+            if (!mSystemBooted) {
+                return;
+            }
+            
+            // Don't enable the screen until all existing windows
+            // have been drawn.
+            final int N = mWindows.size();
+            for (int i=0; i<N; i++) {
+                WindowState w = (WindowState)mWindows.get(i);
+                if (w.isVisibleLw() && !w.isDisplayedLw()) {
+                    return;
+                }
+            }
+            
+            mDisplayEnabled = true;
+            if (false) {
+                Log.i(TAG, "ENABLING SCREEN!");
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                this.dump(null, pw, null);
+                Log.i(TAG, sw.toString());
+            }
+            try {
+                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+                if (surfaceFlinger != null) {
+                    //Log.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
+                    Parcel data = Parcel.obtain();
+                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
+                    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION,
+                                            data, null, 0);
+                    data.recycle();
+                }
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Boot completed: SurfaceFlinger is dead!");
+            }
+        }
+        
+        mPolicy.enableScreenAfterBoot();
+        
+        // Make sure the last requested orientation has been applied.
+        setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false);
+    }
+    
+    public void setInTouchMode(boolean mode) {
+        synchronized(mWindowMap) {
+            mInTouchMode = mode;
+        }
+    }
+
+    public void setRotation(int rotation, 
+            boolean alwaysSendConfiguration) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
+                "setOrientation()")) {
+            return;
+        }
+
+        setRotationUnchecked(rotation, alwaysSendConfiguration);
+    }
+    
+    public void setRotationUnchecked(int rotation, boolean alwaysSendConfiguration) {
+        if(DEBUG_ORIENTATION) Log.v(TAG,
+                "alwaysSendConfiguration set to "+alwaysSendConfiguration);
+        
+        long origId = Binder.clearCallingIdentity();
+        boolean changed;
+        synchronized(mWindowMap) {
+            changed = setRotationUncheckedLocked(rotation);
+        }
+        
+        if (changed) {
+            sendNewConfiguration();
+            synchronized(mWindowMap) {
+                mLayoutNeeded = true;
+                performLayoutAndPlaceSurfacesLocked();
+            }
+        } else if (alwaysSendConfiguration) {
+            //update configuration ignoring orientation change
+            sendNewConfiguration();
+        }
+        
+        Binder.restoreCallingIdentity(origId);
+    }
+    
+    public boolean setRotationUncheckedLocked(int rotation) {
+        boolean changed;
+        if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) {
+            rotation = mRequestedRotation;
+        } else {
+            mRequestedRotation = rotation;
+        }
+        if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from " + rotation);
+        rotation = mPolicy.rotationForOrientation(mForcedAppOrientation,
+                mRotation, mDisplayEnabled);
+        if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation);
+        changed = mDisplayEnabled && mRotation != rotation;
+        
+        if (changed) {
+            if (DEBUG_ORIENTATION) Log.v(TAG, 
+                    "Rotation changed to " + rotation
+                    + " from " + mRotation
+                    + " (forceApp=" + mForcedAppOrientation
+                    + ", req=" + mRequestedRotation + ")");
+            mRotation = rotation;
+            mWindowsFreezingScreen = true;
+            mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
+            mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
+                    2000);
+            startFreezingDisplayLocked();
+            mQueue.setOrientation(rotation);
+            if (mDisplayEnabled) {
+                Surface.setOrientation(0, rotation);
+            }
+            for (int i=mWindows.size()-1; i>=0; i--) {
+                WindowState w = (WindowState)mWindows.get(i);
+                if (w.mSurface != null) {
+                    w.mOrientationChanging = true;
+                }
+            }
+            for (int i=mRotationWatchers.size()-1; i>=0; i--) {
+                try {
+                    mRotationWatchers.get(i).onRotationChanged(rotation);
+                } catch (RemoteException e) {
+                }
+            }
+        } //end if changed
+        
+        return changed;
+    }
+    
+    public int getRotation() {
+        return mRotation;
+    }
+
+    public int watchRotation(IRotationWatcher watcher) {
+        final IBinder watcherBinder = watcher.asBinder();
+        IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
+            public void binderDied() {
+                synchronized (mWindowMap) {
+                    for (int i=0; i<mRotationWatchers.size(); i++) {
+                        if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
+                            mRotationWatchers.remove(i);
+                            i--;
+                        }
+                    }
+                }
+            }
+        };
+        
+        synchronized (mWindowMap) {
+            try {
+                watcher.asBinder().linkToDeath(dr, 0);
+                mRotationWatchers.add(watcher);
+            } catch (RemoteException e) {
+                // Client died, no cleanup needed.
+            }
+            
+            return mRotation;
+        }
+    }
+
+    /**
+     * Starts the view server on the specified port.
+     *
+     * @param port The port to listener to.
+     *
+     * @return True if the server was successfully started, false otherwise.
+     *
+     * @see com.android.server.ViewServer
+     * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT
+     */
+    public boolean startViewServer(int port) {
+        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+            return false;
+        }
+
+        if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) {
+            return false;
+        }
+
+        if (port < 1024) {
+            return false;
+        }
+
+        if (mViewServer != null) {
+            if (!mViewServer.isRunning()) {
+                try {
+                    return mViewServer.start();
+                } catch (IOException e) {
+                    Log.w(TAG, "View server did not start");                    
+                }
+            }
+            return false;
+        }
+
+        try {
+            mViewServer = new ViewServer(this, port);
+            return mViewServer.start();
+        } catch (IOException e) {
+            Log.w(TAG, "View server did not start");
+        }
+        return false;
+    }
+
+    /**
+     * Stops the view server if it exists.
+     *
+     * @return True if the server stopped, false if it wasn't started or
+     *         couldn't be stopped.
+     *
+     * @see com.android.server.ViewServer
+     */
+    public boolean stopViewServer() {
+        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+            return false;
+        }
+
+        if (!checkCallingPermission(Manifest.permission.DUMP, "stopViewServer")) {
+            return false;
+        }
+
+        if (mViewServer != null) {
+            return mViewServer.stop();
+        }
+        return false;
+    }
+
+    /**
+     * Indicates whether the view server is running.
+     *
+     * @return True if the server is running, false otherwise.
+     *
+     * @see com.android.server.ViewServer
+     */
+    public boolean isViewServerRunning() {
+        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+            return false;
+        }
+
+        if (!checkCallingPermission(Manifest.permission.DUMP, "isViewServerRunning")) {
+            return false;
+        }
+
+        return mViewServer != null && mViewServer.isRunning();
+    }
+
+    /**
+     * Lists all availble windows in the system. The listing is written in the
+     * specified Socket's output stream with the following syntax:
+     * windowHashCodeInHexadecimal windowName
+     * Each line of the ouput represents a different window.
+     *
+     * @param client The remote client to send the listing to.
+     * @return False if an error occured, true otherwise.
+     */
+    boolean viewServerListWindows(Socket client) {
+        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+            return false;
+        }
+
+        boolean result = true;
+
+        Object[] windows;
+        synchronized (mWindowMap) {
+            windows = new Object[mWindows.size()];
+            //noinspection unchecked
+            windows = mWindows.toArray(windows);
+        }
+
+        BufferedWriter out = null;
+
+        // Any uncaught exception will crash the system process
+        try {
+            OutputStream clientStream = client.getOutputStream();
+            out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
+
+            final int count = windows.length;
+            for (int i = 0; i < count; i++) {
+                final WindowState w = (WindowState) windows[i];
+                out.write(Integer.toHexString(System.identityHashCode(w)));
+                out.write(' ');
+                out.append(w.mAttrs.getTitle());
+                out.write('\n');
+            }
+
+            out.write("DONE.\n");
+            out.flush();
+        } catch (Exception e) {
+            result = false;
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    result = false;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Sends a command to a target window. The result of the command, if any, will be
+     * written in the output stream of the specified socket.
+     *
+     * The parameters must follow this syntax:
+     * windowHashcode extra
+     *
+     * Where XX is the length in characeters of the windowTitle.
+     *
+     * The first parameter is the target window. The window with the specified hashcode
+     * will be the target. If no target can be found, nothing happens. The extra parameters
+     * will be delivered to the target window and as parameters to the command itself.
+     *
+     * @param client The remote client to sent the result, if any, to.
+     * @param command The command to execute.
+     * @param parameters The command parameters.
+     *
+     * @return True if the command was successfully delivered, false otherwise. This does
+     *         not indicate whether the command itself was successful.
+     */
+    boolean viewServerWindowCommand(Socket client, String command, String parameters) {
+        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+            return false;
+        }
+
+        boolean success = true;
+        Parcel data = null;
+        Parcel reply = null;
+
+        // Any uncaught exception will crash the system process
+        try {
+            // Find the hashcode of the window
+            int index = parameters.indexOf(' ');
+            if (index == -1) {
+                index = parameters.length();
+            }
+            final String code = parameters.substring(0, index);
+            int hashCode = "ffffffff".equals(code) ? -1 : Integer.parseInt(code, 16);
+
+            // Extract the command's parameter after the window description
+            if (index < parameters.length()) {
+                parameters = parameters.substring(index + 1);
+            } else {
+                parameters = "";
+            }
+
+            final WindowManagerService.WindowState window = findWindow(hashCode);
+            if (window == null) {
+                return false;
+            }
+
+            data = Parcel.obtain();
+            data.writeInterfaceToken("android.view.IWindow");
+            data.writeString(command);
+            data.writeString(parameters);
+            data.writeInt(1);
+            ParcelFileDescriptor.fromSocket(client).writeToParcel(data, 0);
+
+            reply = Parcel.obtain();
+
+            final IBinder binder = window.mClient.asBinder();
+            // TODO: GET THE TRANSACTION CODE IN A SAFER MANNER
+            binder.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+
+            reply.readException();
+
+        } catch (Exception e) {
+            Log.w(TAG, "Could not send command " + command + " with parameters " + parameters, e);
+            success = false;
+        } finally {
+            if (data != null) {
+                data.recycle();
+            }
+            if (reply != null) {
+                reply.recycle();
+            }
+        }
+
+        return success;
+    }
+
+    private WindowState findWindow(int hashCode) {
+        if (hashCode == -1) {
+            return getFocusedWindow();
+        }
+
+        synchronized (mWindowMap) {
+            final ArrayList windows = mWindows;
+            final int count = windows.size();
+
+            for (int i = 0; i < count; i++) {
+                WindowState w = (WindowState) windows.get(i);
+                if (System.identityHashCode(w) == hashCode) {
+                    return w;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * Instruct the Activity Manager to fetch the current configuration and broadcast
+     * that to config-changed listeners if appropriate.
+     */
+    void sendNewConfiguration() {
+        try {
+            mActivityManager.updateConfiguration(null);
+        } catch (RemoteException e) {
+        }
+    }
+    
+    public Configuration computeNewConfiguration() {
+        synchronized (mWindowMap) {
+            if (mDisplay == null) {
+                return null;
+            }
+            Configuration config = new Configuration();
+            mQueue.getInputConfiguration(config);
+            final int dw = mDisplay.getWidth();
+            final int dh = mDisplay.getHeight();
+            int orientation = Configuration.ORIENTATION_SQUARE;
+            if (dw < dh) {
+                orientation = Configuration.ORIENTATION_PORTRAIT;
+            } else if (dw > dh) {
+                orientation = Configuration.ORIENTATION_LANDSCAPE;
+            }
+            config.orientation = orientation;
+            config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
+            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
+            mPolicy.adjustConfigurationLw(config);
+            Log.i(TAG, "Input configuration changed: " + config);
+            long now = SystemClock.uptimeMillis();
+            //Log.i(TAG, "Config changing, gc pending: " + mFreezeGcPending + ", now " + now);
+            if (mFreezeGcPending != 0) {
+                if (now > (mFreezeGcPending+1000)) {
+                    //Log.i(TAG, "Gc!  " + now + " > " + (mFreezeGcPending+1000));
+                    mH.removeMessages(H.FORCE_GC);
+                    Runtime.getRuntime().gc();
+                    mFreezeGcPending = now;
+                }
+            } else {
+                mFreezeGcPending = now;
+            }
+            return config;
+        }
+    }
+    
+    // -------------------------------------------------------------
+    // Input Events and Focus Management
+    // -------------------------------------------------------------
+
+    private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
+        if (targetWin == null ||
+                targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
+            mPowerManager.userActivity(SystemClock.uptimeMillis(), false, eventType);
+        }
+    }
+
+    // tells if it's a cheek event or not -- this function is stateful
+    private static final int EVENT_NONE = 0;
+    private static final int EVENT_UNKNOWN = 0;
+    private static final int EVENT_CHEEK = 0;
+    private static final int EVENT_IGNORE_DURATION = 300; // ms
+    private static final float CHEEK_THRESHOLD = 0.6f;
+    private int mEventState = EVENT_NONE;
+    private float mEventSize;
+    private int eventType(MotionEvent ev) {
+        float size = ev.getSize();
+        switch (ev.getAction()) {
+        case MotionEvent.ACTION_DOWN:
+            mEventSize = size;
+            return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_EVENT;
+        case MotionEvent.ACTION_UP:
+            if (size > mEventSize) mEventSize = size;
+            return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : OTHER_EVENT;
+        case MotionEvent.ACTION_MOVE:
+            final int N = ev.getHistorySize();
+            if (size > mEventSize) mEventSize = size;
+            if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
+            for (int i=0; i<N; i++) {
+                size = ev.getHistoricalSize(i);
+                if (size > mEventSize) mEventSize = size;
+                if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
+            }
+            if (ev.getEventTime() < ev.getDownTime() + EVENT_IGNORE_DURATION) {
+                return TOUCH_EVENT;
+            } else {
+                return OTHER_EVENT;
+            }
+        default:
+            // not good
+            return OTHER_EVENT;
+        }
+    }
+
+    /**
+     * @return Returns true if event was dispatched, false if it was dropped for any reason
+     */
+    private boolean dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
+        if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG,
+                "dispatchPointer " + ev);
+
+        Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
+                ev, true, false);
+        
+        int action = ev.getAction();
+        
+        if (action == MotionEvent.ACTION_UP) {
+            // let go of our target
+            mKeyWaiter.mMotionTarget = null;
+            mPowerManager.logPointerUpEvent();
+        } else if (action == MotionEvent.ACTION_DOWN) {
+            mPowerManager.logPointerDownEvent();
+        }
+
+        if (targetObj == null) {
+            // In this case we are either dropping the event, or have received
+            // a move or up without a down.  It is common to receive move
+            // events in such a way, since this means the user is moving the
+            // pointer without actually pressing down.  All other cases should
+            // be atypical, so let's log them.
+            if (ev.getAction() != MotionEvent.ACTION_MOVE) {
+                Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());
+            }
+            if (qev != null) {
+                mQueue.recycleEvent(qev);
+            }
+            ev.recycle();
+            return false;
+        }
+        if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+            if (qev != null) {
+                mQueue.recycleEvent(qev);
+            }
+            ev.recycle();
+            return true;
+        }
+        
+        WindowState target = (WindowState)targetObj;
+        
+        final long eventTime = ev.getEventTime();
+        
+        //Log.i(TAG, "Sending " + ev + " to " + target);
+
+        if (uid != 0 && uid != target.mSession.mUid) {
+            if (mContext.checkPermission(
+                    android.Manifest.permission.INJECT_EVENTS, pid, uid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Log.w(TAG, "Permission denied: injecting pointer event from pid "
+                        + pid + " uid " + uid + " to window " + target
+                        + " owned by uid " + target.mSession.mUid);
+                if (qev != null) {
+                    mQueue.recycleEvent(qev);
+                }
+                ev.recycle();
+                return false;
+            }
+        }
+        
+        if ((target.mAttrs.flags & 
+                        WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
+            //target wants to ignore fat touch events
+            boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev);
+            //explicit flag to return without processing event further
+            boolean returnFlag = false;
+            if((action == MotionEvent.ACTION_DOWN)) {
+                mFatTouch = false;
+                if(cheekPress) {
+                    mFatTouch = true;
+                    returnFlag = true;
+                }
+            } else {
+                if(action == MotionEvent.ACTION_UP) {
+                    if(mFatTouch) {
+                        //earlier even was invalid doesnt matter if current up is cheekpress or not
+                        mFatTouch = false;
+                        returnFlag = true;
+                    } else if(cheekPress) {
+                        //cancel the earlier event
+                        ev.setAction(MotionEvent.ACTION_CANCEL);
+                        action = MotionEvent.ACTION_CANCEL;
+                    }
+                } else if(action == MotionEvent.ACTION_MOVE) {
+                    if(mFatTouch) {
+                        //two cases here
+                        //an invalid down followed by 0 or moves(valid or invalid)
+                        //a valid down,  invalid move, more moves. want to ignore till up 
+                        returnFlag = true;
+                    } else if(cheekPress) {
+                        //valid down followed by invalid moves
+                        //an invalid move have to cancel earlier action
+                        ev.setAction(MotionEvent.ACTION_CANCEL);
+                        action = MotionEvent.ACTION_CANCEL;
+                        if (DEBUG_INPUT) Log.v(TAG, "Sending cancel for invalid ACTION_MOVE");
+                        //note that the subsequent invalid moves will not get here
+                        mFatTouch = true;
+                    }
+                }
+            } //else if action
+            if(returnFlag) {
+                //recycle que, ev
+                if (qev != null) {
+                    mQueue.recycleEvent(qev);
+                }
+                ev.recycle();
+                return false;
+            }
+        } //end if target
+        
+        synchronized(mWindowMap) {
+            if (qev != null && action == MotionEvent.ACTION_MOVE) {
+                mKeyWaiter.bindTargetWindowLocked(target,
+                        KeyWaiter.RETURN_PENDING_POINTER, qev);
+                ev = null;
+            } else {
+                if (action == MotionEvent.ACTION_DOWN) {
+                    WindowState out = mKeyWaiter.mOutsideTouchTargets;
+                    if (out != null) {
+                        MotionEvent oev = MotionEvent.obtain(ev);
+                        oev.setAction(MotionEvent.ACTION_OUTSIDE);
+                        do {
+                            final Rect frame = out.mFrame;
+                            oev.offsetLocation(-(float)frame.left, -(float)frame.top);
+                            try {
+                                out.mClient.dispatchPointer(oev, eventTime);
+                            } catch (android.os.RemoteException e) {
+                                Log.i(TAG, "WINDOW DIED during outside motion dispatch: " + out);
+                            }
+                            oev.offsetLocation((float)frame.left, (float)frame.top);
+                            out = out.mNextOutsideTouch;
+                        } while (out != null);
+                        mKeyWaiter.mOutsideTouchTargets = null;
+                    }
+                }
+                final Rect frame = target.mFrame;
+                ev.offsetLocation(-(float)frame.left, -(float)frame.top);
+                mKeyWaiter.bindTargetWindowLocked(target);
+            }
+        }
+        
+        // finally offset the event to the target's coordinate system and
+        // dispatch the event.
+        try {
+            if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
+                Log.v(TAG, "Delivering pointer " + qev + " to " + target);
+            }
+            target.mClient.dispatchPointer(ev, eventTime);
+            return true;
+        } catch (android.os.RemoteException e) {
+            Log.i(TAG, "WINDOW DIED during motion dispatch: " + target);
+            mKeyWaiter.mMotionTarget = null;
+            try {
+                removeWindow(target.mSession, target.mClient);
+            } catch (java.util.NoSuchElementException ex) {
+                // This will happen if the window has already been
+                // removed.
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * @return Returns true if event was dispatched, false if it was dropped for any reason
+     */
+    private boolean dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
+        if (DEBUG_INPUT) Log.v(
+                TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
+        
+        Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
+                ev, false, false);
+        if (focusObj == null) {
+            Log.w(TAG, "No focus window, dropping trackball: " + ev);
+            if (qev != null) {
+                mQueue.recycleEvent(qev);
+            }
+            ev.recycle();
+            return false;
+        }
+        if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+            if (qev != null) {
+                mQueue.recycleEvent(qev);
+            }
+            ev.recycle();
+            return true;
+        }
+        
+        WindowState focus = (WindowState)focusObj;
+        
+        if (uid != 0 && uid != focus.mSession.mUid) {
+            if (mContext.checkPermission(
+                    android.Manifest.permission.INJECT_EVENTS, pid, uid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Log.w(TAG, "Permission denied: injecting key event from pid "
+                        + pid + " uid " + uid + " to window " + focus
+                        + " owned by uid " + focus.mSession.mUid);
+                if (qev != null) {
+                    mQueue.recycleEvent(qev);
+                }
+                ev.recycle();
+                return false;
+            }
+        }
+        
+        final long eventTime = ev.getEventTime();
+        
+        synchronized(mWindowMap) {
+            if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) {
+                mKeyWaiter.bindTargetWindowLocked(focus,
+                        KeyWaiter.RETURN_PENDING_TRACKBALL, qev);
+                // We don't deliver movement events to the client, we hold
+                // them and wait for them to call back.
+                ev = null;
+            } else {
+                mKeyWaiter.bindTargetWindowLocked(focus);
+            }
+        }
+        
+        try {
+            focus.mClient.dispatchTrackball(ev, eventTime);
+            return true;
+        } catch (android.os.RemoteException e) {
+            Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
+            try {
+                removeWindow(focus.mSession, focus.mClient);
+            } catch (java.util.NoSuchElementException ex) {
+                // This will happen if the window has already been
+                // removed.
+            }
+        }
+        
+        return false;
+    }
+    
+    /**
+     * @return Returns true if event was dispatched, false if it was dropped for any reason
+     */
+    private boolean dispatchKey(KeyEvent event, int pid, int uid) {
+        if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event);
+
+        Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
+                null, false, false);
+        if (focusObj == null) {
+            Log.w(TAG, "No focus window, dropping: " + event);
+            return false;
+        }
+        if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+            return true;
+        }
+        
+        WindowState focus = (WindowState)focusObj;
+        
+        if (DEBUG_INPUT) Log.v(
+            TAG, "Dispatching to " + focus + ": " + event);
+
+        if (uid != 0 && uid != focus.mSession.mUid) {
+            if (mContext.checkPermission(
+                    android.Manifest.permission.INJECT_EVENTS, pid, uid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Log.w(TAG, "Permission denied: injecting key event from pid "
+                        + pid + " uid " + uid + " to window " + focus
+                        + " owned by uid " + focus.mSession.mUid);
+                return false;
+            }
+        }
+        
+        synchronized(mWindowMap) {
+            mKeyWaiter.bindTargetWindowLocked(focus);
+        }
+
+        // NOSHIP extra state logging
+        mKeyWaiter.recordDispatchState(event, focus);
+        // END NOSHIP
+        
+        try {
+            if (DEBUG_INPUT || DEBUG_FOCUS) {
+                Log.v(TAG, "Delivering key " + event.getKeyCode()
+                        + " to " + focus);
+            }
+            focus.mClient.dispatchKey(event);
+            return true;
+        } catch (android.os.RemoteException e) {
+            Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
+            try {
+                removeWindow(focus.mSession, focus.mClient);
+            } catch (java.util.NoSuchElementException ex) {
+                // This will happen if the window has already been
+                // removed.
+            }
+        }
+        
+        return false;
+    }
+    
+    public void pauseKeyDispatching(IBinder _token) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "pauseKeyDispatching()")) {
+            return;
+        }
+
+        synchronized (mWindowMap) {
+            WindowToken token = mTokenMap.get(_token);
+            if (token != null) {
+                mKeyWaiter.pauseDispatchingLocked(token);
+            }
+        }
+    }
+
+    public void resumeKeyDispatching(IBinder _token) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "resumeKeyDispatching()")) {
+            return;
+        }
+
+        synchronized (mWindowMap) {
+            WindowToken token = mTokenMap.get(_token);
+            if (token != null) {
+                mKeyWaiter.resumeDispatchingLocked(token);
+            }
+        }
+    }
+
+    public void setEventDispatching(boolean enabled) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "resumeKeyDispatching()")) {
+            return;
+        }
+
+        synchronized (mWindowMap) {
+            mKeyWaiter.setEventDispatchingLocked(enabled);
+        }
+    }
+    
+    /**
+     * Injects a keystroke event into the UI.
+     * 
+     * @param ev A motion event describing the keystroke action.  (Be sure to use 
+     * {@link SystemClock#uptimeMillis()} as the timebase.)
+     * @param sync If true, wait for the event to be completed before returning to the caller.
+     * @return Returns true if event was dispatched, false if it was dropped for any reason
+     */
+    public boolean injectKeyEvent(KeyEvent ev, boolean sync) {
+        long downTime = ev.getDownTime();
+        long eventTime = ev.getEventTime();
+
+        int action = ev.getAction();
+        int code = ev.getKeyCode();
+        int repeatCount = ev.getRepeatCount();
+        int metaState = ev.getMetaState();
+        int deviceId = ev.getDeviceId();
+        int scancode = ev.getScanCode();
+
+        if (eventTime == 0) eventTime = SystemClock.uptimeMillis();
+        if (downTime == 0) downTime = eventTime;
+
+        KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
+                deviceId, scancode);
+
+        boolean result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid());
+        if (sync) {
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+        }
+        return result;
+    }
+
+    /**
+     * Inject a pointer (touch) event into the UI.
+     * 
+     * @param ev A motion event describing the pointer (touch) action.  (As noted in 
+     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 
+     * {@link SystemClock#uptimeMillis()} as the timebase.)
+     * @param sync If true, wait for the event to be completed before returning to the caller.
+     * @return Returns true if event was dispatched, false if it was dropped for any reason
+     */
+    public boolean injectPointerEvent(MotionEvent ev, boolean sync) {
+        boolean result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        if (sync) {
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+        }
+        return result;        
+    }
+    
+    /**
+     * Inject a trackball (navigation device) event into the UI.
+     * 
+     * @param ev A motion event describing the trackball action.  (As noted in 
+     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 
+     * {@link SystemClock#uptimeMillis()} as the timebase.)
+     * @param sync If true, wait for the event to be completed before returning to the caller.
+     * @return Returns true if event was dispatched, false if it was dropped for any reason
+     */
+    public boolean injectTrackballEvent(MotionEvent ev, boolean sync) {
+        boolean result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        if (sync) {
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+        }
+        return result;
+    }
+    
+    private WindowState getFocusedWindow() {
+        synchronized (mWindowMap) {
+            return getFocusedWindowLocked();
+        }
+    }
+
+    private WindowState getFocusedWindowLocked() {
+        return mCurrentFocus;
+    }
+    
+    /**
+     * This class holds the state for dispatching key events.  This state
+     * is protected by the KeyWaiter instance, NOT by the window lock.  You
+     * can be holding the main window lock while acquire the KeyWaiter lock,
+     * but not the other way around.
+     */
+    final class KeyWaiter {
+        // NOSHIP debugging
+        public class DispatchState {
+            private KeyEvent event;
+            private WindowState focus;
+            private long time;
+            private WindowState lastWin;
+            private IBinder lastBinder;
+            private boolean finished;
+            private boolean gotFirstWindow;
+            private boolean eventDispatching;
+            private long timeToSwitch;
+            private boolean wasFrozen;
+            private boolean focusPaused;
+            
+            DispatchState(KeyEvent theEvent, WindowState theFocus) {
+                focus = theFocus;
+                event = theEvent;
+                time = System.currentTimeMillis();
+                // snapshot KeyWaiter state
+                lastWin = mLastWin;
+                lastBinder = mLastBinder;
+                finished = mFinished;
+                gotFirstWindow = mGotFirstWindow;
+                eventDispatching = mEventDispatching;
+                timeToSwitch = mTimeToSwitch;
+                wasFrozen = mWasFrozen;
+                // cache the paused state at ctor time as well
+                if (theFocus == null || theFocus.mToken == null) {
+                    Log.i(TAG, "focus " + theFocus + " mToken is null at event dispatch!");
+                    focusPaused = false;
+                } else {
+                    focusPaused = theFocus.mToken.paused;
+                }
+            }
+            
+            public String toString() {
+                return "{{" + event + " to " + focus + " @ " + time
+                        + " lw=" + lastWin + " lb=" + lastBinder
+                        + " fin=" + finished + " gfw=" + gotFirstWindow
+                        + " ed=" + eventDispatching + " tts=" + timeToSwitch
+                        + " wf=" + wasFrozen + " fp=" + focusPaused + "}}";
+            }
+        };
+        private DispatchState mDispatchState = null;
+        public void recordDispatchState(KeyEvent theEvent, WindowState theFocus) {
+            mDispatchState = new DispatchState(theEvent, theFocus);
+        }
+        // END NOSHIP
+
+        public static final int RETURN_NOTHING = 0;
+        public static final int RETURN_PENDING_POINTER = 1;
+        public static final int RETURN_PENDING_TRACKBALL = 2;
+        
+        final Object SKIP_TARGET_TOKEN = new Object();
+        final Object CONSUMED_EVENT_TOKEN = new Object();
+        
+        private WindowState mLastWin = null;
+        private IBinder mLastBinder = null;
+        private boolean mFinished = true;
+        private boolean mGotFirstWindow = false;
+        private boolean mEventDispatching = true;
+        private long mTimeToSwitch = 0;
+        /* package */ boolean mWasFrozen = false;
+        
+        // Target of Motion events
+        WindowState mMotionTarget;
+        
+        // Windows above the target who would like to receive an "outside"
+        // touch event for any down events outside of them.
+        WindowState mOutsideTouchTargets;
+
+        /**
+         * Wait for the last event dispatch to complete, then find the next
+         * target that should receive the given event and wait for that one
+         * to be ready to receive it.
+         */
+        Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
+                MotionEvent nextMotion, boolean isPointerEvent,
+                boolean failIfTimeout) {
+            long startTime = SystemClock.uptimeMillis();
+            long keyDispatchingTimeout = 5 * 1000;
+            long waitedFor = 0;
+
+            while (true) {
+                // Figure out which window we care about.  It is either the
+                // last window we are waiting to have process the event or,
+                // if none, then the next window we think the event should go
+                // to.  Note: we retrieve mLastWin outside of the lock, so
+                // it may change before we lock.  Thus we must check it again.
+                WindowState targetWin = mLastWin;
+                boolean targetIsNew = targetWin == null;
+                if (DEBUG_INPUT) Log.v(
+                        TAG, "waitForLastKey: mFinished=" + mFinished +
+                        ", mLastWin=" + mLastWin);
+                if (targetIsNew) {
+                    Object target = findTargetWindow(nextKey, qev, nextMotion,
+                            isPointerEvent);
+                    if (target == SKIP_TARGET_TOKEN) {
+                        // The user has pressed a special key, and we are
+                        // dropping all pending events before it.
+                        if (DEBUG_INPUT) Log.v(TAG, "Skipping: " + nextKey
+                                + " " + nextMotion);
+                        return null;
+                    }
+                    if (target == CONSUMED_EVENT_TOKEN) {
+                        if (DEBUG_INPUT) Log.v(TAG, "Consumed: " + nextKey
+                                + " " + nextMotion);
+                        return target;
+                    }
+                    targetWin = (WindowState)target;
+                }
+                
+                AppWindowToken targetApp = null;
+                
+                // Now: is it okay to send the next event to this window?
+                synchronized (this) {
+                    // First: did we come here based on the last window not
+                    // being null, but it changed by the time we got here?
+                    // If so, try again.
+                    if (!targetIsNew && mLastWin == null) {
+                        continue;
+                    }
+                    
+                    // We never dispatch events if not finished with the
+                    // last one, or the display is frozen.
+                    if (mFinished && !mDisplayFrozen) {
+                        // If event dispatching is disabled, then we
+                        // just consume the events.
+                        if (!mEventDispatching) {
+                            if (DEBUG_INPUT) Log.v(TAG,
+                                    "Skipping event; dispatching disabled: "
+                                    + nextKey + " " + nextMotion);
+                            return null;
+                        }
+                        if (targetWin != null) {
+                            // If this is a new target, and that target is not
+                            // paused or unresponsive, then all looks good to
+                            // handle the event.
+                            if (targetIsNew && !targetWin.mToken.paused) {
+                                return targetWin;
+                            }
+                        
+                        // If we didn't find a target window, and there is no
+                        // focused app window, then just eat the events.
+                        } else if (mFocusedApp == null) {
+                            if (DEBUG_INPUT) Log.v(TAG,
+                                    "Skipping event; no focused app: "
+                                    + nextKey + " " + nextMotion);
+                            return null;
+                        }
+                    }
+                    
+                    if (DEBUG_INPUT) Log.v(
+                            TAG, "Waiting for last key in " + mLastBinder
+                            + " target=" + targetWin
+                            + " mFinished=" + mFinished
+                            + " mDisplayFrozen=" + mDisplayFrozen
+                            + " targetIsNew=" + targetIsNew
+                            + " paused="
+                            + (targetWin != null ? targetWin.mToken.paused : false)
+                            + " mFocusedApp=" + mFocusedApp);
+                    
+                    targetApp = targetWin != null
+                            ? targetWin.mAppToken : mFocusedApp;
+                    
+                    long curTimeout = keyDispatchingTimeout;
+                    if (mTimeToSwitch != 0) {
+                        long now = SystemClock.uptimeMillis();
+                        if (mTimeToSwitch <= now) {
+                            // If an app switch key has been pressed, and we have
+                            // waited too long for the current app to finish
+                            // processing keys, then wait no more!
+                            doFinishedKeyLocked(true);
+                            continue;
+                        }
+                        long switchTimeout = mTimeToSwitch - now;
+                        if (curTimeout > switchTimeout) {
+                            curTimeout = switchTimeout;
+                        }
+                    }
+                    
+                    try {
+                        // after that continue
+                        // processing keys, so we don't get stuck.
+                        if (DEBUG_INPUT) Log.v(
+                                TAG, "Waiting for key dispatch: " + curTimeout);
+                        wait(curTimeout);
+                        if (DEBUG_INPUT) Log.v(TAG, "Finished waiting @"
+                                + SystemClock.uptimeMillis() + " startTime="
+                                + startTime + " switchTime=" + mTimeToSwitch);
+                    } catch (InterruptedException e) {
+                    }
+                }
+
+                // If we were frozen during configuration change, restart the
+                // timeout checks from now; otherwise look at whether we timed
+                // out before awakening.
+                if (mWasFrozen) {
+                    waitedFor = 0;
+                    mWasFrozen = false;
+                } else {
+                    waitedFor = SystemClock.uptimeMillis() - startTime;
+                }
+
+                if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) {
+                    IApplicationToken at = null;
+                    synchronized (this) {
+                        Log.w(TAG, "Key dispatching timed out sending to " +
+                              (targetWin != null ? targetWin.mAttrs.getTitle()
+                              : "<null>"));
+                        // NOSHIP debugging
+                        Log.w(TAG, "Dispatch state: " + mDispatchState);
+                        Log.w(TAG, "Current state:  " + new DispatchState(nextKey, targetWin));
+                        // END NOSHIP
+                        //dump();
+                        if (targetWin != null) {
+                            at = targetWin.getAppToken();
+                        } else if (targetApp != null) {
+                            at = targetApp.appToken;
+                        }
+                    }
+
+                    boolean abort = true;
+                    if (at != null) {
+                        try {
+                            long timeout = at.getKeyDispatchingTimeout();
+                            if (timeout > waitedFor) {
+                                // we did not wait the proper amount of time for this application.
+                                // set the timeout to be the real timeout and wait again.
+                                keyDispatchingTimeout = timeout - waitedFor;
+                                continue;
+                            } else {
+                                abort = at.keyDispatchingTimedOut();
+                            }
+                        } catch (RemoteException ex) {
+                        }
+                    }
+
+                    synchronized (this) {
+                        if (abort && (mLastWin == targetWin || targetWin == null)) {
+                            mFinished = true;
+                            if (mLastWin != null) { 
+                                if (DEBUG_INPUT) Log.v(TAG,
+                                        "Window " + mLastWin +
+                                        " timed out on key input");
+                                if (mLastWin.mToken.paused) {
+                                    Log.w(TAG, "Un-pausing dispatching to this window");
+                                    mLastWin.mToken.paused = false;
+                                }
+                            }
+                            if (mMotionTarget == targetWin) {
+                                mMotionTarget = null;
+                            }
+                            mLastWin = null;
+                            mLastBinder = null;
+                            if (failIfTimeout || targetWin == null) {
+                                return null;
+                            }
+                        } else {
+                            Log.w(TAG, "Continuing to wait for key to be dispatched");
+                            startTime = SystemClock.uptimeMillis();
+                        }
+                    }
+                }
+            }
+        }
+        
+        Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
+                MotionEvent nextMotion, boolean isPointerEvent) {
+            mOutsideTouchTargets = null;
+            
+            if (nextKey != null) {
+                // Find the target window for a normal key event.
+                final int keycode = nextKey.getKeyCode();
+                final int repeatCount = nextKey.getRepeatCount();
+                final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
+                boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
+                if (!dispatch) {
+                    mPolicy.interceptKeyTi(null, keycode,
+                            nextKey.getMetaState(), down, repeatCount);
+                    Log.w(TAG, "Event timeout during app switch: dropping "
+                            + nextKey);
+                    return SKIP_TARGET_TOKEN;
+                }
+                
+                // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")");
+                
+                WindowState focus = null;
+                synchronized(mWindowMap) {
+                    focus = getFocusedWindowLocked();
+                }
+                
+                wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+                
+                if (mPolicy.interceptKeyTi(focus,
+                        keycode, nextKey.getMetaState(), down, repeatCount)) {
+                    return CONSUMED_EVENT_TOKEN;
+                }
+                
+                return focus;
+                
+            } else if (!isPointerEvent) {
+                boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1);
+                if (!dispatch) {
+                    Log.w(TAG, "Event timeout during app switch: dropping trackball "
+                            + nextMotion);
+                    return SKIP_TARGET_TOKEN;
+                }
+                
+                WindowState focus = null;
+                synchronized(mWindowMap) {
+                    focus = getFocusedWindowLocked();
+                }
+                
+                wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+                return focus;
+            }
+            
+            if (nextMotion == null) {
+                return SKIP_TARGET_TOKEN;
+            }
+            
+            boolean dispatch = mKeyWaiter.checkShouldDispatchKey(
+                    KeyEvent.KEYCODE_UNKNOWN);
+            if (!dispatch) {
+                Log.w(TAG, "Event timeout during app switch: dropping pointer "
+                        + nextMotion);
+                return SKIP_TARGET_TOKEN;
+            }
+            
+            // Find the target window for a pointer event.
+            int action = nextMotion.getAction();
+            final float xf = nextMotion.getX();
+            final float yf = nextMotion.getY();
+            final long eventTime = nextMotion.getEventTime();
+            
+            final boolean screenWasOff = qev != null
+                    && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
+            
+            WindowState target = null;
+            
+            synchronized(mWindowMap) {
+                synchronized (this) {
+                    if (action == MotionEvent.ACTION_DOWN) {
+                        if (mMotionTarget != null) {
+                            // this is weird, we got a pen down, but we thought it was
+                            // already down!
+                            // XXX: We should probably send an ACTION_UP to the current
+                            // target.
+                            Log.w(TAG, "Pointer down received while already down in: "
+                                    + mMotionTarget);
+                            mMotionTarget = null;
+                        }
+                        
+                        // ACTION_DOWN is special, because we need to lock next events to
+                        // the window we'll land onto.
+                        final int x = (int)xf;
+                        final int y = (int)yf;
+    
+                        final ArrayList windows = mWindows;
+                        final int N = windows.size();
+                        WindowState topErrWindow = null;
+                        final Rect tmpRect = mTempRect;
+                        for (int i=N-1; i>=0; i--) {
+                            WindowState child = (WindowState)windows.get(i);
+                            //Log.i(TAG, "Checking dispatch to: " + child);
+                            final int flags = child.mAttrs.flags;
+                            if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
+                                if (topErrWindow == null) {
+                                    topErrWindow = child;
+                                }
+                            }
+                            if (!child.isVisibleLw()) {
+                                //Log.i(TAG, "Not visible!");
+                                continue;
+                            }
+                            if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+                                //Log.i(TAG, "Not touchable!");
+                                if ((flags & WindowManager.LayoutParams
+                                        .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
+                                    child.mNextOutsideTouch = mOutsideTouchTargets;
+                                    mOutsideTouchTargets = child;
+                                }
+                                continue;
+                            }
+                            tmpRect.set(child.mFrame);
+                            if (child.mTouchableInsets == ViewTreeObserver
+                                        .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
+                                // The touch is inside of the window if it is
+                                // inside the frame, AND the content part of that
+                                // frame that was given by the application.
+                                tmpRect.left += child.mGivenContentInsets.left;
+                                tmpRect.top += child.mGivenContentInsets.top;
+                                tmpRect.right -= child.mGivenContentInsets.right;
+                                tmpRect.bottom -= child.mGivenContentInsets.bottom;
+                            } else if (child.mTouchableInsets == ViewTreeObserver
+                                        .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
+                                // The touch is inside of the window if it is
+                                // inside the frame, AND the visible part of that
+                                // frame that was given by the application.
+                                tmpRect.left += child.mGivenVisibleInsets.left;
+                                tmpRect.top += child.mGivenVisibleInsets.top;
+                                tmpRect.right -= child.mGivenVisibleInsets.right;
+                                tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
+                            }
+                            final int touchFlags = flags &
+                                (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                                |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+                            if (tmpRect.contains(x, y) || touchFlags == 0) {
+                                //Log.i(TAG, "Using this target!");
+                                if (!screenWasOff || (flags &
+                                        WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
+                                    mMotionTarget = child;
+                                } else {
+                                    //Log.i(TAG, "Waking, skip!");
+                                    mMotionTarget = null;
+                                }
+                                break;
+                            }
+                            
+                            if ((flags & WindowManager.LayoutParams
+                                    .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
+                                child.mNextOutsideTouch = mOutsideTouchTargets;
+                                mOutsideTouchTargets = child;
+                                //Log.i(TAG, "Adding to outside target list: " + child);
+                            }
+                        }
+
+                        // if there's an error window but it's not accepting
+                        // focus (typically because it is not yet visible) just
+                        // wait for it -- any other focused window may in fact
+                        // be in ANR state.
+                        if (topErrWindow != null && mMotionTarget != topErrWindow) {
+                            mMotionTarget = null;
+                        }
+                    }
+                    
+                    target = mMotionTarget;
+                }
+            }
+            
+            wakeupIfNeeded(target, eventType(nextMotion));
+            
+            // Pointer events are a little different -- if there isn't a
+            // target found for any event, then just drop it.
+            return target != null ? target : SKIP_TARGET_TOKEN;
+        }
+        
+        boolean checkShouldDispatchKey(int keycode) {
+            synchronized (this) {
+                if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) {
+                    mTimeToSwitch = 0;
+                    return true;
+                }
+                if (mTimeToSwitch != 0
+                        && mTimeToSwitch < SystemClock.uptimeMillis()) {
+                    return false;
+                }
+                return true;
+            }
+        }
+        
+        void bindTargetWindowLocked(WindowState win,
+                int pendingWhat, QueuedEvent pendingMotion) {
+            synchronized (this) {
+                bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion);
+            }
+        }
+        
+        void bindTargetWindowLocked(WindowState win) {
+            synchronized (this) {
+                bindTargetWindowLockedLocked(win, RETURN_NOTHING, null);
+            }
+        }
+
+        void bindTargetWindowLockedLocked(WindowState win,
+                int pendingWhat, QueuedEvent pendingMotion) {
+            mLastWin = win;
+            mLastBinder = win.mClient.asBinder();
+            mFinished = false;
+            if (pendingMotion != null) {
+                final Session s = win.mSession;
+                if (pendingWhat == RETURN_PENDING_POINTER) {
+                    releasePendingPointerLocked(s);
+                    s.mPendingPointerMove = pendingMotion;
+                    s.mPendingPointerWindow = win;
+                    if (DEBUG_INPUT) Log.v(TAG, 
+                            "bindTargetToWindow " + s.mPendingPointerMove);
+                } else if (pendingWhat == RETURN_PENDING_TRACKBALL) {
+                    releasePendingTrackballLocked(s);
+                    s.mPendingTrackballMove = pendingMotion;
+                    s.mPendingTrackballWindow = win;
+                }
+            }
+        }
+        
+        void releasePendingPointerLocked(Session s) {
+            if (DEBUG_INPUT) Log.v(TAG,
+                    "releasePendingPointer " + s.mPendingPointerMove);
+            if (s.mPendingPointerMove != null) {
+                mQueue.recycleEvent(s.mPendingPointerMove);
+                s.mPendingPointerMove = null;
+            }
+        }
+        
+        void releasePendingTrackballLocked(Session s) {
+            if (s.mPendingTrackballMove != null) {
+                mQueue.recycleEvent(s.mPendingTrackballMove);
+                s.mPendingTrackballMove = null;
+            }
+        }
+        
+        MotionEvent finishedKey(Session session, IWindow client, boolean force,
+                int returnWhat) {
+            if (DEBUG_INPUT) Log.v(
+                TAG, "finishedKey: client=" + client + ", force=" + force);
+
+            if (client == null) {
+                return null;
+            }
+
+            synchronized (this) {
+                if (DEBUG_INPUT) Log.v(
+                    TAG, "finishedKey: client=" + client.asBinder()
+                    + ", force=" + force + ", last=" + mLastBinder
+                    + " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")");
+
+                QueuedEvent qev = null;
+                WindowState win = null;
+                if (returnWhat == RETURN_PENDING_POINTER) {
+                    qev = session.mPendingPointerMove;
+                    win = session.mPendingPointerWindow;
+                    session.mPendingPointerMove = null;
+                    session.mPendingPointerWindow = null;
+                } else if (returnWhat == RETURN_PENDING_TRACKBALL) {
+                    qev = session.mPendingTrackballMove;
+                    win = session.mPendingTrackballWindow;
+                    session.mPendingTrackballMove = null;
+                    session.mPendingTrackballWindow = null;
+                }
+                
+                if (mLastBinder == client.asBinder()) {
+                    if (DEBUG_INPUT) Log.v(
+                        TAG, "finishedKey: last paused="
+                        + ((mLastWin != null) ? mLastWin.mToken.paused : "null"));
+                    if (mLastWin != null && (!mLastWin.mToken.paused || force
+                            || !mEventDispatching)) {
+                        doFinishedKeyLocked(false);
+                    } else {
+                        // Make sure to wake up anyone currently waiting to
+                        // dispatch a key, so they can re-evaluate their
+                        // current situation.
+                        mFinished = true;
+                        notifyAll();
+                    }
+                } else {
+                    if (DEBUG_INPUT || true) Log.v(
+                            TAG, "finishedKey: " + client + " tried to finish but mLastBinder="
+                            + mLastBinder);
+                }
+                
+                if (qev != null) {
+                    MotionEvent res = (MotionEvent)qev.event;
+                    if (DEBUG_INPUT) Log.v(TAG,
+                            "Returning pending motion: " + res);
+                    mQueue.recycleEvent(qev);
+                    if (win != null && returnWhat == RETURN_PENDING_POINTER) {
+                        res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
+                    }
+                    return res;
+                }
+                return null;
+            }
+        }
+
+        void tickle() {
+            synchronized (this) {
+                notifyAll();
+            }
+        }
+        
+        void handleNewWindowLocked(WindowState newWindow) {
+            if (!newWindow.canReceiveKeys()) {
+                return;
+            }
+            synchronized (this) {
+                if (DEBUG_INPUT || true) Log.v(
+                    TAG, "New key dispatch window: win="
+                    + newWindow.mClient.asBinder()
+                    + ", last=" + mLastBinder
+                    + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
+                    + "), finished=" + mFinished + ", paused="
+                    + newWindow.mToken.paused);
+
+                // Displaying a window implicitly causes dispatching to
+                // be unpaused.  (This is to protect against bugs if someone
+                // pauses dispatching but forgets to resume.)
+                newWindow.mToken.paused = false;
+
+                mGotFirstWindow = true;
+                boolean doNotify = true;
+
+                if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
+                    if (DEBUG_INPUT) Log.v(TAG,
+                            "New SYSTEM_ERROR window; resetting state");
+                    mLastWin = null;
+                    mLastBinder = null;
+                    mMotionTarget = null;
+                    mFinished = true;
+                } else if (mLastWin != null) {
+                    // If the new window is above the window we are
+                    // waiting on, then stop waiting and let key dispatching
+                    // start on the new guy.
+                    if (DEBUG_INPUT) Log.v(
+                        TAG, "Last win layer=" + mLastWin.mLayer
+                        + ", new win layer=" + newWindow.mLayer);
+                    if (newWindow.mLayer >= mLastWin.mLayer) {
+                        if (!mLastWin.canReceiveKeys()) {
+                            mLastWin.mToken.paused = false;
+                            if (DEBUG_INPUT || true) Log.v(TAG,
+                                    "Finishing old key to " + mLastWin);
+                            doFinishedKeyLocked(true);  // does a notifyAll()
+                            doNotify = false;
+                        } else {
+                            if (DEBUG_INPUT || true) Log.v(TAG, "mLastWin " + mLastWin
+                                    + " still receiving keys; not resetting dispatch to "
+                                    + newWindow);
+                        }
+                    } else {
+                        // the new window is lower; no need to wake key waiters
+                        if (DEBUG_INPUT || true) Log.v(TAG, 
+                                "New layer " + newWindow.mLayer + " is below last layer "
+                                + mLastWin.mLayer + " - not resetting dispatch");
+                        doNotify = false;
+                    }
+                }
+
+                if (doNotify) {
+                    notifyAll();
+                }
+            }
+        }
+
+        void pauseDispatchingLocked(WindowToken token) {
+            synchronized (this)
+            {
+                if (DEBUG_INPUT) Log.v(TAG, "Pausing WindowToken " + token);
+                token.paused = true;
+
+                /*
+                if (mLastWin != null && !mFinished && mLastWin.mBaseLayer <= layer) {
+                    mPaused = true;
+                } else {
+                    if (mLastWin == null) {
+                        if (Config.LOGI) Log.i(
+                            TAG, "Key dispatching not paused: no last window.");
+                    } else if (mFinished) {
+                        if (Config.LOGI) Log.i(
+                            TAG, "Key dispatching not paused: finished last key.");
+                    } else {
+                        if (Config.LOGI) Log.i(
+                            TAG, "Key dispatching not paused: window in higher layer.");
+                    }
+                }
+                */
+            }
+        }
+
+        void resumeDispatchingLocked(WindowToken token) {
+            synchronized (this) {
+                if (token.paused) {
+                    if (DEBUG_INPUT) Log.v(
+                        TAG, "Resuming WindowToken " + token
+                        + ", last=" + mLastBinder
+                        + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
+                        + "), finished=" + mFinished + ", paused="
+                        + token.paused);
+                    token.paused = false;
+                    if (mLastWin != null && mLastWin.mToken == token && mFinished) {
+                        doFinishedKeyLocked(true);
+                    } else {
+                        notifyAll();
+                    }
+                }
+            }
+        }
+
+        void setEventDispatchingLocked(boolean enabled) {
+            synchronized (this) {
+                mEventDispatching = enabled;
+                notifyAll();
+            }
+        }
+        
+        void appSwitchComing() {
+            synchronized (this) {
+                // Don't wait for more than .5 seconds for app to finish
+                // processing the pending events.
+                long now = SystemClock.uptimeMillis() + 500;
+                if (DEBUG_INPUT) Log.v(TAG, "appSwitchComing: " + now);
+                if (mTimeToSwitch == 0 || now < mTimeToSwitch) {
+                    mTimeToSwitch = now;
+                }
+                notifyAll();
+            }
+        }
+        
+        private final void doFinishedKeyLocked(boolean doRecycle) {
+            if (mLastWin != null) {
+                releasePendingPointerLocked(mLastWin.mSession);
+                releasePendingTrackballLocked(mLastWin.mSession);
+            }
+            
+            if (mLastWin == null || !mLastWin.mToken.paused
+                || !mLastWin.isVisibleLw()) {
+                // If the current window has been paused, we aren't -really-
+                // finished...  so let the waiters still wait.
+                mLastWin = null;
+                mLastBinder = null;
+            }
+            mFinished = true;
+            notifyAll();
+        }
+    }
+
+    private class KeyQ extends KeyInputQueue
+            implements KeyInputQueue.FilterCallback {
+        PowerManager.WakeLock mHoldingScreen;
+        
+        KeyQ() {
+            super(mContext);
+            PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+            mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+                    "KEEP_SCREEN_ON_FLAG");
+            mHoldingScreen.setReferenceCounted(false);
+        }
+
+        @Override
+        boolean preprocessEvent(InputDevice device, RawInputEvent event) {
+            if (mPolicy.preprocessInputEventTq(event)) {
+                return true;
+            }
+            
+            switch (event.type) {
+                case RawInputEvent.EV_KEY: {
+                    // XXX begin hack
+                    if (DEBUG) {
+                        if (event.keycode == KeyEvent.KEYCODE_G) {
+                            if (event.value != 0) {
+                                // G down
+                                mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+                            }
+                            return false;
+                        }
+                        if (event.keycode == KeyEvent.KEYCODE_D) {
+                            if (event.value != 0) {
+                                //dump();
+                            }
+                            return false;
+                        }
+                    }
+                    // XXX end hack
+                    
+                    boolean screenIsOff = !mPowerManager.screenIsOn();
+                    boolean screenIsDim = !mPowerManager.screenIsBright();
+                    int actions = mPolicy.interceptKeyTq(event, !screenIsOff);
+                    
+                    if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
+                        mPowerManager.goToSleep(event.when);
+                    }
+
+                    if (screenIsOff) {
+                        event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
+                    }
+                    if (screenIsDim) {
+                        event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
+                    }
+                    if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
+                        mPowerManager.userActivity(event.when, false,
+                                LocalPowerManager.BUTTON_EVENT);
+                    }
+                    
+                    if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
+                        if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {
+                            filterQueue(this);
+                            mKeyWaiter.appSwitchComing();
+                        }
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+                    
+                case RawInputEvent.EV_REL: {
+                    boolean screenIsOff = !mPowerManager.screenIsOn();
+                    boolean screenIsDim = !mPowerManager.screenIsBright();
+                    if (screenIsOff) {
+                        if (!mPolicy.isWakeRelMovementTq(event.deviceId,
+                                device.classes, event)) {
+                            //Log.i(TAG, "dropping because screenIsOff and !isWakeKey");
+                            return false;
+                        }
+                        event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
+                    }
+                    if (screenIsDim) {
+                        event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
+                    }
+                    return true;
+                }
+                
+                case RawInputEvent.EV_ABS: {
+                    boolean screenIsOff = !mPowerManager.screenIsOn();
+                    boolean screenIsDim = !mPowerManager.screenIsBright();
+                    if (screenIsOff) {
+                        if (!mPolicy.isWakeAbsMovementTq(event.deviceId,
+                                device.classes, event)) {
+                            //Log.i(TAG, "dropping because screenIsOff and !isWakeKey");
+                            return false;
+                        }
+                        event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
+                    }
+                    if (screenIsDim) {
+                        event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
+                    }
+                    return true;
+                }
+                    
+                default:
+                    return true;
+            }
+        }
+
+        public int filterEvent(QueuedEvent ev) {
+            switch (ev.classType) {
+                case RawInputEvent.CLASS_KEYBOARD:
+                    KeyEvent ke = (KeyEvent)ev.event;
+                    if (mPolicy.isMovementKeyTi(ke.getKeyCode())) {
+                        Log.w(TAG, "Dropping movement key during app switch: "
+                                + ke.getKeyCode() + ", action=" + ke.getAction());
+                        return FILTER_REMOVE;
+                    }
+                    return FILTER_ABORT;
+                default:
+                    return FILTER_KEEP;
+            }
+        }
+        
+        /**
+         * Must be called with the main window manager lock held.
+         */
+        void setHoldScreenLocked(boolean holding) {
+            boolean state = mHoldingScreen.isHeld();
+            if (holding != state) {
+                if (holding) {
+                    mHoldingScreen.acquire();
+                } else {
+                    mPolicy.screenOnStopped();
+                    mHoldingScreen.release();
+                }
+            }
+        }
+    };
+
+    public boolean detectSafeMode() {
+        mSafeMode = mPolicy.detectSafeMode();
+        return mSafeMode;
+    }
+    
+    public void systemReady() {
+        mPolicy.systemReady();
+    }
+    
+    private final class InputDispatcherThread extends Thread {
+        // Time to wait when there is nothing to do: 9999 seconds.
+        static final int LONG_WAIT=9999*1000;
+
+        public InputDispatcherThread() {
+            super("InputDispatcher");
+        }
+        
+        @Override
+        public void run() {
+            while (true) {
+                try {
+                    process();
+                } catch (Exception e) {
+                    Log.e(TAG, "Exception in input dispatcher", e);
+                }
+            }
+        }
+        
+        private void process() {
+            android.os.Process.setThreadPriority(
+                    android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
+            
+            // The last key event we saw
+            KeyEvent lastKey = null;
+
+            // Last keydown time for auto-repeating keys
+            long lastKeyTime = SystemClock.uptimeMillis();
+            long nextKeyTime = lastKeyTime+LONG_WAIT;
+
+            // How many successive repeats we generated 
+            int keyRepeatCount = 0;
+
+            // Need to report that configuration has changed?
+            boolean configChanged = false;
+            
+            while (true) {
+                long curTime = SystemClock.uptimeMillis();
+
+                if (DEBUG_INPUT) Log.v(
+                    TAG, "Waiting for next key: now=" + curTime
+                    + ", repeat @ " + nextKeyTime);
+
+                // Retrieve next event, waiting only as long as the next
+                // repeat timeout.  If the configuration has changed, then
+                // don't wait at all -- we'll report the change as soon as
+                // we have processed all events.
+                QueuedEvent ev = mQueue.getEvent(
+                    (int)((!configChanged && curTime < nextKeyTime)
+                            ? (nextKeyTime-curTime) : 0));
+
+                if (DEBUG_INPUT && ev != null) Log.v(
+                        TAG, "Event: type=" + ev.classType + " data=" + ev.event);
+
+                try {
+                    if (ev != null) {
+                        curTime = ev.when;
+                        int eventType;
+                        if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
+                            eventType = eventType((MotionEvent)ev.event);
+                        } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
+                                    ev.classType == RawInputEvent.CLASS_TRACKBALL) {
+                            eventType = LocalPowerManager.BUTTON_EVENT;
+                        } else {
+                            eventType = LocalPowerManager.OTHER_EVENT;
+                        }
+                        mPowerManager.userActivity(curTime, false, eventType);
+                        switch (ev.classType) {
+                            case RawInputEvent.CLASS_KEYBOARD:
+                                KeyEvent ke = (KeyEvent)ev.event;
+                                if (ke.isDown()) {
+                                    lastKey = ke;
+                                    keyRepeatCount = 0;
+                                    lastKeyTime = curTime;
+                                    nextKeyTime = lastKeyTime
+                                            + KEY_REPEAT_FIRST_DELAY;
+                                    if (DEBUG_INPUT) Log.v(
+                                        TAG, "Received key down: first repeat @ "
+                                        + nextKeyTime);
+                                } else {
+                                    lastKey = null;
+                                    // Arbitrary long timeout.
+                                    lastKeyTime = curTime;
+                                    nextKeyTime = curTime + LONG_WAIT;
+                                    if (DEBUG_INPUT) Log.v(
+                                        TAG, "Received key up: ignore repeat @ "
+                                        + nextKeyTime);
+                                }
+                                dispatchKey((KeyEvent)ev.event, 0, 0);
+                                mQueue.recycleEvent(ev);
+                                break;
+                            case RawInputEvent.CLASS_TOUCHSCREEN:
+                                //Log.i(TAG, "Read next event " + ev);
+                                dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
+                                break;
+                            case RawInputEvent.CLASS_TRACKBALL:
+                                dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
+                                break;
+                            case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
+                                configChanged = true;
+                                break;
+                            default:
+                                mQueue.recycleEvent(ev);
+                            break;
+                        }
+                        
+                    } else if (configChanged) {
+                        configChanged = false;
+                        sendNewConfiguration();
+                        
+                    } else if (lastKey != null) {
+                        curTime = SystemClock.uptimeMillis();
+                        
+                        // Timeout occurred while key was down.  If it is at or
+                        // past the key repeat time, dispatch the repeat.
+                        if (DEBUG_INPUT) Log.v(
+                            TAG, "Key timeout: repeat=" + nextKeyTime
+                            + ", now=" + curTime);
+                        if (curTime < nextKeyTime) {
+                            continue;
+                        }
+    
+                        lastKeyTime = nextKeyTime;
+                        nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
+                        keyRepeatCount++;
+                        if (DEBUG_INPUT) Log.v(
+                            TAG, "Key repeat: count=" + keyRepeatCount
+                            + ", next @ " + nextKeyTime);
+                        dispatchKey(new KeyEvent(lastKey, curTime, keyRepeatCount), 0, 0);
+                        
+                    } else {
+                        curTime = SystemClock.uptimeMillis();
+                        
+                        lastKeyTime = curTime;
+                        nextKeyTime = curTime + LONG_WAIT;
+                    }
+                    
+                } catch (Exception e) {
+                    Log.e(TAG,
+                        "Input thread received uncaught exception: " + e, e);
+                }
+            }
+        }
+    }
+
+    // -------------------------------------------------------------
+    // Client Session State
+    // -------------------------------------------------------------
+
+    private final class Session extends IWindowSession.Stub
+            implements IBinder.DeathRecipient {
+        final IInputMethodClient mClient;
+        final IInputContext mInputContext;
+        final int mUid;
+        final int mPid;
+        SurfaceSession mSurfaceSession;
+        int mNumWindow = 0;
+        boolean mClientDead = false;
+        
+        /**
+         * Current pointer move event being dispatched to client window...  must
+         * hold key lock to access.
+         */
+        QueuedEvent mPendingPointerMove;
+        WindowState mPendingPointerWindow;
+        
+        /**
+         * Current trackball move event being dispatched to client window...  must
+         * hold key lock to access.
+         */
+        QueuedEvent mPendingTrackballMove;
+        WindowState mPendingTrackballWindow;
+
+        public Session(IInputMethodClient client, IInputContext inputContext) {
+            mClient = client;
+            mInputContext = inputContext;
+            mUid = Binder.getCallingUid();
+            mPid = Binder.getCallingPid();
+            synchronized (mWindowMap) {
+                if (mInputMethodManager == null && mHaveInputMethods) {
+                    IBinder b = ServiceManager.getService(
+                            Context.INPUT_METHOD_SERVICE);
+                    mInputMethodManager = IInputMethodManager.Stub.asInterface(b);
+                }
+            }
+            long ident = Binder.clearCallingIdentity();
+            try {
+                // Note: it is safe to call in to the input method manager
+                // here because we are not holding our lock.
+                if (mInputMethodManager != null) {
+                    mInputMethodManager.addClient(client, inputContext,
+                            mUid, mPid);
+                } else {
+                    client.setUsingInputMethod(false);
+                }
+                client.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                // The caller has died, so we can just forget about this.
+                try {
+                    if (mInputMethodManager != null) {
+                        mInputMethodManager.removeClient(client);
+                    }
+                } catch (RemoteException ee) {
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+        
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            try {
+                return super.onTransact(code, data, reply, flags);
+            } catch (RuntimeException e) {
+                // Log all 'real' exceptions thrown to the caller
+                if (!(e instanceof SecurityException)) {
+                    Log.e(TAG, "Window Session Crash", e);
+                }
+                throw e;
+            }
+        }
+
+        public void binderDied() {
+            // Note: it is safe to call in to the input method manager
+            // here because we are not holding our lock.
+            try {
+                if (mInputMethodManager != null) {
+                    mInputMethodManager.removeClient(mClient);
+                }
+            } catch (RemoteException e) {
+            }
+            synchronized(mWindowMap) {
+                mClientDead = true;
+                killSessionLocked();
+            }
+        }
+
+        public int add(IWindow window, WindowManager.LayoutParams attrs,
+                int viewVisibility, Rect outContentInsets) {
+            return addWindow(this, window, attrs, viewVisibility, outContentInsets);
+        }
+        
+        public void remove(IWindow window) {
+            removeWindow(this, window);
+        }
+        
+        public int relayout(IWindow window, WindowManager.LayoutParams attrs,
+                int requestedWidth, int requestedHeight, int viewFlags,
+                boolean insetsPending, Rect outFrame, Rect outContentInsets,
+                Rect outVisibleInsets, Surface outSurface) {
+            return relayoutWindow(this, window, attrs,
+                    requestedWidth, requestedHeight, viewFlags, insetsPending,
+                    outFrame, outContentInsets, outVisibleInsets, outSurface);
+        }
+        
+        public void setTransparentRegion(IWindow window, Region region) {
+            setTransparentRegionWindow(this, window, region);
+        }
+        
+        public void setInsets(IWindow window, int touchableInsets,
+                Rect contentInsets, Rect visibleInsets) {
+            setInsetsWindow(this, window, touchableInsets, contentInsets,
+                    visibleInsets);
+        }
+        
+        public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
+            getWindowDisplayFrame(this, window, outDisplayFrame);
+        }
+        
+        public void finishDrawing(IWindow window) {
+            if (localLOGV) Log.v(
+                TAG, "IWindow finishDrawing called for " + window);
+            finishDrawingWindow(this, window);
+        }
+
+        public void finishKey(IWindow window) {
+            if (localLOGV) Log.v(
+                TAG, "IWindow finishKey called for " + window);
+            mKeyWaiter.finishedKey(this, window, false,
+                    KeyWaiter.RETURN_NOTHING);
+        }
+
+        public MotionEvent getPendingPointerMove(IWindow window) {
+            if (localLOGV) Log.v(
+                    TAG, "IWindow getPendingMotionEvent called for " + window);
+            return mKeyWaiter.finishedKey(this, window, false,
+                    KeyWaiter.RETURN_PENDING_POINTER);
+        }
+        
+        public MotionEvent getPendingTrackballMove(IWindow window) {
+            if (localLOGV) Log.v(
+                    TAG, "IWindow getPendingMotionEvent called for " + window);
+            return mKeyWaiter.finishedKey(this, window, false,
+                    KeyWaiter.RETURN_PENDING_TRACKBALL);
+        }
+
+        public void setInTouchMode(boolean mode) {
+            synchronized(mWindowMap) {
+                mInTouchMode = mode;
+            }
+        }
+
+        public boolean getInTouchMode() {
+            synchronized(mWindowMap) {
+                return mInTouchMode;
+            }
+        }
+
+        public boolean performHapticFeedback(IWindow window, int effectId,
+                boolean always) {
+            synchronized(mWindowMap) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    return mPolicy.performHapticFeedback(
+                            windowForClientLocked(this, window), effectId, always);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+        
+        void windowAddedLocked() {
+            if (mSurfaceSession == null) {
+                if (localLOGV) Log.v(
+                    TAG, "First window added to " + this + ", creating SurfaceSession");
+                mSurfaceSession = new SurfaceSession();
+                mSessions.add(this);
+            }
+            mNumWindow++;
+        }
+
+        void windowRemovedLocked() {
+            mNumWindow--;
+            killSessionLocked();
+        }
+    
+        void killSessionLocked() {
+            if (mNumWindow <= 0 && mClientDead) {
+                mSessions.remove(this);
+                if (mSurfaceSession != null) {
+                    if (localLOGV) Log.v(
+                        TAG, "Last window removed from " + this
+                        + ", destroying " + mSurfaceSession);
+                    try {
+                        mSurfaceSession.kill();
+                    } catch (Exception e) {
+                        Log.w(TAG, "Exception thrown when killing surface session "
+                            + mSurfaceSession + " in session " + this
+                            + ": " + e.toString());
+                    }
+                    mSurfaceSession = null;
+                }
+            }
+        }
+        
+        void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + this);
+            pw.println(prefix + "mNumWindow=" + mNumWindow
+                    + " mClientDead=" + mClientDead
+                    + " mSurfaceSession=" + mSurfaceSession);
+            pw.println(prefix + "mPendingPointerWindow=" + mPendingPointerWindow
+                    + " mPendingPointerMove=" + mPendingPointerMove);
+            pw.println(prefix + "mPendingTrackballWindow=" + mPendingTrackballWindow
+                    + " mPendingTrackballMove=" + mPendingTrackballMove);
+        }
+
+        @Override
+        public String toString() {
+            return "Session{"
+                + Integer.toHexString(System.identityHashCode(this)) + "}";
+        }
+    }
+
+    // -------------------------------------------------------------
+    // Client Window State
+    // -------------------------------------------------------------
+
+    private final class WindowState implements WindowManagerPolicy.WindowState {
+        final Session mSession;
+        final IWindow mClient;
+        WindowToken mToken;
+        AppWindowToken mAppToken;
+        AppWindowToken mTargetAppToken;
+        final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
+        final DeathRecipient mDeathRecipient;
+        final WindowState mAttachedWindow;
+        final ArrayList mChildWindows = new ArrayList();
+        final int mBaseLayer;
+        final int mSubLayer;
+        final boolean mLayoutAttached;
+        final boolean mIsImWindow;
+        int mViewVisibility;
+        boolean mPolicyVisibility = true;
+        boolean mPolicyVisibilityAfterAnim = true;
+        boolean mAppFreezing;
+        Surface mSurface;
+        boolean mAttachedHidden;    // is our parent window hidden?
+        boolean mLastHidden;        // was this window last hidden?
+        int mRequestedWidth;
+        int mRequestedHeight;
+        int mLastRequestedWidth;
+        int mLastRequestedHeight;
+        int mReqXPos;
+        int mReqYPos;
+        int mLayer;
+        int mAnimLayer;
+        int mLastLayer;
+        boolean mHaveFrame;
+
+        WindowState mNextOutsideTouch;
+        
+        // Actual frame shown on-screen (may be modified by animation)
+        final Rect mShownFrame = new Rect();
+        final Rect mLastShownFrame = new Rect();
+        
+        /**
+         * Insets that determine the actually visible area
+         */
+        final Rect mVisibleInsets = new Rect();
+        final Rect mLastVisibleInsets = new Rect();
+        boolean mVisibleInsetsChanged;
+
+        /**
+         * Insets that are covered by system windows
+         */
+        final Rect mContentInsets = new Rect();
+        final Rect mLastContentInsets = new Rect();
+        boolean mContentInsetsChanged;
+
+        /**
+         * Set to true if we are waiting for this window to receive its
+         * given internal insets before laying out other windows based on it.
+         */
+        boolean mGivenInsetsPending;
+        
+        /**
+         * These are the content insets that were given during layout for
+         * this window, to be applied to windows behind it.
+         */
+        final Rect mGivenContentInsets = new Rect();
+        
+        /**
+         * These are the visible insets that were given during layout for
+         * this window, to be applied to windows behind it.
+         */
+        final Rect mGivenVisibleInsets = new Rect();
+        
+        /**
+         * Flag indicating whether the touchable region should be adjusted by
+         * the visible insets; if false the area outside the visible insets is
+         * NOT touchable, so we must use those to adjust the frame during hit
+         * tests.
+         */
+        int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+        
+        // Current transformation being applied.
+        float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
+        float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
+        float mHScale=1, mVScale=1;
+        float mLastHScale=1, mLastVScale=1;
+        final Matrix mTmpMatrix = new Matrix();
+
+        // "Real" frame that the application sees.
+        final Rect mFrame = new Rect();
+        final Rect mLastFrame = new Rect();
+
+        final Rect mContainingFrame = new Rect();
+        final Rect mDisplayFrame = new Rect();
+        final Rect mContentFrame = new Rect();
+        final Rect mVisibleFrame = new Rect();
+
+        float mShownAlpha = 1;
+        float mAlpha = 1;
+        float mLastAlpha = 1;
+
+        // Set to true if, when the window gets displayed, it should perform
+        // an enter animation.
+        boolean mEnterAnimationPending;
+
+        // Currently running animation.
+        boolean mAnimating;
+        boolean mLocalAnimating;
+        Animation mAnimation;
+        boolean mAnimationIsEntrance;
+        boolean mHasTransformation;
+        boolean mHasLocalTransformation;
+        final Transformation mTransformation = new Transformation();
+
+        // This is set after IWindowSession.relayout() has been called at
+        // least once for the window.  It allows us to detect the situation
+        // where we don't yet have a surface, but should have one soon, so
+        // we can give the window focus before waiting for the relayout.
+        boolean mRelayoutCalled;
+        
+        // This is set after the Surface has been created but before the
+        // window has been drawn.  During this time the surface is hidden.
+        boolean mDrawPending;
+
+        // This is set after the window has finished drawing for the first
+        // time but before its surface is shown.  The surface will be
+        // displayed when the next layout is run.
+        boolean mCommitDrawPending;
+
+        // This is set during the time after the window's drawing has been
+        // committed, and before its surface is actually shown.  It is used
+        // to delay showing the surface until all windows in a token are ready
+        // to be shown.
+        boolean mReadyToShow;
+        
+        // Set when the window has been shown in the screen the first time.
+        boolean mHasDrawn;
+
+        // Currently running an exit animation?
+        boolean mExiting;
+
+        // Currently on the mDestroySurface list?
+        boolean mDestroying;
+        
+        // Completely remove from window manager after exit animation?
+        boolean mRemoveOnExit;
+
+        // Set when the orientation is changing and this window has not yet
+        // been updated for the new orientation.
+        boolean mOrientationChanging;
+        
+        // Is this window now (or just being) removed?
+        boolean mRemoved;
+        
+        WindowState(Session s, IWindow c, WindowToken token,
+               WindowState attachedWindow, WindowManager.LayoutParams a,
+               int viewVisibility) {
+            mSession = s;
+            mClient = c;
+            mToken = token;
+            mAttrs.copyFrom(a);
+            mViewVisibility = viewVisibility;
+            DeathRecipient deathRecipient = new DeathRecipient();
+            mAlpha = a.alpha;
+            if (localLOGV) Log.v(
+                TAG, "Window " + this + " client=" + c.asBinder()
+                + " token=" + token + " (" + mAttrs.token + ")");
+            try {
+                c.asBinder().linkToDeath(deathRecipient, 0);
+            } catch (RemoteException e) {
+                mDeathRecipient = null;
+                mAttachedWindow = null;
+                mLayoutAttached = false;
+                mIsImWindow = false;
+                mBaseLayer = 0;
+                mSubLayer = 0;
+                return;
+            }
+            mDeathRecipient = deathRecipient;
+            
+            if ((mAttrs.type >= FIRST_SUB_WINDOW &&
+                    mAttrs.type <= LAST_SUB_WINDOW)) {
+                // The multiplier here is to reserve space for multiple
+                // windows in the same type layer.
+                mBaseLayer = mPolicy.windowTypeToLayerLw(
+                        attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER
+                        + TYPE_LAYER_OFFSET;
+                mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
+                mAttachedWindow = attachedWindow;
+                mAttachedWindow.mChildWindows.add(this);
+                mLayoutAttached = mAttrs.type !=
+                        WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+                mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD
+                        || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+            } else {
+                // The multiplier here is to reserve space for multiple
+                // windows in the same type layer.
+                mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
+                        * TYPE_LAYER_MULTIPLIER
+                        + TYPE_LAYER_OFFSET;
+                mSubLayer = 0;
+                mAttachedWindow = null;
+                mLayoutAttached = false;
+                mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
+                        || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+            }
+
+            WindowState appWin = this;
+            while (appWin.mAttachedWindow != null) {
+                appWin = mAttachedWindow;
+            }
+            WindowToken appToken = appWin.mToken;
+            while (appToken.appWindowToken == null) {
+                WindowToken parent = mTokenMap.get(appToken.token);
+                if (parent == null || appToken == parent) {
+                    break;
+                }
+                appToken = parent;
+            }
+            mAppToken = appToken.appWindowToken;
+
+            mSurface = null;
+            mRequestedWidth = 0;
+            mRequestedHeight = 0;
+            mLastRequestedWidth = 0;
+            mLastRequestedHeight = 0;
+            mReqXPos = 0;
+            mReqYPos = 0;
+            mLayer = 0;
+            mAnimLayer = 0;
+            mLastLayer = 0;
+        }
+
+        void attach() {
+            if (localLOGV) Log.v(
+                TAG, "Attaching " + this + " token=" + mToken
+                + ", list=" + mToken.windows);
+            mSession.windowAddedLocked();
+        }
+
+        public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) {
+            mHaveFrame = true;
+
+            final int pw = pf.right-pf.left;
+            final int ph = pf.bottom-pf.top;
+
+            int w,h;
+            if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) {
+                w = mAttrs.width < 0 ? pw : mAttrs.width;
+                h = mAttrs.height< 0 ? ph : mAttrs.height;
+            } else {
+                w = mAttrs.width == mAttrs.FILL_PARENT ? pw : mRequestedWidth;
+                h = mAttrs.height== mAttrs.FILL_PARENT ? ph : mRequestedHeight;
+            }
+            
+            final Rect container = mContainingFrame;
+            container.set(pf);
+
+            final Rect display = mDisplayFrame;
+            display.set(df);
+
+            final Rect content = mContentFrame;
+            content.set(cf);
+            
+            final Rect visible = mVisibleFrame;
+            visible.set(vf);
+            
+            final Rect frame = mFrame;
+            
+            //System.out.println("In: w=" + w + " h=" + h + " container=" +
+            //                   container + " x=" + mAttrs.x + " y=" + mAttrs.y);
+
+            Gravity.apply(mAttrs.gravity, w, h, container,
+                    (int) (mAttrs.x + mAttrs.horizontalMargin * pw),
+                    (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame);
+
+            //System.out.println("Out: " + mFrame);
+
+            // Now make sure the window fits in the overall display.
+            Gravity.applyDisplay(mAttrs.gravity, df, frame);
+            
+            // Make sure the content and visible frames are inside of the
+            // final window frame.
+            if (content.left < frame.left) content.left = frame.left;
+            if (content.top < frame.top) content.top = frame.top;
+            if (content.right > frame.right) content.right = frame.right;
+            if (content.bottom > frame.bottom) content.bottom = frame.bottom;
+            if (visible.left < frame.left) visible.left = frame.left;
+            if (visible.top < frame.top) visible.top = frame.top;
+            if (visible.right > frame.right) visible.right = frame.right;
+            if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;
+            
+            final Rect contentInsets = mContentInsets;
+            contentInsets.left = content.left-frame.left;
+            contentInsets.top = content.top-frame.top;
+            contentInsets.right = frame.right-content.right;
+            contentInsets.bottom = frame.bottom-content.bottom;
+            
+            final Rect visibleInsets = mVisibleInsets;
+            visibleInsets.left = visible.left-frame.left;
+            visibleInsets.top = visible.top-frame.top;
+            visibleInsets.right = frame.right-visible.right;
+            visibleInsets.bottom = frame.bottom-visible.bottom;
+            
+            if (localLOGV) {
+                //if ("com.google.android.youtube".equals(mAttrs.packageName)
+                //        && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+                    Log.v(TAG, "Resolving (mRequestedWidth="
+                            + mRequestedWidth + ", mRequestedheight="
+                            + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+                            + "): frame=" + mFrame.toShortString()
+                            + " ci=" + contentInsets.toShortString()
+                            + " vi=" + visibleInsets.toShortString());
+                //}
+            }
+        }
+        
+        public Rect getFrameLw() {
+            return mFrame;
+        }
+
+        public Rect getShownFrameLw() {
+            return mShownFrame;
+        }
+
+        public Rect getDisplayFrameLw() {
+            return mDisplayFrame;
+        }
+
+        public Rect getContentFrameLw() {
+            return mContentFrame;
+        }
+
+        public Rect getVisibleFrameLw() {
+            return mVisibleFrame;
+        }
+
+        public boolean getGivenInsetsPendingLw() {
+            return mGivenInsetsPending;
+        }
+
+        public Rect getGivenContentInsetsLw() {
+            return mGivenContentInsets;
+        }
+        
+        public Rect getGivenVisibleInsetsLw() {
+            return mGivenVisibleInsets;
+        }
+        
+        public WindowManager.LayoutParams getAttrs() {
+            return mAttrs;
+        }
+
+        public int getSurfaceLayer() {
+            return mLayer;
+        }
+        
+        public IApplicationToken getAppToken() {
+            return mAppToken != null ? mAppToken.appToken : null;
+        }
+
+        public boolean hasAppShownWindows() {
+            return mAppToken != null ? mAppToken.firstWindowDrawn : false;
+        }
+
+        public boolean hasAppStartingIcon() {
+            return mAppToken != null ? (mAppToken.startingData != null) : false;
+        }
+
+        public WindowManagerPolicy.WindowState getAppStartingWindow() {
+            return mAppToken != null ? mAppToken.startingWindow : null;
+        }
+
+        public void setAnimation(Animation anim) {
+            if (localLOGV) Log.v(
+                TAG, "Setting animation in " + this + ": " + anim);
+            mAnimating = false;
+            mLocalAnimating = false;
+            mAnimation = anim;
+            mAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+            mAnimation.scaleCurrentDuration(mWindowAnimationScale);
+        }
+
+        public void clearAnimation() {
+            if (mAnimation != null) {
+                mAnimating = true;
+                mLocalAnimating = false;
+                mAnimation = null;
+            }
+        }
+        
+        Surface createSurfaceLocked() {
+            if (mSurface == null) {
+                mDrawPending = true;
+                mCommitDrawPending = false;
+                mReadyToShow = false;
+                if (mAppToken != null) {
+                    mAppToken.allDrawn = false;
+                }
+
+                int flags = 0;
+                if (mAttrs.memoryType == MEMORY_TYPE_HARDWARE) {
+                    flags |= Surface.HARDWARE;
+                } else if (mAttrs.memoryType == MEMORY_TYPE_GPU) {
+                    flags |= Surface.GPU;
+                } else if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
+                    flags |= Surface.PUSH_BUFFERS;
+                }
+
+                if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+                    flags |= Surface.SECURE;
+                }
+                if (DEBUG_VISIBILITY) Log.v(
+                    TAG, "Creating surface in session "
+                    + mSession.mSurfaceSession + " window " + this
+                    + " w=" + mFrame.width()
+                    + " h=" + mFrame.height() + " format="
+                    + mAttrs.format + " flags=" + flags);
+
+                int w = mFrame.width();
+                int h = mFrame.height();
+                if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
+                    // for a scaled surface, we always want the requested
+                    // size.
+                    w = mRequestedWidth;
+                    h = mRequestedHeight;
+                }
+
+                try {
+                    mSurface = new Surface(
+                            mSession.mSurfaceSession, mSession.mPid, 
+                            0, w, h, mAttrs.format, flags);
+                } catch (Surface.OutOfResourcesException e) {
+                    Log.w(TAG, "OutOfResourcesException creating surface");
+                    reclaimSomeSurfaceMemoryLocked(this, "create");
+                    return null;
+                } catch (Exception e) {
+                    Log.e(TAG, "Exception creating surface", e);
+                    return null;
+                }
+                
+                if (localLOGV) Log.v(
+                    TAG, "Got surface: " + mSurface
+                    + ", set left=" + mFrame.left + " top=" + mFrame.top
+                    + ", animLayer=" + mAnimLayer);
+                if (SHOW_TRANSACTIONS) {
+                    Log.i(TAG, ">>> OPEN TRANSACTION");
+                    Log.i(TAG, "  SURFACE " + mSurface + ": CREATE ("
+                            + mAttrs.getTitle() + ") pos=(" +
+                          mFrame.left + "," + mFrame.top + ") (" +
+                          mFrame.width() + "x" + mFrame.height() + "), layer=" +
+                          mAnimLayer + " HIDE");
+                }
+                Surface.openTransaction();
+                try {
+                    try {
+                        mSurface.setPosition(mFrame.left, mFrame.top);
+                        mSurface.setLayer(mAnimLayer);
+                        mSurface.hide();
+                        if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
+                            mSurface.setFlags(Surface.SURFACE_DITHER,
+                                    Surface.SURFACE_DITHER);
+                        }
+                    } catch (RuntimeException e) {
+                        Log.w(TAG, "Error creating surface in " + w, e);
+                        reclaimSomeSurfaceMemoryLocked(this, "create-init");
+                    }
+                    mLastHidden = true;
+                } finally {
+                    if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION");
+                    Surface.closeTransaction();
+                }
+                if (localLOGV) Log.v(
+                        TAG, "Created surface " + this);
+            }
+            return mSurface;
+        }
+        
+        void destroySurfaceLocked() {
+            // Window is no longer on-screen, so can no longer receive
+            // key events...  if we were waiting for it to finish
+            // handling a key event, the wait is over!
+            mKeyWaiter.finishedKey(mSession, mClient, true,
+                    KeyWaiter.RETURN_NOTHING);
+            mKeyWaiter.releasePendingPointerLocked(mSession);
+            mKeyWaiter.releasePendingTrackballLocked(mSession);
+
+            if (mAppToken != null && this == mAppToken.startingWindow) {
+                mAppToken.startingDisplayed = false;
+            }
+            
+            if (localLOGV) Log.v(
+                TAG, "Window " + this
+                + " destroying surface " + mSurface + ", session " + mSession);
+            if (mSurface != null) {
+                try {
+                    if (SHOW_TRANSACTIONS) {
+                        RuntimeException ex = new RuntimeException();
+                        ex.fillInStackTrace();
+                        Log.i(TAG, "  SURFACE " + mSurface + ": DESTROY ("
+                                + mAttrs.getTitle() + ")", ex);
+                    }
+                    mSurface.clear();
+                } catch (RuntimeException e) {
+                    Log.w(TAG, "Exception thrown when destroying Window " + this
+                        + " surface " + mSurface + " session " + mSession
+                        + ": " + e.toString());
+                }
+                mSurface = null;
+                mDrawPending = false;
+                mCommitDrawPending = false;
+                mReadyToShow = false;
+
+                int i = mChildWindows.size();
+                while (i > 0) {
+                    i--;
+                    WindowState c = (WindowState)mChildWindows.get(i);
+                    c.mAttachedHidden = true;
+                }
+            }
+        }
+
+        boolean finishDrawingLocked() {
+            if (mDrawPending) {
+                if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Log.v(
+                    TAG, "finishDrawingLocked: " + mSurface);
+                mCommitDrawPending = true;
+                mDrawPending = false;
+                return true;
+            }
+            return false;
+        }
+
+        // This must be called while inside a transaction.
+        void commitFinishDrawingLocked(long currentTime) {
+            //Log.i(TAG, "commitFinishDrawingLocked: " + mSurface);
+            if (!mCommitDrawPending) {
+                return;
+            }
+            mCommitDrawPending = false;
+            mReadyToShow = true;
+            final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING;
+            final AppWindowToken atoken = mAppToken;
+            if (atoken == null || atoken.allDrawn || starting) {
+                performShowLocked();
+            }
+        }
+
+        // This must be called while inside a transaction.
+        boolean performShowLocked() {
+            if (DEBUG_VISIBILITY) {
+                RuntimeException e = new RuntimeException();
+                e.fillInStackTrace();
+                Log.v(TAG, "performShow on " + this
+                        + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay()
+                        + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e);
+            }
+            if (mReadyToShow && isReadyForDisplay()) {
+                if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Log.i(
+                        TAG, "  SURFACE " + mSurface + ": SHOW (performShowLocked)");
+                if (DEBUG_VISIBILITY) Log.v(TAG, "Showing " + this
+                        + " during animation: policyVis=" + mPolicyVisibility
+                        + " attHidden=" + mAttachedHidden
+                        + " tok.hiddenRequested="
+                        + (mAppToken != null ? mAppToken.hiddenRequested : false)
+                        + " tok.idden="
+                        + (mAppToken != null ? mAppToken.hidden : false)
+                        + " animating=" + mAnimating
+                        + " tok animating="
+                        + (mAppToken != null ? mAppToken.animating : false));
+                if (!showSurfaceRobustlyLocked(this)) {
+                    return false;
+                }
+                mLastAlpha = -1;
+                mHasDrawn = true;
+                mLastHidden = false;
+                mReadyToShow = false;
+                enableScreenIfNeededLocked();
+
+                applyEnterAnimationLocked(this);
+                
+                int i = mChildWindows.size();
+                while (i > 0) {
+                    i--;
+                    WindowState c = (WindowState)mChildWindows.get(i);
+                    if (c.mSurface != null && c.mAttachedHidden) {
+                        c.mAttachedHidden = false;
+                        c.performShowLocked();
+                    }
+                }
+                
+                if (mAttrs.type != TYPE_APPLICATION_STARTING
+                        && mAppToken != null) {
+                    mAppToken.firstWindowDrawn = true;
+                    if (mAnimation == null && mAppToken.startingData != null) {
+                        if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Finish starting "
+                                + mToken
+                                + ": first real window is shown, no animation");
+                        mFinishedStarting.add(mAppToken);
+                        mH.sendEmptyMessage(H.FINISHED_STARTING);
+                    }
+                    mAppToken.updateReportedVisibilityLocked();
+                }
+            }
+            return true;
+        }
+        
+        // This must be called while inside a transaction.  Returns true if
+        // there is more animation to run.
+        boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+            if (!mDisplayFrozen) {
+                // We will run animations as long as the display isn't frozen.
+                
+                if (!mDrawPending && !mCommitDrawPending && mAnimation != null) {
+                    mHasTransformation = true;
+                    mHasLocalTransformation = true;
+                    if (!mLocalAnimating) {
+                        if (DEBUG_ANIM) Log.v(
+                            TAG, "Starting animation in " + this +
+                            " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() +
+                            " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale);
+                        mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
+                        mAnimation.setStartTime(currentTime);
+                        mLocalAnimating = true;
+                        mAnimating = true;
+                    }
+                    mTransformation.clear();
+                    final boolean more = mAnimation.getTransformation(
+                        currentTime, mTransformation);
+                    if (DEBUG_ANIM) Log.v(
+                        TAG, "Stepped animation in " + this +
+                        ": more=" + more + ", xform=" + mTransformation);
+                    if (more) {
+                        // we're not done!
+                        return true;
+                    }
+                    if (DEBUG_ANIM) Log.v(
+                        TAG, "Finished animation in " + this +
+                        " @ " + currentTime);
+                    mAnimation = null;
+                    //WindowManagerService.this.dump();
+                }
+                mHasLocalTransformation = false;
+                if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
+                        && mAppToken.hasTransformation) {
+                    // When our app token is animating, we kind-of pretend like
+                    // we are as well.  Note the mLocalAnimating mAnimationIsEntrance
+                    // part of this check means that we will only do this if
+                    // our window is not currently exiting, or it is not
+                    // locally animating itself.  The idea being that one that
+                    // is exiting and doing a local animation should be removed
+                    // once that animation is done.
+                    mAnimating = true;
+                    mHasTransformation = true;
+                    mTransformation.clear();
+                    return false;
+                } else if (mHasTransformation) {
+                    // Little trick to get through the path below to act like
+                    // we have finished an animation.
+                    mAnimating = true;
+                } else if (isAnimating()) {
+                    mAnimating = true;
+                }
+            } else if (mAnimation != null) {
+                // If the display is frozen, and there is a pending animation,
+                // clear it and make sure we run the cleanup code.
+                mAnimating = true;
+                mLocalAnimating = true;
+                mAnimation = null;
+            }
+            
+            if (!mAnimating && !mLocalAnimating) {
+                return false;
+            }
+
+            if (DEBUG_ANIM) Log.v(
+                TAG, "Animation done in " + this + ": exiting=" + mExiting
+                + ", reportedVisible="
+                + (mAppToken != null ? mAppToken.reportedVisible : false));
+            
+            mAnimating = false;
+            mLocalAnimating = false;
+            mAnimation = null;
+            mAnimLayer = mLayer;
+            if (mIsImWindow) {
+                mAnimLayer += mInputMethodAnimLayerAdjustment;
+            }
+            if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
+                    + " anim layer: " + mAnimLayer);
+            mHasTransformation = false;
+            mHasLocalTransformation = false;
+            mPolicyVisibility = mPolicyVisibilityAfterAnim;
+            mTransformation.clear();
+            if (mHasDrawn
+                    && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
+                    && mAppToken != null
+                    && mAppToken.firstWindowDrawn
+                    && mAppToken.startingData != null) {
+                if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Finish starting "
+                        + mToken + ": first real window done animating");
+                mFinishedStarting.add(mAppToken);
+                mH.sendEmptyMessage(H.FINISHED_STARTING);
+            }
+            
+            finishExit();
+
+            if (mAppToken != null) {
+                mAppToken.updateReportedVisibilityLocked();
+            }
+
+            return false;
+        }
+
+        void finishExit() {
+            if (DEBUG_ANIM) Log.v(
+                    TAG, "finishExit in " + this
+                    + ": exiting=" + mExiting
+                    + " remove=" + mRemoveOnExit
+                    + " windowAnimating=" + isWindowAnimating());
+            
+            final int N = mChildWindows.size();
+            for (int i=0; i<N; i++) {
+                ((WindowState)mChildWindows.get(i)).finishExit();
+            }
+            
+            if (!mExiting) {
+                return;
+            }
+            
+            if (isWindowAnimating()) {
+                return;
+            }
+
+            if (localLOGV) Log.v(
+                    TAG, "Exit animation finished in " + this
+                    + ": remove=" + mRemoveOnExit);
+            if (mSurface != null) {
+                mDestroySurface.add(this);
+                mDestroying = true;
+                if (SHOW_TRANSACTIONS) Log.i(
+                        TAG, "  SURFACE " + mSurface + ": HIDE (finishExit)");
+                try {
+                    mSurface.hide();
+                } catch (RuntimeException e) {
+                    Log.w(TAG, "Error hiding surface in " + this, e);
+                }
+                mLastHidden = true;
+                mKeyWaiter.releasePendingPointerLocked(mSession);
+            }
+            mExiting = false;
+            if (mRemoveOnExit) {
+                mPendingRemove.add(this);
+                mRemoveOnExit = false;
+            }
+        }
+        
+        boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+            if (dsdx < .99999f || dsdx > 1.00001f) return false;
+            if (dtdy < .99999f || dtdy > 1.00001f) return false;
+            if (dtdx < -.000001f || dtdx > .000001f) return false;
+            if (dsdy < -.000001f || dsdy > .000001f) return false;
+            return true;
+        }
+        
+        void computeShownFrameLocked() {
+            final boolean selfTransformation = mHasLocalTransformation;
+            Transformation attachedTransformation =
+                    (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation)
+                    ? mAttachedWindow.mTransformation : null;
+            Transformation appTransformation =
+                    (mAppToken != null && mAppToken.hasTransformation)
+                    ? mAppToken.transformation : null;
+            if (selfTransformation || attachedTransformation != null
+                    || appTransformation != null) {
+                // cache often used attributes locally  
+                final Rect frame = mFrame;
+                final float tmpFloats[] = mTmpFloats;
+                final Matrix tmpMatrix = mTmpMatrix;
+
+                // Compute the desired transformation.
+                tmpMatrix.setTranslate(frame.left, frame.top);
+                if (selfTransformation) {
+                    tmpMatrix.preConcat(mTransformation.getMatrix());
+                }
+                if (attachedTransformation != null) {
+                    tmpMatrix.preConcat(attachedTransformation.getMatrix());
+                }
+                if (appTransformation != null) {
+                    tmpMatrix.preConcat(appTransformation.getMatrix());
+                }
+
+                // "convert" it into SurfaceFlinger's format
+                // (a 2x2 matrix + an offset)
+                // Here we must not transform the position of the surface
+                // since it is already included in the transformation.
+                //Log.i(TAG, "Transform: " + matrix);
+                
+                tmpMatrix.getValues(tmpFloats);
+                mDsDx = tmpFloats[Matrix.MSCALE_X];
+                mDtDx = tmpFloats[Matrix.MSKEW_X];
+                mDsDy = tmpFloats[Matrix.MSKEW_Y];
+                mDtDy = tmpFloats[Matrix.MSCALE_Y];
+                int x = (int)tmpFloats[Matrix.MTRANS_X];
+                int y = (int)tmpFloats[Matrix.MTRANS_Y];
+                int w = frame.width();
+                int h = frame.height();
+                mShownFrame.set(x, y, x+w, y+h);
+
+                // Now set the alpha...  but because our current hardware
+                // can't do alpha transformation on a non-opaque surface,
+                // turn it off if we are running an animation that is also
+                // transforming since it is more important to have that
+                // animation be smooth.
+                mShownAlpha = mAlpha;
+                if (!mLimitedAlphaCompositing
+                        || (!PixelFormat.formatHasAlpha(mAttrs.format)
+                        || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
+                                && x == frame.left && y == frame.top))) {
+                    //Log.i(TAG, "Applying alpha transform");
+                    if (selfTransformation) {
+                        mShownAlpha *= mTransformation.getAlpha();
+                    }
+                    if (attachedTransformation != null) {
+                        mShownAlpha *= attachedTransformation.getAlpha();
+                    }
+                    if (appTransformation != null) {
+                        mShownAlpha *= appTransformation.getAlpha();
+                    }
+                } else {
+                    //Log.i(TAG, "Not applying alpha transform");
+                }
+                
+                if (localLOGV) Log.v(
+                    TAG, "Continuing animation in " + this +
+                    ": " + mShownFrame +
+                    ", alpha=" + mTransformation.getAlpha());
+                return;
+            }
+            
+            mShownFrame.set(mFrame);
+            mShownAlpha = mAlpha;
+            mDsDx = 1;
+            mDtDx = 0;
+            mDsDy = 0;
+            mDtDy = 1;
+        }
+        
+        /**
+         * Is this window visible?  It is not visible if there is no
+         * surface, or we are in the process of running an exit animation
+         * that will remove the surface, or its app token has been hidden.
+         */
+        public boolean isVisibleLw() {
+            final AppWindowToken atoken = mAppToken;
+            return mSurface != null && mPolicyVisibility && !mAttachedHidden
+                    && (atoken == null || !atoken.hiddenRequested)
+                    && !mExiting && !mDestroying;
+        }
+
+        /**
+         * Is this window visible, ignoring its app token?  It is not visible
+         * if there is no surface, or we are in the process of running an exit animation
+         * that will remove the surface.
+         */
+        public boolean isWinVisibleLw() {
+            final AppWindowToken atoken = mAppToken;
+            return mSurface != null && mPolicyVisibility && !mAttachedHidden
+                    && (atoken == null || !atoken.hiddenRequested || atoken.animating)
+                    && !mExiting && !mDestroying;
+        }
+
+        /**
+         * The same as isVisible(), but follows the current hidden state of
+         * the associated app token, not the pending requested hidden state.
+         */
+        boolean isVisibleNow() {
+            return mSurface != null && mPolicyVisibility && !mAttachedHidden
+                    && !mToken.hidden && !mExiting && !mDestroying;
+        }
+
+        /**
+         * Same as isVisible(), but we also count it as visible between the
+         * call to IWindowSession.add() and the first relayout().
+         */
+        boolean isVisibleOrAdding() {
+            final AppWindowToken atoken = mAppToken;
+            return (mSurface != null
+                            || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
+                    && mPolicyVisibility && !mAttachedHidden
+                    && (atoken == null || !atoken.hiddenRequested)
+                    && !mExiting && !mDestroying;
+        }
+
+        /**
+         * Is this window currently on-screen?  It is on-screen either if it
+         * is visible or it is currently running an animation before no longer
+         * being visible.
+         */
+        boolean isOnScreen() {
+            final AppWindowToken atoken = mAppToken;
+            if (atoken != null) {
+                return mSurface != null && mPolicyVisibility && !mDestroying
+                        && ((!mAttachedHidden && !atoken.hiddenRequested)
+                                || mAnimating || atoken.animating);
+            } else {
+                return mSurface != null && mPolicyVisibility && !mDestroying
+                        && (!mAttachedHidden || mAnimating);
+            }
+        }
+        
+        /**
+         * Like isOnScreen(), but we don't return true if the window is part
+         * of a transition that has not yet been started.
+         */
+        boolean isReadyForDisplay() {
+            final AppWindowToken atoken = mAppToken;
+            final boolean animating = atoken != null ? atoken.animating : false;
+            return mSurface != null && mPolicyVisibility && !mDestroying
+                    && ((!mAttachedHidden && !mToken.hidden)
+                            || mAnimating || animating);
+        }
+
+        /** Is the window or its container currently animating? */
+        boolean isAnimating() {
+            final WindowState attached = mAttachedWindow;
+            final AppWindowToken atoken = mAppToken;
+            return mAnimation != null
+                    || (attached != null && attached.mAnimation != null)
+                    || (atoken != null && 
+                            (atoken.animation != null
+                                    || atoken.inPendingTransaction));
+        }
+
+        /** Is this window currently animating? */
+        boolean isWindowAnimating() {
+            return mAnimation != null;
+        }
+
+        /**
+         * Like isOnScreen, but returns false if the surface hasn't yet
+         * been drawn.
+         */
+        public boolean isDisplayedLw() {
+            final AppWindowToken atoken = mAppToken;
+            return mSurface != null && mPolicyVisibility && !mDestroying
+                && !mDrawPending && !mCommitDrawPending
+                && ((!mAttachedHidden &&
+                        (atoken == null || !atoken.hiddenRequested))
+                        || mAnimating);
+        }
+
+        public boolean fillsScreenLw(int screenWidth, int screenHeight,
+                                   boolean shownFrame, boolean onlyOpaque) {
+            if (mSurface == null) {
+                return false;
+            }
+            if (mAppToken != null && !mAppToken.appFullscreen) {
+                return false;
+            }
+            if (onlyOpaque && mAttrs.format != PixelFormat.OPAQUE) {
+                return false;
+            }
+            final Rect frame = shownFrame ? mShownFrame : mFrame;
+            if (frame.left <= 0 && frame.top <= 0
+                    && frame.right >= screenWidth
+                    && frame.bottom >= screenHeight) {
+                return true;
+            }
+            return false;
+        }
+        
+        boolean isFullscreenOpaque(int screenWidth, int screenHeight) {
+            if (mAttrs.format != PixelFormat.OPAQUE || mSurface == null
+                    || mAnimation != null || mDrawPending || mCommitDrawPending) {
+                return false;
+            }
+            if (mFrame.left <= 0 && mFrame.top <= 0 &&
+                mFrame.right >= screenWidth && mFrame.bottom >= screenHeight) {
+                return true;
+            }
+            return false;
+        }
+
+        void removeLocked() {
+            if (mAttachedWindow != null) {
+                mAttachedWindow.mChildWindows.remove(this);
+            }
+            destroySurfaceLocked();
+            mSession.windowRemovedLocked();
+            try {
+                mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
+            } catch (RuntimeException e) {
+                // Ignore if it has already been removed (usually because
+                // we are doing this as part of processing a death note.)
+            }
+        }
+
+        private class DeathRecipient implements IBinder.DeathRecipient {
+            public void binderDied() {
+                try {
+                    synchronized(mWindowMap) {
+                        WindowState win = windowForClientLocked(mSession, mClient);
+                        Log.i(TAG, "WIN DEATH: " + win);
+                        if (win != null) {
+                            removeWindowLocked(mSession, win);
+                        }
+                    }
+                } catch (IllegalArgumentException ex) {
+                    // This will happen if the window has already been
+                    // removed.
+                }
+            }
+        }
+
+        /** Returns true if this window desires key events. */
+        public final boolean canReceiveKeys() {
+            return     isVisibleOrAdding()
+                    && (mViewVisibility == View.VISIBLE)
+                    && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
+        }
+
+        public boolean hasDrawnLw() {
+            return mHasDrawn;
+        }
+
+        public boolean showLw(boolean doAnimation) {
+            if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim) {
+                mPolicyVisibility = true;
+                mPolicyVisibilityAfterAnim = true;
+                if (doAnimation) {
+                    applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
+                }
+                requestAnimationLocked(0);
+                return true;
+            }
+            return false;
+        }
+
+        public boolean hideLw(boolean doAnimation) {
+            boolean current = doAnimation ? mPolicyVisibilityAfterAnim
+                    : mPolicyVisibility;
+            if (current) {
+                if (doAnimation) {
+                    applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
+                    if (mAnimation == null) {
+                        doAnimation = false;
+                    }
+                }
+                if (doAnimation) {
+                    mPolicyVisibilityAfterAnim = false;
+                } else {
+                    mPolicyVisibilityAfterAnim = false;
+                    mPolicyVisibility = false;
+                }
+                requestAnimationLocked(0);
+                return true;
+            }
+            return false;
+        }
+
+        void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + this);
+            pw.println(prefix + "mSession=" + mSession
+                  + " mClient=" + mClient.asBinder());
+            pw.println(prefix + "mAttrs=" + mAttrs);
+            pw.println(prefix + "mAttachedWindow=" + mAttachedWindow
+                    + " mLayoutAttached=" + mLayoutAttached
+                    + " mIsImWindow=" + mIsImWindow);
+            pw.println(prefix + "mBaseLayer=" + mBaseLayer
+                  + " mSubLayer=" + mSubLayer
+                  + " mAnimLayer=" + mLayer + "+"
+                  + (mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
+                          : (mAppToken != null ? mAppToken.animLayerAdjustment : 0))
+                  + "=" + mAnimLayer
+                  + " mLastLayer=" + mLastLayer);
+            pw.println(prefix + "mSurface=" + mSurface);
+            pw.println(prefix + "mToken=" + mToken);
+            pw.println(prefix + "mAppToken=" + mAppToken);
+            pw.println(prefix + "mTargetAppToken=" + mTargetAppToken);
+            pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
+                  + " mPolicyVisibility=" + mPolicyVisibility
+                  + " (after=" + mPolicyVisibilityAfterAnim
+                  + ") mAttachedHidden=" + mAttachedHidden
+                  + " mLastHidden=" + mLastHidden
+                  + " mHaveFrame=" + mHaveFrame);
+            pw.println(prefix + "Requested w=" + mRequestedWidth + " h=" + mRequestedHeight
+                  + " x=" + mReqXPos + " y=" + mReqYPos);
+            pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString()
+                    + " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString()
+                    + " mTouchableInsets=" + mTouchableInsets
+                    + " pending=" + mGivenInsetsPending);
+            pw.println(prefix + "mShownFrame=" + mShownFrame.toShortString()
+                  + " last=" + mLastShownFrame.toShortString());
+            pw.println(prefix + "mFrame=" + mFrame.toShortString()
+                    + " last=" + mLastFrame.toShortString());
+            pw.println(prefix + "mContainingFrame=" + mContainingFrame.toShortString()
+                    + " mDisplayFrame=" + mDisplayFrame.toShortString());
+            pw.println(prefix + "mContentFrame=" + mContentFrame.toShortString()
+                    + " mVisibleFrame=" + mVisibleFrame.toShortString());
+            pw.println(prefix + "mContentInsets=" + mContentInsets.toShortString()
+                    + " last=" + mLastContentInsets.toShortString()
+                    + " mVisibleInsets=" + mVisibleInsets.toShortString()
+                    + " last=" + mLastVisibleInsets.toShortString());
+            pw.println(prefix + "mShownAlpha=" + mShownAlpha
+                  + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha);
+            pw.println(prefix + "mAnimating=" + mAnimating
+                    + " mLocalAnimating=" + mLocalAnimating
+                    + " mAnimationIsEntrance=" + mAnimationIsEntrance
+                    + " mAnimation=" + mAnimation);
+            pw.println(prefix + "XForm: has=" + mHasTransformation
+                    + " " + mTransformation.toShortString());
+            pw.println(prefix + "mDrawPending=" + mDrawPending
+                  + " mCommitDrawPending=" + mCommitDrawPending
+                  + " mReadyToShow=" + mReadyToShow
+                  + " mHasDrawn=" + mHasDrawn);
+            pw.println(prefix + "mExiting=" + mExiting
+                    + " mRemoveOnExit=" + mRemoveOnExit
+                    + " mDestroying=" + mDestroying
+                    + " mRemoved=" + mRemoved);
+            pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
+                    + " mAppFreezing=" + mAppFreezing);
+        }
+
+        @Override
+        public String toString() {
+            return "Window{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}";
+        }
+    }
+    
+    // -------------------------------------------------------------
+    // Window Token State
+    // -------------------------------------------------------------
+
+    class WindowToken {
+        // The actual token.
+        final IBinder token;
+
+        // The type of window this token is for, as per WindowManager.LayoutParams.
+        final int windowType;
+        
+        // Set if this token was explicitly added by a client, so should
+        // not be removed when all windows are removed.
+        final boolean explicit;
+        
+        // If this is an AppWindowToken, this is non-null.
+        AppWindowToken appWindowToken;
+        
+        // All of the windows associated with this token.
+        final ArrayList<WindowState> windows = new ArrayList<WindowState>();
+
+        // Is key dispatching paused for this token?
+        boolean paused = false;
+
+        // Should this token's windows be hidden?
+        boolean hidden;
+
+        // Temporary for finding which tokens no longer have visible windows.
+        boolean hasVisible;
+
+        WindowToken(IBinder _token, int type, boolean _explicit) {
+            token = _token;
+            windowType = type;
+            explicit = _explicit;
+        }
+
+        void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + this);
+            pw.println(prefix + "token=" + token);
+            pw.println(prefix + "windows=" + windows);
+            pw.println(prefix + "windowType=" + windowType + " hidden=" + hidden
+                    + " hasVisible=" + hasVisible);
+        }
+
+        @Override
+        public String toString() {
+            return "WindowToken{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " token=" + token + "}";
+        }
+    };
+
+    class AppWindowToken extends WindowToken {
+        // Non-null only for application tokens.
+        final IApplicationToken appToken;
+
+        // All of the windows and child windows that are included in this
+        // application token.  Note this list is NOT sorted!
+        final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>();
+
+        int groupId = -1;
+        boolean appFullscreen;
+        int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        
+        // These are used for determining when all windows associated with
+        // an activity have been drawn, so they can be made visible together
+        // at the same time.
+        int lastTransactionSequence = mTransactionSequence-1;
+        int numInterestingWindows;
+        int numDrawnWindows;
+        boolean inPendingTransaction;
+        boolean allDrawn;
+        
+        // Is this token going to be hidden in a little while?  If so, it
+        // won't be taken into account for setting the screen orientation.
+        boolean willBeHidden;
+        
+        // Is this window's surface needed?  This is almost like hidden, except
+        // it will sometimes be true a little earlier: when the token has
+        // been shown, but is still waiting for its app transition to execute
+        // before making its windows shown.
+        boolean hiddenRequested;
+        
+        // Have we told the window clients to hide themselves?
+        boolean clientHidden;
+        
+        // Last visibility state we reported to the app token.
+        boolean reportedVisible;
+
+        // Set to true when the token has been removed from the window mgr.
+        boolean removed;
+
+        // Have we been asked to have this token keep the screen frozen?
+        boolean freezingScreen;
+        
+        boolean animating;
+        Animation animation;
+        boolean hasTransformation;
+        final Transformation transformation = new Transformation();
+        
+        // Offset to the window of all layers in the token, for use by
+        // AppWindowToken animations.
+        int animLayerAdjustment;
+        
+        // Information about an application starting window if displayed.
+        StartingData startingData;
+        WindowState startingWindow;
+        View startingView;
+        boolean startingDisplayed;
+        boolean startingMoved;
+        boolean firstWindowDrawn;
+
+        AppWindowToken(IApplicationToken _token) {
+            super(_token.asBinder(),
+                    WindowManager.LayoutParams.TYPE_APPLICATION, true);
+            appWindowToken = this;
+            appToken = _token;
+        }
+        
+        public void setAnimation(Animation anim) {
+            if (localLOGV) Log.v(
+                TAG, "Setting animation in " + this + ": " + anim);
+            animation = anim;
+            animating = false;
+            anim.restrictDuration(MAX_ANIMATION_DURATION);
+            anim.scaleCurrentDuration(mTransitionAnimationScale);
+            int zorder = anim.getZAdjustment();
+            int adj = 0;
+            if (zorder == Animation.ZORDER_TOP) {
+                adj = TYPE_LAYER_OFFSET;
+            } else if (zorder == Animation.ZORDER_BOTTOM) {
+                adj = -TYPE_LAYER_OFFSET;
+            }
+            
+            if (animLayerAdjustment != adj) {
+                animLayerAdjustment = adj;
+                updateLayers();
+            }
+        }
+        
+        public void setDummyAnimation() {
+            if (animation == null) {
+                if (localLOGV) Log.v(
+                    TAG, "Setting dummy animation in " + this);
+                animation = sDummyAnimation;
+            }
+        }
+
+        public void clearAnimation() {
+            if (animation != null) {
+                animation = null;
+                animating = true;
+            }
+        }
+        
+        void updateLayers() {
+            final int N = allAppWindows.size();
+            final int adj = animLayerAdjustment;
+            for (int i=0; i<N; i++) {
+                WindowState w = allAppWindows.get(i);
+                w.mAnimLayer = w.mLayer + adj;
+                if (DEBUG_LAYERS) Log.v(TAG, "Updating layer " + w + ": "
+                        + w.mAnimLayer);
+                if (w == mInputMethodTarget) {
+                    setInputMethodAnimLayerAdjustment(adj);
+                }
+            }
+        }
+        
+        void sendAppVisibilityToClients() {
+            final int N = allAppWindows.size();
+            for (int i=0; i<N; i++) {
+                WindowState win = allAppWindows.get(i);
+                if (win == startingWindow && clientHidden) {
+                    // Don't hide the starting window.
+                    continue;
+                }
+                try {
+                    if (DEBUG_VISIBILITY) Log.v(TAG,
+                            "Setting visibility of " + win + ": " + (!clientHidden));
+                    win.mClient.dispatchAppVisibility(!clientHidden);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+        
+        void showAllWindowsLocked() {
+            final int NW = allAppWindows.size();
+            for (int i=0; i<NW; i++) {
+                WindowState w = allAppWindows.get(i);
+                if (DEBUG_VISIBILITY) Log.v(TAG,
+                        "performing show on: " + w);
+                w.performShowLocked();
+            }
+        }
+        
+        // This must be called while inside a transaction.
+        boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+            if (!mDisplayFrozen) {
+                // We will run animations as long as the display isn't frozen.
+                
+                if (animation == sDummyAnimation) {
+                    // This guy is going to animate, but not yet.  For now count
+                    // it is not animating for purposes of scheduling transactions;
+                    // when it is really time to animate, this will be set to
+                    // a real animation and the next call will execute normally.
+                    return false;
+                }
+                
+                if ((allDrawn || animating || startingDisplayed) && animation != null) {
+                    if (!animating) {
+                        if (DEBUG_ANIM) Log.v(
+                            TAG, "Starting animation in " + this +
+                            " @ " + currentTime + ": dw=" + dw + " dh=" + dh
+                            + " scale=" + mTransitionAnimationScale
+                            + " allDrawn=" + allDrawn + " animating=" + animating);
+                        animation.initialize(dw, dh, dw, dh);
+                        animation.setStartTime(currentTime);
+                        animating = true;
+                    }
+                    transformation.clear();
+                    final boolean more = animation.getTransformation(
+                        currentTime, transformation);
+                    if (DEBUG_ANIM) Log.v(
+                        TAG, "Stepped animation in " + this +
+                        ": more=" + more + ", xform=" + transformation);
+                    if (more) {
+                        // we're done!
+                        hasTransformation = true;
+                        return true;
+                    }
+                    if (DEBUG_ANIM) Log.v(
+                        TAG, "Finished animation in " + this +
+                        " @ " + currentTime);
+                    animation = null;
+                }
+            } else if (animation != null) {
+                // If the display is frozen, and there is a pending animation,
+                // clear it and make sure we run the cleanup code.
+                animating = true;
+                animation = null;
+            }
+
+            hasTransformation = false;
+            
+            if (!animating) {
+                return false;
+            }
+
+            clearAnimation();
+            animating = false;
+            if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) {
+                moveInputMethodWindowsIfNeededLocked(true);
+            }
+            
+            if (DEBUG_ANIM) Log.v(
+                    TAG, "Animation done in " + this
+                    + ": reportedVisible=" + reportedVisible);
+
+            transformation.clear();
+            if (animLayerAdjustment != 0) {
+                animLayerAdjustment = 0;
+                updateLayers();
+            }
+            
+            final int N = windows.size();
+            for (int i=0; i<N; i++) {
+                ((WindowState)windows.get(i)).finishExit();
+            }
+            updateReportedVisibilityLocked();
+            
+            return false;
+        }
+
+        void updateReportedVisibilityLocked() {
+            if (appToken == null) {
+                return;
+            }
+            
+            int numInteresting = 0;
+            int numVisible = 0;
+            boolean nowGone = true;
+            
+            if (DEBUG_VISIBILITY) Log.v(TAG, "Update reported visibility: " + this);
+            final int N = allAppWindows.size();
+            for (int i=0; i<N; i++) {
+                WindowState win = allAppWindows.get(i);
+                if (win == startingWindow || win.mAppFreezing) {
+                    continue;
+                }
+                if (DEBUG_VISIBILITY) {
+                    Log.v(TAG, "Win " + win + ": isDisplayed="
+                            + win.isDisplayedLw()
+                            + ", isAnimating=" + win.isAnimating());
+                    if (!win.isDisplayedLw()) {
+                        Log.v(TAG, "Not displayed: s=" + win.mSurface
+                                + " pv=" + win.mPolicyVisibility
+                                + " dp=" + win.mDrawPending
+                                + " cdp=" + win.mCommitDrawPending
+                                + " ah=" + win.mAttachedHidden
+                                + " th="
+                                + (win.mAppToken != null
+                                        ? win.mAppToken.hiddenRequested : false)
+                                + " a=" + win.mAnimating);
+                    }
+                }
+                numInteresting++;
+                if (win.isDisplayedLw()) {
+                    if (!win.isAnimating()) {
+                        numVisible++;
+                    }
+                    nowGone = false;
+                } else if (win.isAnimating()) {
+                    nowGone = false;
+                }
+            }
+            
+            boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
+            if (DEBUG_VISIBILITY) Log.v(TAG, "VIS " + this + ": interesting="
+                    + numInteresting + " visible=" + numVisible);
+            if (nowVisible != reportedVisible) {
+                if (DEBUG_VISIBILITY) Log.v(
+                        TAG, "Visibility changed in " + this
+                        + ": vis=" + nowVisible);
+                reportedVisible = nowVisible;
+                Message m = mH.obtainMessage(
+                        H.REPORT_APPLICATION_TOKEN_WINDOWS,
+                        nowVisible ? 1 : 0,
+                        nowGone ? 1 : 0,
+                        this);
+                    mH.sendMessage(m);
+            }
+        }
+        
+        void dump(PrintWriter pw, String prefix) {
+            super.dump(pw, prefix);
+            pw.println(prefix + "app=" + (appToken != null));
+            pw.println(prefix + "allAppWindows=" + allAppWindows);
+            pw.println(prefix + "groupId=" + groupId
+                    + " requestedOrientation=" + requestedOrientation);
+            pw.println(prefix + "hiddenRequested=" + hiddenRequested
+                    + " clientHidden=" + clientHidden
+                    + " willBeHidden=" + willBeHidden
+                    + " reportedVisible=" + reportedVisible);
+            pw.println(prefix + "paused=" + paused
+                    + " freezingScreen=" + freezingScreen);
+            pw.println(prefix + "numInterestingWindows=" + numInterestingWindows
+                    + " numDrawnWindows=" + numDrawnWindows
+                    + " inPendingTransaction=" + inPendingTransaction
+                    + " allDrawn=" + allDrawn);
+            pw.println(prefix + "animating=" + animating
+                    + " animation=" + animation);
+            pw.println(prefix + "animLayerAdjustment=" + animLayerAdjustment
+                    + " transformation=" + transformation.toShortString());
+            pw.println(prefix + "startingData=" + startingData
+                    + " removed=" + removed
+                    + " firstWindowDrawn=" + firstWindowDrawn);
+            pw.println(prefix + "startingWindow=" + startingWindow
+                    + " startingView=" + startingView
+                    + " startingDisplayed=" + startingDisplayed
+                    + " startingMoved" + startingMoved);
+        }
+
+        @Override
+        public String toString() {
+            return "AppWindowToken{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " token=" + token + "}";
+        }
+    }
+    
+    public static WindowManager.LayoutParams findAnimations(
+            ArrayList<AppWindowToken> order,
+            ArrayList<AppWindowToken> tokenList1,
+            ArrayList<AppWindowToken> tokenList2) {
+        // We need to figure out which animation to use...
+        WindowManager.LayoutParams animParams = null;
+        int animSrc = 0;
+        
+        //Log.i(TAG, "Looking for animations...");
+        for (int i=order.size()-1; i>=0; i--) {
+            AppWindowToken wtoken = order.get(i);
+            //Log.i(TAG, "Token " + wtoken + " with " + wtoken.windows.size() + " windows");
+            if (tokenList1.contains(wtoken) || tokenList2.contains(wtoken)) {
+                int j = wtoken.windows.size();
+                while (j > 0) {
+                    j--;
+                    WindowState win = wtoken.windows.get(j);
+                    //Log.i(TAG, "Window " + win + ": type=" + win.mAttrs.type);
+                    if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
+                            || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
+                        //Log.i(TAG, "Found base or application window, done!");
+                        if (wtoken.appFullscreen) {
+                            return win.mAttrs;
+                        }
+                        if (animSrc < 2) {
+                            animParams = win.mAttrs;
+                            animSrc = 2;
+                        }
+                    } else if (animSrc < 1 && win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION) {
+                        //Log.i(TAG, "Found normal window, we may use this...");
+                        animParams = win.mAttrs;
+                        animSrc = 1;
+                    }
+                }
+            }
+        }
+        
+        return animParams;
+    }
+    
+    // -------------------------------------------------------------
+    // DummyAnimation
+    // -------------------------------------------------------------
+
+    // This is an animation that does nothing: it just immediately finishes
+    // itself every time it is called.  It is used as a stub animation in cases
+    // where we want to synchronize multiple things that may be animating.
+    static final class DummyAnimation extends Animation {
+        public boolean getTransformation(long currentTime, Transformation outTransformation) {
+            return false;
+        }
+    }
+    static final Animation sDummyAnimation = new DummyAnimation();
+    
+    // -------------------------------------------------------------
+    // Async Handler
+    // -------------------------------------------------------------
+
+    static final class StartingData {
+        final String pkg;
+        final int theme;
+        final CharSequence nonLocalizedLabel;
+        final int labelRes;
+        final int icon;
+        
+        StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel,
+                int _labelRes, int _icon) {
+            pkg = _pkg;
+            theme = _theme;
+            nonLocalizedLabel = _nonLocalizedLabel;
+            labelRes = _labelRes;
+            icon = _icon;
+        }
+    }
+
+    private final class H extends Handler {
+        public static final int REPORT_FOCUS_CHANGE = 2;
+        public static final int REPORT_LOSING_FOCUS = 3;
+        public static final int ANIMATE = 4;
+        public static final int ADD_STARTING = 5;
+        public static final int REMOVE_STARTING = 6;
+        public static final int FINISHED_STARTING = 7;
+        public static final int REPORT_APPLICATION_TOKEN_WINDOWS = 8;
+        public static final int UPDATE_ORIENTATION = 10;
+        public static final int WINDOW_FREEZE_TIMEOUT = 11;
+        public static final int HOLD_SCREEN_CHANGED = 12;
+        public static final int APP_TRANSITION_TIMEOUT = 13;
+        public static final int PERSIST_ANIMATION_SCALE = 14;
+        public static final int FORCE_GC = 15;
+        public static final int ENABLE_SCREEN = 16;
+        public static final int APP_FREEZE_TIMEOUT = 17;
+        
+        private Session mLastReportedHold;
+        
+        public H() {
+        }
+        
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case REPORT_FOCUS_CHANGE: {
+                    WindowState lastFocus;
+                    WindowState newFocus;
+    
+                    synchronized(mWindowMap) {
+                        lastFocus = mLastFocus;
+                        newFocus = mCurrentFocus;
+                        if (lastFocus == newFocus) {
+                            // Focus is not changing, so nothing to do.
+                            return;
+                        }
+                        mLastFocus = newFocus;
+                        //Log.i(TAG, "Focus moving from " + lastFocus
+                        //        + " to " + newFocus);
+                        if (newFocus != null && lastFocus != null
+                                && !newFocus.isDisplayedLw()) {
+                            //Log.i(TAG, "Delaying loss of focus...");
+                            mLosingFocus.add(lastFocus);
+                            lastFocus = null;
+                        }
+                    }
+
+                    if (lastFocus != newFocus) {
+                        //System.out.println("Changing focus from " + lastFocus
+                        //                   + " to " + newFocus);
+                        if (newFocus != null) {
+                            try {
+                                //Log.i(TAG, "Gaining focus: " + newFocus);
+                                newFocus.mClient.windowFocusChanged(true, mInTouchMode);
+                            } catch (RemoteException e) {
+                                // Ignore if process has died.
+                            }
+                        }
+
+                        if (lastFocus != null) {
+                            try {
+                                //Log.i(TAG, "Losing focus: " + lastFocus);
+                                lastFocus.mClient.windowFocusChanged(false, mInTouchMode);
+                            } catch (RemoteException e) {
+                                // Ignore if process has died.
+                            }
+                        }
+                    }
+                } break;
+
+                case REPORT_LOSING_FOCUS: {
+                    ArrayList<WindowState> losers;
+    
+                    synchronized(mWindowMap) {
+                        losers = mLosingFocus;
+                        mLosingFocus = new ArrayList<WindowState>();
+                    }
+
+                    final int N = losers.size();
+                    for (int i=0; i<N; i++) {
+                        try {
+                            //Log.i(TAG, "Losing delayed focus: " + losers.get(i));
+                            losers.get(i).mClient.windowFocusChanged(false, mInTouchMode);
+                        } catch (RemoteException e) {
+                             // Ignore if process has died.
+                        }
+                    }
+                } break;
+
+                case ANIMATE: {
+                    synchronized(mWindowMap) {
+                        mAnimationPending = false;
+                        performLayoutAndPlaceSurfacesLocked();
+                    }
+                } break;
+
+                case ADD_STARTING: {
+                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
+                    final StartingData sd = wtoken.startingData;
+
+                    if (sd == null) {
+                        // Animation has been canceled... do nothing.
+                        return;
+                    }
+                    
+                    if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Add starting "
+                            + wtoken + ": pkg=" + sd.pkg);
+                    
+                    View view = null;
+                    try {
+                        view = mPolicy.addStartingWindow(
+                            wtoken.token, sd.pkg,
+                            sd.theme, sd.nonLocalizedLabel, sd.labelRes,
+                            sd.icon);
+                    } catch (Exception e) {
+                        Log.w(TAG, "Exception when adding starting window", e);
+                    }
+
+                    if (view != null) {
+                        boolean abort = false;
+
+                        synchronized(mWindowMap) {
+                            if (wtoken.removed || wtoken.startingData == null) {
+                                // If the window was successfully added, then
+                                // we need to remove it.
+                                if (wtoken.startingWindow != null) {
+                                    if (DEBUG_STARTING_WINDOW) Log.v(TAG,
+                                            "Aborted starting " + wtoken
+                                            + ": removed=" + wtoken.removed
+                                            + " startingData=" + wtoken.startingData);
+                                    wtoken.startingWindow = null;
+                                    wtoken.startingData = null;
+                                    abort = true;
+                                }
+                            } else {
+                                wtoken.startingView = view;
+                            }
+                            if (DEBUG_STARTING_WINDOW && !abort) Log.v(TAG,
+                                    "Added starting " + wtoken
+                                    + ": startingWindow="
+                                    + wtoken.startingWindow + " startingView="
+                                    + wtoken.startingView);
+                        }
+
+                        if (abort) {
+                            try {
+                                mPolicy.removeStartingWindow(wtoken.token, view);
+                            } catch (Exception e) {
+                                Log.w(TAG, "Exception when removing starting window", e);
+                            }
+                        }
+                    }
+                } break;
+
+                case REMOVE_STARTING: {
+                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
+                    IBinder token = null;
+                    View view = null;
+                    synchronized (mWindowMap) {
+                        if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Remove starting "
+                                + wtoken + ": startingWindow="
+                                + wtoken.startingWindow + " startingView="
+                                + wtoken.startingView);
+                        if (wtoken.startingWindow != null) {
+                            view = wtoken.startingView;
+                            token = wtoken.token;
+                            wtoken.startingData = null;
+                            wtoken.startingView = null;
+                            wtoken.startingWindow = null;
+                        }
+                    }
+                    if (view != null) {
+                        try {
+                            mPolicy.removeStartingWindow(token, view);
+                        } catch (Exception e) {
+                            Log.w(TAG, "Exception when removing starting window", e);
+                        }
+                    }
+                } break;
+
+                case FINISHED_STARTING: {
+                    IBinder token = null;
+                    View view = null;
+                    while (true) {
+                        synchronized (mWindowMap) {
+                            final int N = mFinishedStarting.size();
+                            if (N <= 0) {
+                                break;
+                            }
+                            AppWindowToken wtoken = mFinishedStarting.remove(N-1);
+
+                            if (DEBUG_STARTING_WINDOW) Log.v(TAG,
+                                    "Finished starting " + wtoken
+                                    + ": startingWindow=" + wtoken.startingWindow
+                                    + " startingView=" + wtoken.startingView);
+
+                            if (wtoken.startingWindow == null) {
+                                continue;
+                            }
+
+                            view = wtoken.startingView;
+                            token = wtoken.token;
+                            wtoken.startingData = null;
+                            wtoken.startingView = null;
+                            wtoken.startingWindow = null;
+                        }
+
+                        try {
+                            mPolicy.removeStartingWindow(token, view);
+                        } catch (Exception e) {
+                            Log.w(TAG, "Exception when removing starting window", e);
+                        }
+                    }
+                } break;
+
+                case REPORT_APPLICATION_TOKEN_WINDOWS: {
+                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
+
+                    boolean nowVisible = msg.arg1 != 0;
+                    boolean nowGone = msg.arg2 != 0;
+
+                    try {
+                        if (DEBUG_VISIBILITY) Log.v(
+                                TAG, "Reporting visible in " + wtoken
+                                + " visible=" + nowVisible
+                                + " gone=" + nowGone);
+                        if (nowVisible) {
+                            wtoken.appToken.windowsVisible();
+                        } else {
+                            wtoken.appToken.windowsGone();
+                        }
+                    } catch (RemoteException ex) {
+                    }
+                } break;
+                
+                case UPDATE_ORIENTATION: {
+                    setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false);
+                    break;
+                }
+                
+                case WINDOW_FREEZE_TIMEOUT: {
+                    synchronized (mWindowMap) {
+                        Log.w(TAG, "Window freeze timeout expired.");
+                        int i = mWindows.size();
+                        while (i > 0) {
+                            i--;
+                            WindowState w = (WindowState)mWindows.get(i);
+                            if (w.mOrientationChanging) {
+                                w.mOrientationChanging = false;
+                                Log.w(TAG, "Force clearing orientation change: " + w);
+                            }
+                        }
+                        performLayoutAndPlaceSurfacesLocked();
+                    }
+                    break;
+                }
+                
+                case HOLD_SCREEN_CHANGED: {
+                    Session oldHold;
+                    Session newHold;
+                    synchronized (mWindowMap) {
+                        oldHold = mLastReportedHold;
+                        newHold = (Session)msg.obj;
+                        mLastReportedHold = newHold;
+                    }
+                    
+                    if (oldHold != newHold) {
+                        try {
+                            if (oldHold != null) {
+                                mBatteryStats.noteStopWakelock(oldHold.mUid,
+                                        "window",
+                                        BatteryStats.WAKE_TYPE_WINDOW);
+                            }
+                            if (newHold != null) {
+                                mBatteryStats.noteStartWakelock(newHold.mUid,
+                                        "window",
+                                        BatteryStats.WAKE_TYPE_WINDOW);
+                            }
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    break;
+                }
+                
+                case APP_TRANSITION_TIMEOUT: {
+                    synchronized (mWindowMap) {
+                        if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+                            if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                                    "*** APP TRANSITION TIMEOUT");
+                            mAppTransitionReady = true;
+                            mAppTransitionTimeout = true;
+                            performLayoutAndPlaceSurfacesLocked();
+                        }
+                    }
+                    break;
+                }
+                
+                case PERSIST_ANIMATION_SCALE: {
+                    Settings.System.putFloat(mContext.getContentResolver(),
+                            Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
+                    Settings.System.putFloat(mContext.getContentResolver(),
+                            Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+                    break;
+                }
+                
+                case FORCE_GC: {
+                    synchronized(mWindowMap) {
+                        if (mAnimationPending) {
+                            // If we are animating, don't do the gc now but
+                            // delay a bit so we don't interrupt the animation.
+                            mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
+                                    2000);
+                            return;
+                        }
+                        // If we are currently rotating the display, it will
+                        // schedule a new message when done.
+                        if (mDisplayFrozen) {
+                            return;
+                        }
+                        mFreezeGcPending = 0;
+                    }
+                    Runtime.getRuntime().gc();
+                    break;
+                }
+                
+                case ENABLE_SCREEN: {
+                    performEnableScreen();
+                    break;
+                }
+                
+                case APP_FREEZE_TIMEOUT: {
+                    synchronized (mWindowMap) {
+                        Log.w(TAG, "App freeze timeout expired.");
+                        int i = mAppTokens.size();
+                        while (i > 0) {
+                            i--;
+                            AppWindowToken tok = mAppTokens.get(i);
+                            if (tok.freezingScreen) {
+                                Log.w(TAG, "Force clearing freeze: " + tok);
+                                unsetAppFreezingScreenLocked(tok, true, true);
+                            }
+                        }
+                    }
+                    break;
+                }
+                
+            }
+        }
+    }
+
+    // -------------------------------------------------------------
+    // IWindowManager API
+    // -------------------------------------------------------------
+
+    public IWindowSession openSession(IInputMethodClient client,
+            IInputContext inputContext) {
+        if (client == null) throw new IllegalArgumentException("null client");
+        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
+        return new Session(client, inputContext);
+    }
+
+    public boolean inputMethodClientHasFocus(IInputMethodClient client) {
+        synchronized (mWindowMap) {
+            // The focus for the client is the window immediately below
+            // where we would place the input method window.
+            int idx = findDesiredInputMethodWindowIndexLocked(false);
+            WindowState imFocus;
+            if (idx > 0) {
+                imFocus = (WindowState)mWindows.get(idx-1);
+                if (imFocus != null) {
+                    if (imFocus.mSession.mClient != null &&
+                            imFocus.mSession.mClient.asBinder() == client.asBinder()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+    
+    // -------------------------------------------------------------
+    // Internals
+    // -------------------------------------------------------------
+
+    final WindowState windowForClientLocked(Session session, IWindow client) {
+        return windowForClientLocked(session, client.asBinder());
+    }
+    
+    final WindowState windowForClientLocked(Session session, IBinder client) {
+        WindowState win = mWindowMap.get(client);
+        if (localLOGV) Log.v(
+            TAG, "Looking up client " + client + ": " + win);
+        if (win == null) {
+            RuntimeException ex = new RuntimeException();
+            Log.w(TAG, "Requested window " + client + " does not exist", ex);
+            return null;
+        }
+        if (session != null && win.mSession != session) {
+            RuntimeException ex = new RuntimeException();
+            Log.w(TAG, "Requested window " + client + " is in session " +
+                  win.mSession + ", not " + session, ex);
+            return null;
+        }
+
+        return win;
+    }
+
+    private final void assignLayersLocked() {
+        int N = mWindows.size();
+        int curBaseLayer = 0;
+        int curLayer = 0;
+        int i;
+        
+        for (i=0; i<N; i++) {
+            WindowState w = (WindowState)mWindows.get(i);
+            if (w.mBaseLayer == curBaseLayer || w.mIsImWindow) {
+                curLayer += WINDOW_LAYER_MULTIPLIER;
+                w.mLayer = curLayer;
+            } else {
+                curBaseLayer = curLayer = w.mBaseLayer;
+                w.mLayer = curLayer;
+            }
+            if (w.mTargetAppToken != null) {
+                w.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
+            } else if (w.mAppToken != null) {
+                w.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
+            } else {
+                w.mAnimLayer = w.mLayer;
+            }
+            if (w.mIsImWindow) {
+                w.mAnimLayer += mInputMethodAnimLayerAdjustment;
+            }
+            if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": "
+                    + w.mAnimLayer);
+            //System.out.println(
+            //    "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
+        }
+    }
+
+    private boolean mInLayout = false;
+    private final void performLayoutAndPlaceSurfacesLocked() {
+        if (mInLayout) {
+            if (Config.DEBUG) {
+                throw new RuntimeException("Recursive call!");
+            }
+            Log.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout");
+            return;
+        }
+
+        boolean recoveringMemory = false;
+        if (mForceRemoves != null) {
+            recoveringMemory = true;
+            // Wait a little it for things to settle down, and off we go.
+            for (int i=0; i<mForceRemoves.size(); i++) {
+                WindowState ws = mForceRemoves.get(i);
+                Log.i(TAG, "Force removing: " + ws);
+                removeWindowInnerLocked(ws.mSession, ws);
+            }
+            mForceRemoves = null;
+            Log.w(TAG, "Due to memory failure, waiting a bit for next layout");
+            Object tmp = new Object();
+            synchronized (tmp) {
+                try {
+                    tmp.wait(250);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        
+        mInLayout = true;
+        try {
+            performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
+            
+            int i = mPendingRemove.size()-1;
+            if (i >= 0) {
+                while (i >= 0) {
+                    WindowState w = mPendingRemove.get(i);
+                    removeWindowInnerLocked(w.mSession, w);
+                    i--;
+                }
+                mPendingRemove.clear();
+
+                mInLayout = false;
+                assignLayersLocked();
+                mLayoutNeeded = true;
+                performLayoutAndPlaceSurfacesLocked();
+
+            } else {
+                mInLayout = false;
+                if (mLayoutNeeded) {
+                    requestAnimationLocked(0);
+                }
+            }
+        } catch (RuntimeException e) {
+            mInLayout = false;
+            Log.e(TAG, "Unhandled exception while layout out windows", e);
+        }
+    }
+
+    private final void performLayoutLockedInner() {
+        final int dw = mDisplay.getWidth();
+        final int dh = mDisplay.getHeight();
+
+        final int N = mWindows.size();
+        int i;
+
+        // FIRST LOOP: Perform a layout, if needed.
+        
+        if (mLayoutNeeded) {
+            mPolicy.beginLayoutLw(dw, dh);
+
+            // First perform layout of any root windows (not attached
+            // to another window).
+            int topAttached = -1;
+            for (i = N-1; i >= 0; i--) {
+                WindowState win = (WindowState) mWindows.get(i);
+    
+                boolean gone = win.mViewVisibility == View.GONE
+                        || !win.mRelayoutCalled
+                        || win.mToken.hidden;
+
+                // If this view is GONE, then skip it -- keep the current
+                // frame, and let the caller know so they can ignore it
+                // if they want.  (We do the normal layout for INVISIBLE
+                // windows, since that means "perform layout as normal,
+                // just don't display").
+                if (!gone || !win.mHaveFrame) {
+                    if (!win.mLayoutAttached) {
+                        mPolicy.layoutWindowLw(win, win.mAttrs, null);
+                    } else {
+                        if (topAttached < 0) topAttached = i;
+                    }
+                }
+            }
+            
+            // Now perform layout of attached windows, which usually
+            // depend on the position of the window they are attached to.
+            // XXX does not deal with windows that are attached to windows
+            // that are themselves attached.
+            for (i = topAttached; i >= 0; i--) {
+                WindowState win = (WindowState) mWindows.get(i);
+
+                // If this view is GONE, then skip it -- keep the current
+                // frame, and let the caller know so they can ignore it
+                // if they want.  (We do the normal layout for INVISIBLE
+                // windows, since that means "perform layout as normal,
+                // just don't display").
+                if (win.mLayoutAttached) {
+                    if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
+                            || !win.mHaveFrame) {
+                        mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
+                    }
+                }
+            }
+
+            mPolicy.finishLayoutLw();
+            mLayoutNeeded = false;
+        }
+    }
+    
+    private final void performLayoutAndPlaceSurfacesLockedInner(
+            boolean recoveringMemory) {
+        final long currentTime = SystemClock.uptimeMillis();
+        final int dw = mDisplay.getWidth();
+        final int dh = mDisplay.getHeight();
+
+        final int N = mWindows.size();
+        int i;
+
+        // FIRST LOOP: Perform a layout, if needed.
+        
+        performLayoutLockedInner();
+        
+        if (mFxSession == null) {
+            mFxSession = new SurfaceSession();
+        }
+        
+        if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION");
+
+        // Initialize state of exiting tokens.
+        for (i=mExitingTokens.size()-1; i>=0; i--) {
+            mExitingTokens.get(i).hasVisible = false;
+        }
+
+        // Initialize state of exiting applications.
+        for (i=mExitingAppTokens.size()-1; i>=0; i--) {
+            mExitingAppTokens.get(i).hasVisible = false;
+        }
+
+        // SECOND LOOP: Execute animations and update visibility of windows.
+        
+        boolean orientationChangeComplete = true;
+        Session holdScreen = null;
+        float screenBrightness = -1;
+        boolean focusDisplayed = false;
+        boolean animating = false;
+
+        Surface.openTransaction();
+        try {
+            boolean restart;
+
+            do {
+                final int transactionSequence = ++mTransactionSequence;
+
+                // Update animations of all applications, including those
+                // associated with exiting/removed apps
+                boolean tokensAnimating = false;
+                final int NAT = mAppTokens.size();
+                for (i=0; i<NAT; i++) {
+                    if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
+                        tokensAnimating = true;
+                    }
+                }
+                final int NEAT = mExitingAppTokens.size();
+                for (i=0; i<NEAT; i++) {
+                    if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
+                        tokensAnimating = true;
+                    }
+                }
+
+                animating = tokensAnimating;
+                restart = false;
+
+                boolean tokenMayBeDrawn = false;
+
+                mPolicy.beginAnimationLw(dw, dh);
+
+                for (i=N-1; i>=0; i--) {
+                    WindowState w = (WindowState)mWindows.get(i);
+
+                    final WindowManager.LayoutParams attrs = w.mAttrs;
+
+                    if (w.mSurface != null) {
+                        // Execute animation.
+                        w.commitFinishDrawingLocked(currentTime);
+                        if (w.stepAnimationLocked(currentTime, dw, dh)) {
+                            animating = true;
+                            //w.dump("  ");
+                        }
+
+                        mPolicy.animatingWindowLw(w, attrs);
+                    }
+
+                    final AppWindowToken atoken = w.mAppToken;
+                    if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
+                        if (atoken.lastTransactionSequence != transactionSequence) {
+                            atoken.lastTransactionSequence = transactionSequence;
+                            atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+                            atoken.startingDisplayed = false;
+                        }
+                        if ((w.isOnScreen() || w.mAttrs.type
+                                == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+                                && !w.mExiting && !w.mDestroying) {
+                            if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
+                                Log.v(TAG, "Eval win " + w + ": isDisplayed="
+                                        + w.isDisplayedLw()
+                                        + ", isAnimating=" + w.isAnimating());
+                                if (!w.isDisplayedLw()) {
+                                    Log.v(TAG, "Not displayed: s=" + w.mSurface
+                                            + " pv=" + w.mPolicyVisibility
+                                            + " dp=" + w.mDrawPending
+                                            + " cdp=" + w.mCommitDrawPending
+                                            + " ah=" + w.mAttachedHidden
+                                            + " th=" + atoken.hiddenRequested
+                                            + " a=" + w.mAnimating);
+                                }
+                            }
+                            if (w != atoken.startingWindow) {
+                                if (!atoken.freezingScreen || !w.mAppFreezing) {
+                                    atoken.numInterestingWindows++;
+                                    if (w.isDisplayedLw()) {
+                                        atoken.numDrawnWindows++;
+                                        if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Log.v(TAG,
+                                                "tokenMayBeDrawn: " + atoken
+                                                + " freezingScreen=" + atoken.freezingScreen
+                                                + " mAppFreezing=" + w.mAppFreezing);
+                                        tokenMayBeDrawn = true;
+                                    }
+                                }
+                            } else if (w.isDisplayedLw()) {
+                                atoken.startingDisplayed = true;
+                            }
+                        }
+                    } else if (w.mReadyToShow) {
+                        w.performShowLocked();
+                    }
+                }
+
+                if (mPolicy.finishAnimationLw()) {
+                    restart = true;
+                }
+
+                if (tokenMayBeDrawn) {
+                    // See if any windows have been drawn, so they (and others
+                    // associated with them) can now be shown.
+                    final int NT = mTokenList.size();
+                    for (i=0; i<NT; i++) {
+                        AppWindowToken wtoken = mTokenList.get(i).appWindowToken;
+                        if (wtoken == null) {
+                            continue;
+                        }
+                        if (wtoken.freezingScreen) {
+                            int numInteresting = wtoken.numInterestingWindows;
+                            if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+                                if (DEBUG_VISIBILITY) Log.v(TAG,
+                                        "allDrawn: " + wtoken
+                                        + " interesting=" + numInteresting
+                                        + " drawn=" + wtoken.numDrawnWindows);
+                                wtoken.showAllWindowsLocked();
+                                unsetAppFreezingScreenLocked(wtoken, false, true);
+                                orientationChangeComplete = true;
+                            }
+                        } else if (!wtoken.allDrawn) {
+                            int numInteresting = wtoken.numInterestingWindows;
+                            if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+                                if (DEBUG_VISIBILITY) Log.v(TAG,
+                                        "allDrawn: " + wtoken
+                                        + " interesting=" + numInteresting
+                                        + " drawn=" + wtoken.numDrawnWindows);
+                                wtoken.allDrawn = true;
+                                restart = true;
+
+                                // We can now show all of the drawn windows!
+                                if (!mOpeningApps.contains(wtoken)) {
+                                    wtoken.showAllWindowsLocked();
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // If we are ready to perform an app transition, check through
+                // all of the app tokens to be shown and see if they are ready
+                // to go.
+                if (mAppTransitionReady) {
+                    int NN = mOpeningApps.size();
+                    boolean goodToGo = true;
+                    if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                            "Checking " + NN + " opening apps (frozen="
+                            + mDisplayFrozen + " timeout="
+                            + mAppTransitionTimeout + ")...");
+                    if (!mDisplayFrozen && !mAppTransitionTimeout) {
+                        // If the display isn't frozen, wait to do anything until
+                        // all of the apps are ready.  Otherwise just go because
+                        // we'll unfreeze the display when everyone is ready.
+                        for (i=0; i<NN && goodToGo; i++) {
+                            AppWindowToken wtoken = mOpeningApps.get(i);
+                            if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                                    "Check opening app" + wtoken + ": allDrawn="
+                                    + wtoken.allDrawn + " startingDisplayed="
+                                    + wtoken.startingDisplayed);
+                            if (!wtoken.allDrawn && !wtoken.startingDisplayed
+                                    && !wtoken.startingMoved) {
+                                goodToGo = false;
+                            }
+                        }
+                    }
+                    if (goodToGo) {
+                        if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "**** GOOD TO GO");
+                        int transit = mNextAppTransition;
+                        if (mSkipAppTransitionAnimation) {
+                            transit = WindowManagerPolicy.TRANSIT_NONE;
+                        }
+                        mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+                        mAppTransitionReady = false;
+                        mAppTransitionTimeout = false;
+                        mStartingIconInTransition = false;
+                        mSkipAppTransitionAnimation = false;
+
+                        mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+
+                        // We need to figure out which animation to use...
+                        WindowManager.LayoutParams lp = findAnimations(mAppTokens,
+                                mOpeningApps, mClosingApps);
+
+                        NN = mOpeningApps.size();
+                        for (i=0; i<NN; i++) {
+                            AppWindowToken wtoken = mOpeningApps.get(i);
+                            if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                                    "Now opening app" + wtoken);
+                            wtoken.reportedVisible = false;
+                            wtoken.inPendingTransaction = false;
+                            setTokenVisibilityLocked(wtoken, lp, true, transit, false);
+                            wtoken.updateReportedVisibilityLocked();
+                            wtoken.showAllWindowsLocked();
+                        }
+                        NN = mClosingApps.size();
+                        for (i=0; i<NN; i++) {
+                            AppWindowToken wtoken = mClosingApps.get(i);
+                            if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                                    "Now closing app" + wtoken);
+                            wtoken.inPendingTransaction = false;
+                            setTokenVisibilityLocked(wtoken, lp, false, transit, false);
+                            wtoken.updateReportedVisibilityLocked();
+                            // Force the allDrawn flag, because we want to start
+                            // this guy's animations regardless of whether it's
+                            // gotten drawn.
+                            wtoken.allDrawn = true;
+                        }
+
+                        mOpeningApps.clear();
+                        mClosingApps.clear();
+
+                        // This has changed the visibility of windows, so perform
+                        // a new layout to get them all up-to-date.
+                        mLayoutNeeded = true;
+                        moveInputMethodWindowsIfNeededLocked(true);
+                        performLayoutLockedInner();
+                        updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES);
+
+                        restart = true;
+                    }
+                }
+            } while (restart);
+
+            // THIRD LOOP: Update the surfaces of all windows.
+
+            final boolean someoneLosingFocus = mLosingFocus.size() != 0;
+
+            boolean obscured = false;
+            boolean blurring = false;
+            boolean dimming = false;
+            boolean covered = false;
+
+            for (i=N-1; i>=0; i--) {
+                WindowState w = (WindowState)mWindows.get(i);
+
+                boolean displayed = false;
+                final WindowManager.LayoutParams attrs = w.mAttrs;
+                final int attrFlags = attrs.flags;
+
+                if (w.mSurface != null) {
+                    w.computeShownFrameLocked();
+                    if (localLOGV) Log.v(
+                            TAG, "Placing surface #" + i + " " + w.mSurface
+                            + ": new=" + w.mShownFrame + ", old="
+                            + w.mLastShownFrame);
+
+                    boolean resize;
+                    int width, height;
+                    if ((w.mAttrs.flags & w.mAttrs.FLAG_SCALED) != 0) {
+                        resize = w.mLastRequestedWidth != w.mRequestedWidth ||
+                        w.mLastRequestedHeight != w.mRequestedHeight;
+                        // for a scaled surface, we just want to use
+                        // the requested size.
+                        width  = w.mRequestedWidth;
+                        height = w.mRequestedHeight;
+                        w.mLastRequestedWidth = width;
+                        w.mLastRequestedHeight = height;
+                        w.mLastShownFrame.set(w.mShownFrame);
+                        try {
+                            w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
+                        } catch (RuntimeException e) {
+                            Log.w(TAG, "Error positioning surface in " + w, e);
+                            if (!recoveringMemory) {
+                                reclaimSomeSurfaceMemoryLocked(w, "position");
+                            }
+                        }
+                    } else {
+                        resize = !w.mLastShownFrame.equals(w.mShownFrame);
+                        width = w.mShownFrame.width();
+                        height = w.mShownFrame.height();
+                        w.mLastShownFrame.set(w.mShownFrame);
+                        if (resize) {
+                            if (SHOW_TRANSACTIONS) Log.i(
+                                    TAG, "  SURFACE " + w.mSurface + ": ("
+                                    + w.mShownFrame.left + ","
+                                    + w.mShownFrame.top + ") ("
+                                    + w.mShownFrame.width() + "x"
+                                    + w.mShownFrame.height() + ")");
+                        }
+                    }
+
+                    if (resize) {
+                        if (width < 1) width = 1;
+                        if (height < 1) height = 1;
+                        if (w.mSurface != null) {
+                            try {
+                                w.mSurface.setSize(width, height);
+                                w.mSurface.setPosition(w.mShownFrame.left,
+                                        w.mShownFrame.top);
+                            } catch (RuntimeException e) {
+                                // If something goes wrong with the surface (such
+                                // as running out of memory), don't take down the
+                                // entire system.
+                                Log.e(TAG, "Failure updating surface of " + w
+                                        + "size=(" + width + "x" + height
+                                        + "), pos=(" + w.mShownFrame.left
+                                        + "," + w.mShownFrame.top + ")", e);
+                                if (!recoveringMemory) {
+                                    reclaimSomeSurfaceMemoryLocked(w, "size");
+                                }
+                            }
+                        }
+                    }
+                    if (!w.mAppFreezing) {
+                        w.mContentInsetsChanged =
+                            !w.mLastContentInsets.equals(w.mContentInsets);
+                        w.mVisibleInsetsChanged =
+                            !w.mLastVisibleInsets.equals(w.mVisibleInsets);
+                        if (!w.mLastFrame.equals(w.mFrame) 
+                                || w.mContentInsetsChanged
+                                || w.mVisibleInsetsChanged) {
+                            w.mLastFrame.set(w.mFrame);
+                            w.mLastContentInsets.set(w.mContentInsets);
+                            w.mLastVisibleInsets.set(w.mVisibleInsets);
+                            // If the orientation is changing, then we need to
+                            // hold off on unfreezing the display until this
+                            // window has been redrawn; to do that, we need
+                            // to go through the process of getting informed
+                            // by the application when it has finished drawing.
+                            if (w.mOrientationChanging) {
+                                if (DEBUG_ORIENTATION) Log.v(TAG,
+                                        "Orientation start waiting for draw in "
+                                        + w + ", surface " + w.mSurface);
+                                w.mDrawPending = true;
+                                w.mCommitDrawPending = false;
+                                w.mReadyToShow = false;
+                                if (w.mAppToken != null) {
+                                    w.mAppToken.allDrawn = false;
+                                }
+                            }
+                            if (DEBUG_ORIENTATION) Log.v(TAG, 
+                                    "Resizing window " + w + " to " + w.mFrame);
+                            mResizingWindows.add(w);
+                        } else if (w.mOrientationChanging) {
+                            if (!w.mDrawPending && !w.mCommitDrawPending) {
+                                if (DEBUG_ORIENTATION) Log.v(TAG,
+                                        "Orientation not waiting for draw in "
+                                        + w + ", surface " + w.mSurface);
+                                w.mOrientationChanging = false;
+                            }
+                        }
+                    }
+
+                    if (w.mAttachedHidden) {
+                        if (!w.mLastHidden) {
+                            //dump();
+                            w.mLastHidden = true;
+                            if (SHOW_TRANSACTIONS) Log.i(
+                                    TAG, "  SURFACE " + w.mSurface + ": HIDE (performLayout-attached)");
+                            if (w.mSurface != null) {
+                                try {
+                                    w.mSurface.hide();
+                                } catch (RuntimeException e) {
+                                    Log.w(TAG, "Exception hiding surface in " + w);
+                                }
+                            }
+                            mKeyWaiter.releasePendingPointerLocked(w.mSession);
+                        }
+                        // If we are waiting for this window to handle an
+                        // orientation change, well, it is hidden, so
+                        // doesn't really matter.  Note that this does
+                        // introduce a potential glitch if the window
+                        // becomes unhidden before it has drawn for the
+                        // new orientation.
+                        if (w.mOrientationChanging) {
+                            w.mOrientationChanging = false;
+                            if (DEBUG_ORIENTATION) Log.v(TAG,
+                                    "Orientation change skips hidden " + w);
+                        }
+                    } else if (!w.isReadyForDisplay()) {
+                        if (!w.mLastHidden) {
+                            //dump();
+                            w.mLastHidden = true;
+                            if (SHOW_TRANSACTIONS) Log.i(
+                                    TAG, "  SURFACE " + w.mSurface + ": HIDE (performLayout-ready)");
+                            if (w.mSurface != null) {
+                                try {
+                                    w.mSurface.hide();
+                                } catch (RuntimeException e) {
+                                    Log.w(TAG, "Exception exception hiding surface in " + w);
+                                }
+                            }
+                            mKeyWaiter.releasePendingPointerLocked(w.mSession);
+                        }
+                        // If we are waiting for this window to handle an
+                        // orientation change, well, it is hidden, so
+                        // doesn't really matter.  Note that this does
+                        // introduce a potential glitch if the window
+                        // becomes unhidden before it has drawn for the
+                        // new orientation.
+                        if (w.mOrientationChanging) {
+                            w.mOrientationChanging = false;
+                            if (DEBUG_ORIENTATION) Log.v(TAG,
+                                    "Orientation change skips hidden " + w);
+                        }
+                    } else if (w.mLastLayer != w.mAnimLayer
+                            || w.mLastAlpha != w.mShownAlpha
+                            || w.mLastDsDx != w.mDsDx
+                            || w.mLastDtDx != w.mDtDx
+                            || w.mLastDsDy != w.mDsDy
+                            || w.mLastDtDy != w.mDtDy
+                            || w.mLastHScale != w.mHScale
+                            || w.mLastVScale != w.mVScale
+                            || w.mLastHidden) {
+                        displayed = true;
+                        w.mLastAlpha = w.mShownAlpha;
+                        w.mLastLayer = w.mAnimLayer;
+                        w.mLastDsDx = w.mDsDx;
+                        w.mLastDtDx = w.mDtDx;
+                        w.mLastDsDy = w.mDsDy;
+                        w.mLastDtDy = w.mDtDy;
+                        w.mLastHScale = w.mHScale;
+                        w.mLastVScale = w.mVScale;
+                        if (SHOW_TRANSACTIONS) Log.i(
+                                TAG, "  SURFACE " + w.mSurface + ": alpha="
+                                + w.mShownAlpha + " layer=" + w.mAnimLayer);
+                        if (w.mSurface != null) {
+                            try {
+                                w.mSurface.setAlpha(w.mShownAlpha);
+                                w.mSurface.setLayer(w.mAnimLayer);
+                                w.mSurface.setMatrix(
+                                        w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
+                                        w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
+                            } catch (RuntimeException e) {
+                                Log.w(TAG, "Error updating surface in " + w, e);
+                                if (!recoveringMemory) {
+                                    reclaimSomeSurfaceMemoryLocked(w, "update");
+                                }
+                            }
+                        }
+
+                        if (w.mLastHidden && !w.mDrawPending
+                                && !w.mCommitDrawPending
+                                && !w.mReadyToShow) {
+                            if (SHOW_TRANSACTIONS) Log.i(
+                                    TAG, "  SURFACE " + w.mSurface + ": SHOW (performLayout)");
+                            if (DEBUG_VISIBILITY) Log.v(TAG, "Showing " + w
+                                    + " during relayout");
+                            if (showSurfaceRobustlyLocked(w)) {
+                                w.mHasDrawn = true;
+                                w.mLastHidden = false;
+                            } else {
+                                w.mOrientationChanging = false;
+                            }
+                        }
+                        if (w.mSurface != null) {
+                            w.mToken.hasVisible = true;
+                        }
+                    } else {
+                        displayed = true;
+                    }
+
+                    if (displayed) {
+                        if (!covered) {
+                            if (attrs.width == LayoutParams.FILL_PARENT
+                                    && attrs.height == LayoutParams.FILL_PARENT) {
+                                covered = true;
+                            }
+                        }
+                        if (w.mOrientationChanging) {
+                            if (w.mDrawPending || w.mCommitDrawPending) {
+                                orientationChangeComplete = false;
+                                if (DEBUG_ORIENTATION) Log.v(TAG,
+                                        "Orientation continue waiting for draw in " + w);
+                            } else {
+                                w.mOrientationChanging = false;
+                                if (DEBUG_ORIENTATION) Log.v(TAG,
+                                        "Orientation change complete in " + w);
+                            }
+                        }
+                        w.mToken.hasVisible = true;
+                    }
+                } else if (w.mOrientationChanging) {
+                    if (DEBUG_ORIENTATION) Log.v(TAG,
+                            "Orientation change skips hidden " + w);
+                    w.mOrientationChanging = false;
+                }
+
+                final boolean canBeSeen = w.isDisplayedLw();
+
+                if (someoneLosingFocus && w == mCurrentFocus && canBeSeen) {
+                    focusDisplayed = true;
+                }
+
+                // Update effect.
+                if (!obscured) {
+                    if (w.mSurface != null) {
+                        if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
+                            holdScreen = w.mSession;
+                        }
+                        if (w.mAttrs.screenBrightness >= 0 && screenBrightness < 0) {
+                            screenBrightness = w.mAttrs.screenBrightness;
+                        }
+                    }
+                    if (w.isFullscreenOpaque(dw, dh)) {
+                        // This window completely covers everything behind it,
+                        // so we want to leave all of them as unblurred (for
+                        // performance reasons).
+                        obscured = true;
+                    } else if (canBeSeen && !obscured &&
+                            (attrFlags&FLAG_BLUR_BEHIND|FLAG_DIM_BEHIND) != 0) {
+                        if (localLOGV) Log.v(TAG, "Win " + w
+                                + ": blurring=" + blurring
+                                + " obscured=" + obscured
+                                + " displayed=" + displayed);
+                        if ((attrFlags&FLAG_DIM_BEHIND) != 0) {
+                            if (!dimming) {
+                                //Log.i(TAG, "DIM BEHIND: " + w);
+                                dimming = true;
+                                mDimShown = true;
+                                if (mDimSurface == null) {
+                                    if (SHOW_TRANSACTIONS) Log.i(TAG, "  DIM "
+                                            + mDimSurface + ": CREATE");
+                                    try {
+                                        mDimSurface = new Surface(mFxSession, 0, 
+                                                -1, 16, 16,
+                                                PixelFormat.OPAQUE,
+                                                Surface.FX_SURFACE_DIM);
+                                    } catch (Exception e) {
+                                        Log.e(TAG, "Exception creating Dim surface", e);
+                                    }
+                                }
+                                if (SHOW_TRANSACTIONS) Log.i(TAG, "  DIM "
+                                        + mDimSurface + ": SHOW pos=(0,0) (" +
+                                        dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
+                                if (mDimSurface != null) {
+                                    try {
+                                        mDimSurface.setPosition(0, 0);
+                                        mDimSurface.setSize(dw, dh);
+                                        mDimSurface.show();
+                                    } catch (RuntimeException e) {
+                                        Log.w(TAG, "Failure showing dim surface", e);
+                                    }
+                                }
+                            }
+                            mDimSurface.setLayer(w.mAnimLayer-1);
+                            final float target = w.mExiting ? 0 : attrs.dimAmount;
+                            if (mDimTargetAlpha != target) {
+                                // If the desired dim level has changed, then
+                                // start an animation to it.
+                                mLastDimAnimTime = currentTime;
+                                long duration = (w.mAnimating && w.mAnimation != null)
+                                        ? w.mAnimation.computeDurationHint()
+                                        : DEFAULT_DIM_DURATION;
+                                if (target > mDimTargetAlpha) {
+                                    // This is happening behind the activity UI,
+                                    // so we can make it run a little longer to
+                                    // give a stronger impression without disrupting
+                                    // the user.
+                                    duration *= DIM_DURATION_MULTIPLIER;
+                                }
+                                if (duration < 1) {
+                                    // Don't divide by zero
+                                    duration = 1;
+                                }
+                                mDimTargetAlpha = target;
+                                mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha)
+                                        / duration;
+                            }
+                        }
+                        if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
+                            if (!blurring) {
+                                //Log.i(TAG, "BLUR BEHIND: " + w);
+                                blurring = true;
+                                mBlurShown = true;
+                                if (mBlurSurface == null) {
+                                    if (SHOW_TRANSACTIONS) Log.i(TAG, "  BLUR "
+                                            + mBlurSurface + ": CREATE");
+                                    try {
+                                        mBlurSurface = new Surface(mFxSession, 0, 
+                                                -1, 16, 16,
+                                                PixelFormat.OPAQUE,
+                                                Surface.FX_SURFACE_BLUR);
+                                    } catch (Exception e) {
+                                        Log.e(TAG, "Exception creating Blur surface", e);
+                                    }
+                                }
+                                if (SHOW_TRANSACTIONS) Log.i(TAG, "  BLUR "
+                                        + mBlurSurface + ": SHOW pos=(0,0) (" +
+                                        dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
+                                if (mBlurSurface != null) {
+                                    mBlurSurface.setPosition(0, 0);
+                                    mBlurSurface.setSize(dw, dh);
+                                    try {
+                                        mBlurSurface.show();
+                                    } catch (RuntimeException e) {
+                                        Log.w(TAG, "Failure showing blur surface", e);
+                                    }
+                                }
+                            }
+                            mBlurSurface.setLayer(w.mAnimLayer-2);
+                        }
+                    }
+                }
+            }
+
+            if (!dimming && mDimShown) {
+                // Time to hide the dim surface...  start fading.
+                if (mDimTargetAlpha != 0) {
+                    mLastDimAnimTime = currentTime;
+                    mDimTargetAlpha = 0;
+                    mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION;
+                }
+            }
+
+            if (mDimShown && mLastDimAnimTime != 0) {
+                mDimCurrentAlpha += mDimDeltaPerMs
+                        * (currentTime-mLastDimAnimTime);
+                boolean more = true;
+                if (mDisplayFrozen) {
+                    // If the display is frozen, there is no reason to animate.
+                    more = false;
+                } else if (mDimDeltaPerMs > 0) {
+                    if (mDimCurrentAlpha > mDimTargetAlpha) {
+                        more = false;
+                    }
+                } else if (mDimDeltaPerMs < 0) {
+                    if (mDimCurrentAlpha < mDimTargetAlpha) {
+                        more = false;
+                    }
+                } else {
+                    more = false;
+                }
+                
+                // Do we need to continue animating?
+                if (more) {
+                    if (SHOW_TRANSACTIONS) Log.i(TAG, "  DIM "
+                            + mDimSurface + ": alpha=" + mDimCurrentAlpha);
+                    mLastDimAnimTime = currentTime;
+                    mDimSurface.setAlpha(mDimCurrentAlpha);
+                    animating = true;
+                } else {
+                    mDimCurrentAlpha = mDimTargetAlpha;
+                    mLastDimAnimTime = 0;
+                    if (SHOW_TRANSACTIONS) Log.i(TAG, "  DIM "
+                            + mDimSurface + ": final alpha=" + mDimCurrentAlpha);
+                    mDimSurface.setAlpha(mDimCurrentAlpha);
+                    if (!dimming) {
+                        if (SHOW_TRANSACTIONS) Log.i(TAG, "  DIM " + mDimSurface
+                                + ": HIDE");
+                        try {
+                            mDimSurface.hide();
+                        } catch (RuntimeException e) {
+                            Log.w(TAG, "Illegal argument exception hiding dim surface");
+                        }
+                        mDimShown = false;
+                    }
+                }
+            }
+            
+            if (!blurring && mBlurShown) {
+                if (SHOW_TRANSACTIONS) Log.i(TAG, "  BLUR " + mBlurSurface
+                        + ": HIDE");
+                try {
+                    mBlurSurface.hide();
+                } catch (IllegalArgumentException e) {
+                    Log.w(TAG, "Illegal argument exception hiding blur surface");
+                }
+                mBlurShown = false;
+            }
+
+            if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION");
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Unhandled exception in Window Manager", e);
+        }
+
+        Surface.closeTransaction();
+        
+        if (DEBUG_ORIENTATION && mDisplayFrozen) Log.v(TAG,
+                "With display frozen, orientationChangeComplete="
+                + orientationChangeComplete);
+        if (orientationChangeComplete) {
+            if (mWindowsFreezingScreen) {
+                mWindowsFreezingScreen = false;
+                mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
+            }
+            if (mAppsFreezingScreen == 0) {
+                stopFreezingDisplayLocked();
+            }
+        }
+        
+        i = mResizingWindows.size();
+        if (i > 0) {
+            do {
+                i--;
+                WindowState win = mResizingWindows.get(i);
+                try {
+                    win.mClient.resized(win.mFrame.width(),
+                            win.mFrame.height(), win.mLastContentInsets,
+                            win.mLastVisibleInsets, win.mDrawPending);
+                    win.mContentInsetsChanged = false;
+                    win.mVisibleInsetsChanged = false;
+                } catch (RemoteException e) {
+                    win.mOrientationChanging = false;
+                }
+            } while (i > 0);
+            mResizingWindows.clear();
+        }
+        
+        // Destroy the surface of any windows that are no longer visible.
+        i = mDestroySurface.size();
+        if (i > 0) {
+            do {
+                i--;
+                WindowState win = mDestroySurface.get(i);
+                win.mDestroying = false;
+                if (mInputMethodWindow == win) {
+                    mInputMethodWindow = null;
+                }
+                win.destroySurfaceLocked();
+            } while (i > 0);
+            mDestroySurface.clear();
+        }
+
+        // Time to remove any exiting tokens?
+        for (i=mExitingTokens.size()-1; i>=0; i--) {
+            WindowToken token = mExitingTokens.get(i);
+            if (!token.hasVisible) {
+                mExitingTokens.remove(i);
+            }
+        }
+
+        // Time to remove any exiting applications?
+        for (i=mExitingAppTokens.size()-1; i>=0; i--) {
+            AppWindowToken token = mExitingAppTokens.get(i);
+            if (!token.hasVisible && !mClosingApps.contains(token)) {
+                mAppTokens.remove(token);
+                mExitingAppTokens.remove(i);
+            }
+        }
+
+        if (focusDisplayed) {
+            mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
+        }
+        if (animating) {
+            requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
+        }
+        mQueue.setHoldScreenLocked(holdScreen != null);
+        if (screenBrightness < 0 || screenBrightness > 1.0f) {
+            mPowerManager.setScreenBrightnessOverride(-1);
+        } else {
+            mPowerManager.setScreenBrightnessOverride((int)
+                    (screenBrightness * Power.BRIGHTNESS_ON));
+        }
+        if (holdScreen != mHoldingScreenOn) {
+            mHoldingScreenOn = holdScreen;
+            Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
+            mH.sendMessage(m);
+        }
+    }
+
+    void requestAnimationLocked(long delay) {
+        if (!mAnimationPending) {
+            mAnimationPending = true;
+            mH.sendMessageDelayed(mH.obtainMessage(H.ANIMATE), delay);
+        }
+    }
+    
+    /**
+     * Have the surface flinger show a surface, robustly dealing with
+     * error conditions.  In particular, if there is not enough memory
+     * to show the surface, then we will try to get rid of other surfaces
+     * in order to succeed.
+     * 
+     * @return Returns true if the surface was successfully shown.
+     */
+    boolean showSurfaceRobustlyLocked(WindowState win) {
+        try {
+            if (win.mSurface != null) {
+                win.mSurface.show();
+            }
+            return true;
+        } catch (RuntimeException e) {
+            Log.w(TAG, "Failure showing surface " + win.mSurface + " in " + win);
+        }
+        
+        reclaimSomeSurfaceMemoryLocked(win, "show");
+        
+        return false;
+    }
+    
+    void reclaimSomeSurfaceMemoryLocked(WindowState win, String operation) {
+        final Surface surface = win.mSurface;
+        
+        EventLog.writeEvent(LOG_WM_NO_SURFACE_MEMORY, win.toString(),
+                win.mSession.mPid, operation);
+        
+        if (mForceRemoves == null) {
+            mForceRemoves = new ArrayList<WindowState>();
+        }
+        
+        long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            // There was some problem...   first, do a sanity check of the
+            // window list to make sure we haven't left any dangling surfaces
+            // around.
+            int N = mWindows.size();
+            boolean leakedSurface = false;
+            Log.i(TAG, "Out of memory for surface!  Looking for leaks...");
+            for (int i=0; i<N; i++) {
+                WindowState ws = (WindowState)mWindows.get(i);
+                if (ws.mSurface != null) {
+                    if (!mSessions.contains(ws.mSession)) {
+                        Log.w(TAG, "LEAKED SURFACE (session doesn't exist): "
+                                + ws + " surface=" + ws.mSurface
+                                + " token=" + win.mToken
+                                + " pid=" + ws.mSession.mPid
+                                + " uid=" + ws.mSession.mUid);
+                        ws.mSurface.clear();
+                        ws.mSurface = null;
+                        mForceRemoves.add(ws);
+                        i--;
+                        N--;
+                        leakedSurface = true;
+                    } else if (win.mAppToken != null && win.mAppToken.clientHidden) {
+                        Log.w(TAG, "LEAKED SURFACE (app token hidden): "
+                                + ws + " surface=" + ws.mSurface
+                                + " token=" + win.mAppToken);
+                        ws.mSurface.clear();
+                        ws.mSurface = null;
+                        leakedSurface = true;
+                    }
+                }
+            }
+            
+            boolean killedApps = false;
+            if (!leakedSurface) {
+                Log.w(TAG, "No leaked surfaces; killing applicatons!");
+                SparseIntArray pidCandidates = new SparseIntArray();
+                for (int i=0; i<N; i++) {
+                    WindowState ws = (WindowState)mWindows.get(i);
+                    if (ws.mSurface != null) {
+                        pidCandidates.append(ws.mSession.mPid, ws.mSession.mPid);
+                    }
+                }
+                if (pidCandidates.size() > 0) {
+                    int[] pids = new int[pidCandidates.size()];
+                    for (int i=0; i<pids.length; i++) {
+                        pids[i] = pidCandidates.keyAt(i);
+                    }
+                    try {
+                        if (mActivityManager.killPidsForMemory(pids)) {
+                            killedApps = true;
+                        }
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+            
+            if (leakedSurface || killedApps) {
+                // We managed to reclaim some memory, so get rid of the trouble
+                // surface and ask the app to request another one.
+                Log.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
+                if (surface != null) {
+                    surface.clear();
+                    win.mSurface = null;
+                }
+                
+                try {
+                    win.mClient.dispatchGetNewSurface();
+                } catch (RemoteException e) {
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+    }
+    
+    private boolean updateFocusedWindowLocked(int mode) {
+        WindowState newFocus = computeFocusedWindowLocked();
+        if (mCurrentFocus != newFocus) {
+            // This check makes sure that we don't already have the focus
+            // change message pending.
+            mH.removeMessages(H.REPORT_FOCUS_CHANGE);
+            mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
+            if (localLOGV) Log.v(
+                TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
+            final WindowState oldFocus = mCurrentFocus;
+            mCurrentFocus = newFocus;
+            mLosingFocus.remove(newFocus);
+            if (newFocus != null) {
+                mKeyWaiter.handleNewWindowLocked(newFocus);
+            }
+            
+            final WindowState imWindow = mInputMethodWindow;
+            if (newFocus != imWindow && oldFocus != imWindow) {
+                moveInputMethodWindowsIfNeededLocked(
+                        mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
+                        mode != UPDATE_FOCUS_WILL_PLACE_SURFACES);
+                mLayoutNeeded = true;
+                if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+                    performLayoutLockedInner();
+                } else if (mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+                    mLayoutNeeded = true;
+                    performLayoutAndPlaceSurfacesLocked();
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private WindowState computeFocusedWindowLocked() {
+        WindowState result = null;
+        WindowState win;
+
+        int i = mWindows.size() - 1;
+        int nextAppIndex = mAppTokens.size()-1;
+        WindowToken nextApp = nextAppIndex >= 0
+            ? mAppTokens.get(nextAppIndex) : null;
+
+        while (i >= 0) {
+            win = (WindowState)mWindows.get(i);
+
+            if (localLOGV || DEBUG_FOCUS) Log.v(
+                TAG, "Looking for focus: " + i
+                + " = " + win
+                + ", flags=" + win.mAttrs.flags
+                + ", canReceive=" + win.canReceiveKeys());
+
+            AppWindowToken thisApp = win.mAppToken;
+            
+            // If this window's application has been removed, just skip it.
+            if (thisApp != null && thisApp.removed) {
+                i--;
+                continue;
+            }
+            
+            // If there is a focused app, don't allow focus to go to any
+            // windows below it.  If this is an application window, step
+            // through the app tokens until we find its app.
+            if (thisApp != null && nextApp != null && thisApp != nextApp
+                    && win.mAttrs.type != TYPE_APPLICATION_STARTING) {
+                int origAppIndex = nextAppIndex;
+                while (nextAppIndex > 0) {
+                    if (nextApp == mFocusedApp) {
+                        // Whoops, we are below the focused app...  no focus
+                        // for you!
+                        if (localLOGV || DEBUG_FOCUS) Log.v(
+                            TAG, "Reached focused app: " + mFocusedApp);
+                        return null;
+                    }
+                    nextAppIndex--;
+                    nextApp = mAppTokens.get(nextAppIndex);
+                    if (nextApp == thisApp) {
+                        break;
+                    }
+                }
+                if (thisApp != nextApp) {
+                    // Uh oh, the app token doesn't exist!  This shouldn't
+                    // happen, but if it does we can get totally hosed...
+                    // so restart at the original app.
+                    nextAppIndex = origAppIndex;
+                    nextApp = mAppTokens.get(nextAppIndex);
+                }
+            }
+
+            // Dispatch to this window if it is wants key events.
+            if (win.canReceiveKeys()) {
+                if (DEBUG_FOCUS) Log.v(
+                        TAG, "Found focus @ " + i + " = " + win);
+                result = win;
+                break;
+            }
+
+            i--;
+        }
+
+        return result;
+    }
+
+    private void startFreezingDisplayLocked() {
+        if (mDisplayFrozen) {
+            return;
+        }
+        
+        mScreenFrozenLock.acquire();
+        
+        long now = SystemClock.uptimeMillis();
+        //Log.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now);
+        if (mFreezeGcPending != 0) {
+            if (now > (mFreezeGcPending+1000)) {
+                //Log.i(TAG, "Gc!  " + now + " > " + (mFreezeGcPending+1000));
+                mH.removeMessages(H.FORCE_GC);
+                Runtime.getRuntime().gc();
+                mFreezeGcPending = now;
+            }
+        } else {
+            mFreezeGcPending = now;
+        }
+        
+        mDisplayFrozen = true;
+        if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+            mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
+            mAppTransitionReady = true;
+        }
+        
+        if (PROFILE_ORIENTATION) {
+            File file = new File("/data/system/frozen");
+            Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+        }
+        Surface.freezeDisplay(0);
+    }
+    
+    private void stopFreezingDisplayLocked() {
+        if (!mDisplayFrozen) {
+            return;
+        }
+        
+        mDisplayFrozen = false;
+        mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+        if (PROFILE_ORIENTATION) {
+            Debug.stopMethodTracing();
+        }
+        Surface.unfreezeDisplay(0);
+        
+        // Freezing the display also suspends key event delivery, to
+        // keep events from going astray while the display is reconfigured.
+        // Now that we're back, notify the key waiter that we're alive
+        // again and it should restart its timeouts.
+        synchronized (mKeyWaiter) {
+            mKeyWaiter.mWasFrozen = true;
+            mKeyWaiter.notifyAll();
+        }
+
+        // A little kludge: a lot could have happened while the
+        // display was frozen, so now that we are coming back we
+        // do a gc so that any remote references the system
+        // processes holds on others can be released if they are
+        // no longer needed.
+        mH.removeMessages(H.FORCE_GC);
+        mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
+                2000);
+        
+        mScreenFrozenLock.release();
+    }
+    
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump WindowManager from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+        
+        synchronized(mWindowMap) {
+            pw.println("Current Window Manager state:");
+            for (int i=mWindows.size()-1; i>=0; i--) {
+                WindowState w = (WindowState)mWindows.get(i);
+                pw.println("  Window #" + i + ":");
+                w.dump(pw, "    ");
+            }
+            if (mInputMethodDialogs.size() > 0) {
+                pw.println(" ");
+                pw.println("  Input method dialogs:");
+                for (int i=mInputMethodDialogs.size()-1; i>=0; i--) {
+                    WindowState w = mInputMethodDialogs.get(i);
+                    pw.println("  IM Dialog #" + i + ": " + w);
+                }
+            }
+            if (mPendingRemove.size() > 0) {
+                pw.println(" ");
+                pw.println("  Remove pending for:");
+                for (int i=mPendingRemove.size()-1; i>=0; i--) {
+                    WindowState w = mPendingRemove.get(i);
+                    pw.println("  Remove #" + i + ":");
+                    w.dump(pw, "    ");
+                }
+            }
+            if (mForceRemoves != null && mForceRemoves.size() > 0) {
+                pw.println(" ");
+                pw.println("  Windows force removing:");
+                for (int i=mForceRemoves.size()-1; i>=0; i--) {
+                    WindowState w = mForceRemoves.get(i);
+                    pw.println("  Removing #" + i + ":");
+                    w.dump(pw, "    ");
+                }
+            }
+            if (mDestroySurface.size() > 0) {
+                pw.println(" ");
+                pw.println("  Windows waiting to destroy their surface:");
+                for (int i=mDestroySurface.size()-1; i>=0; i--) {
+                    WindowState w = mDestroySurface.get(i);
+                    pw.println("  Destroy #" + i + ":");
+                    w.dump(pw, "    ");
+                }
+            }
+            if (mLosingFocus.size() > 0) {
+                pw.println(" ");
+                pw.println("  Windows losing focus:");
+                for (int i=mLosingFocus.size()-1; i>=0; i--) {
+                    WindowState w = mLosingFocus.get(i);
+                    pw.println("  Losing #" + i + ":");
+                    w.dump(pw, "    ");
+                }
+            }
+            if (mSessions.size() > 0) {
+                pw.println(" ");
+                pw.println("  All active sessions:");
+                Iterator<Session> it = mSessions.iterator();
+                while (it.hasNext()) {
+                    Session s = it.next();
+                    pw.println("  Session " + s);
+                    s.dump(pw, "    ");
+                }
+            }
+            if (mTokenMap.size() > 0) {
+                pw.println(" ");
+                pw.println("  All tokens:");
+                Iterator<WindowToken> it = mTokenMap.values().iterator();
+                while (it.hasNext()) {
+                    WindowToken token = it.next();
+                    pw.println("  Token " + token.token);
+                    token.dump(pw, "    ");
+                }
+            }
+            if (mTokenList.size() > 0) {
+                pw.println(" ");
+                pw.println("  Window token list:");
+                for (int i=0; i<mTokenList.size(); i++) {
+                    pw.println("  WindowToken #" + i + ": " + mTokenList.get(i));
+                }
+            }
+            if (mAppTokens.size() > 0) {
+                pw.println(" ");
+                pw.println("  Application tokens in Z order:");
+                for (int i=mAppTokens.size()-1; i>=0; i--) {
+                    pw.println("  AppWindowToken #" + i + ": " + mAppTokens.get(i));
+                }
+            }
+            if (mFinishedStarting.size() > 0) {
+                pw.println(" ");
+                pw.println("  Finishing start of application tokens:");
+                for (int i=mFinishedStarting.size()-1; i>=0; i--) {
+                    WindowToken token = mFinishedStarting.get(i);
+                    pw.println("  Finish Starting App Token #" + i + ":");
+                    token.dump(pw, "    ");
+                }
+            }
+            if (mExitingTokens.size() > 0) {
+                pw.println(" ");
+                pw.println("  Exiting tokens:");
+                for (int i=mExitingTokens.size()-1; i>=0; i--) {
+                    WindowToken token = mExitingTokens.get(i);
+                    pw.println("  Exiting Token #" + i + ":");
+                    token.dump(pw, "    ");
+                }
+            }
+            if (mExitingAppTokens.size() > 0) {
+                pw.println(" ");
+                pw.println("  Exiting application tokens:");
+                for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
+                    WindowToken token = mExitingAppTokens.get(i);
+                    pw.println("  Exiting App Token #" + i + ":");
+                    token.dump(pw, "    ");
+                }
+            }
+            pw.println(" ");
+            pw.println("  mCurrentFocus=" + mCurrentFocus);
+            pw.println("  mLastFocus=" + mLastFocus);
+            pw.println("  mFocusedApp=" + mFocusedApp);
+            pw.println("  mInputMethodTarget=" + mInputMethodTarget);
+            pw.println("  mInputMethodWindow=" + mInputMethodWindow);
+            pw.println("  mInTouchMode=" + mInTouchMode);
+            pw.println("  mSystemBooted=" + mSystemBooted
+                    + " mDisplayEnabled=" + mDisplayEnabled);
+            pw.println("  mLayoutNeeded=" + mLayoutNeeded
+                    + " mBlurShown=" + mBlurShown);
+            pw.println("  mDimShown=" + mDimShown
+                    + " current=" + mDimCurrentAlpha
+                    + " target=" + mDimTargetAlpha
+                    + " delta=" + mDimDeltaPerMs
+                    + " lastAnimTime=" + mLastDimAnimTime);
+            pw.println("  mInputMethodAnimLayerAdjustment="
+                    + mInputMethodAnimLayerAdjustment);
+            pw.println("  mDisplayFrozen=" + mDisplayFrozen
+                    + " mWindowsFreezingScreen=" + mWindowsFreezingScreen
+                    + " mAppsFreezingScreen=" + mAppsFreezingScreen);
+            pw.println("  mRotation=" + mRotation
+                    + ", mForcedAppOrientation=" + mForcedAppOrientation
+                    + ", mRequestedRotation=" + mRequestedRotation);
+            pw.println("  mAnimationPending=" + mAnimationPending
+                    + " mWindowAnimationScale=" + mWindowAnimationScale
+                    + " mTransitionWindowAnimationScale=" + mTransitionAnimationScale);
+            pw.println("  mNextAppTransition=0x"
+                    + Integer.toHexString(mNextAppTransition)
+                    + ", mAppTransitionReady=" + mAppTransitionReady
+                    + ", mAppTransitionTimeout=" + mAppTransitionTimeout);
+            pw.println("  mStartingIconInTransition=" + mStartingIconInTransition
+                    + ", mSkipAppTransitionAnimation=" + mSkipAppTransitionAnimation);
+            pw.println("  mOpeningApps=" + mOpeningApps);
+                    pw.println("  mClosingApps=" + mClosingApps);
+            pw.println("  DisplayWidth=" + mDisplay.getWidth()
+                    + " DisplayHeight=" + mDisplay.getHeight());
+            pw.println("  KeyWaiter state:");
+            pw.println("    mLastWin=" + mKeyWaiter.mLastWin
+                    + " mLastBinder=" + mKeyWaiter.mLastBinder);
+            pw.println("    mFinished=" + mKeyWaiter.mFinished
+                    + " mGotFirstWindow=" + mKeyWaiter.mGotFirstWindow
+                    + " mEventDispatching=" + mKeyWaiter.mEventDispatching
+                    + " mTimeToSwitch=" + mKeyWaiter.mTimeToSwitch);
+        }
+    }
+
+    public void monitor() {
+        synchronized (mWindowMap) { }
+        synchronized (mKeyguardDisabled) { }
+        synchronized (mKeyWaiter) { }
+    }
+}