Further improvements to window management!

Fix issue #2493497: Stuck in the Emergency dialer - Home/Back keys doesn't work
This was another case of not updating the window focus when needed, this time
when the lock screen was hidden.

Also re-arrange the layout/animate flow to address issues where you would see
a flicker of whatever was behind the lock screen when showing a new activity that
hides the lock screen.  This was because we were deciding to hide the lock screen
during the layout phase, which meant we had to do it without considering whether
it had drawn.  So we could hide the lock screen before the window is shown for the
first time after being drawn.  Now we can do this in the policy during animate, so
we can wait until the window is drawn and actually being shown.

The flow in perform layout is thus significantly changed, where the layout and
animate loops are both under the same repeating loop.  The actual flow from this
should be the same, but it now allows the policy to request a new layout after
the animation loop is done.  This actually cleans up a number of things in this
code as the complexity has increased.

Finally this includes a change to the ui mode manager when switching modes, to do
the resource configuration switch at a different time.  This makes transitions
between modes much cleaner (though not yet perfect).

Change-Id: I5d9e75c1e79df1106108dd522f8ffed6058ef82b
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1483f13..4995cac 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.IContentProvider;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
@@ -989,6 +990,11 @@
             return Settings.System.putFloat(cr, FONT_SCALE, config.fontScale);
         }
 
+        /** @hide */
+        public static boolean hasInterestingConfigurationChanges(int changes) {
+            return (changes&ActivityInfo.CONFIG_FONT_SCALE) != 0;
+        }
+        
         public static boolean getShowGTalkServiceStatus(ContentResolver cr) {
             return getInt(cr, SHOW_GTALK_SERVICE_STATUS, 0) != 0;
         }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index ab3260e..b39cb9d 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -627,8 +627,9 @@
      * returned, all windows given to layoutWindow() <em>must</em> have had a
      * frame assigned.
      *  
-     * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT}
-     * and {@link #FINISH_LAYOUT_REDO_CONFIG}.
+     * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
+     * {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
+     * or {@link #FINISH_LAYOUT_REDO_ANIM}.
      */
     public int finishLayoutLw();
 
@@ -638,6 +639,8 @@
     static final int FINISH_LAYOUT_REDO_CONFIG = 0x0002;
     /** Wallpaper may need to move */
     static final int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004;
+    /** Need to recompute animations */
+    static final int FINISH_LAYOUT_REDO_ANIM = 0x0008;
     
     /**
      * Called when animation of the windows is about to start.
@@ -661,10 +664,11 @@
      * something that may have modified the animation state of another window, 
      * be sure to return true in order to perform another animation frame. 
      *  
-     * @return Return true if animation state may have changed (so that another 
-     *         frame of animation will be run).
+     * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
+     * {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
+     * or {@link #FINISH_LAYOUT_REDO_ANIM}.
      */
-    public boolean finishAnimationLw();
+    public int finishAnimationLw();
 
     /**
      * Return true if it is okay to perform animations for an app transition
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 5f23a90..8103d7c 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -88,7 +88,9 @@
 
     private boolean mComputedNightMode;
     private int mCurUiMode = 0;
+    private int mSetUiMode = 0;
     
+    private boolean mHoldingConfiguration = false;
     private Configuration mConfiguration = new Configuration();
     
     private boolean mSystemReady;
@@ -114,25 +116,32 @@
                 return;
             }
 
-            // Launch a dock activity
-            String category;
-            if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
-                // Only launch car home when car mode is enabled.
-                category = Intent.CATEGORY_CAR_DOCK;
-            } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
-                category = Intent.CATEGORY_DESK_DOCK;
-            } else {
-                category = null;
-            }
-            if (category != null) {
-                intent = new Intent(Intent.ACTION_MAIN);
-                intent.addCategory(category);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                try {
-                    mContext.startActivity(intent);
-                } catch (ActivityNotFoundException e) {
-                    Slog.w(TAG, e.getCause());
+            synchronized (mLock) {
+                // Launch a dock activity
+                String category;
+                if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+                    // Only launch car home when car mode is enabled.
+                    category = Intent.CATEGORY_CAR_DOCK;
+                } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
+                    category = Intent.CATEGORY_DESK_DOCK;
+                } else {
+                    category = null;
+                }
+                if (category != null) {
+                    intent = new Intent(Intent.ACTION_MAIN);
+                    intent.addCategory(category);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    try {
+                        mContext.startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                        Slog.w(TAG, e.getCause());
+                    }
+                }
+                
+                if (mHoldingConfiguration) {
+                    mHoldingConfiguration = false;
+                    updateConfigurationLocked();
                 }
             }
         }
@@ -370,42 +379,46 @@
         }
     }
 
+    final void updateConfigurationLocked() {
+        int uiMode = 0;
+        if (mCarModeEnabled) {
+            uiMode = Configuration.UI_MODE_TYPE_CAR;
+        } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+            uiMode = Configuration.UI_MODE_TYPE_DESK;
+        }
+        if (uiMode != 0) {
+            if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                updateTwilightLocked();
+                uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
+                        : Configuration.UI_MODE_NIGHT_NO;
+            } else {
+                uiMode |= mNightMode << 4;
+            }
+        } else {
+            // Disabling the car mode clears the night mode.
+            uiMode = Configuration.UI_MODE_TYPE_NORMAL |
+                    Configuration.UI_MODE_NIGHT_NO;
+        }
+        
+        mCurUiMode = uiMode;
+        
+        if (!mHoldingConfiguration && uiMode != mSetUiMode) {
+            mSetUiMode = uiMode;
+            
+            try {
+                final IActivityManager am = ActivityManagerNative.getDefault();
+                mConfiguration.uiMode = uiMode;
+                am.updateConfiguration(mConfiguration);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failure communicating with activity manager", e);
+            }
+        }
+    }
+    
     final void updateLocked() {
         long ident = Binder.clearCallingIdentity();
         
         try {
-            int uiMode = 0;
-            if (mCarModeEnabled) {
-                uiMode = Configuration.UI_MODE_TYPE_CAR;
-            } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
-                uiMode = Configuration.UI_MODE_TYPE_DESK;
-            }
-            if (uiMode != 0) {
-                if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                    updateTwilightLocked();
-                    uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
-                            : Configuration.UI_MODE_NIGHT_NO;
-                } else {
-                    uiMode |= mNightMode << 4;
-                }
-            } else {
-                // Disabling the car mode clears the night mode.
-                uiMode = Configuration.UI_MODE_TYPE_NORMAL |
-                        Configuration.UI_MODE_NIGHT_NO;
-            }
-            
-            if (uiMode != mCurUiMode) {
-                mCurUiMode = uiMode;
-                
-                try {
-                    final IActivityManager am = ActivityManagerNative.getDefault();
-                    mConfiguration.uiMode = uiMode;
-                    am.updateConfiguration(mConfiguration);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failure communicating with activity manager", e);
-                }
-            }
-            
             String action = null;
             String oldAction = null;
             if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
@@ -450,7 +463,13 @@
                 // placed into a dock.
                 mContext.sendOrderedBroadcast(new Intent(action), null,
                         mResultReceiver, null, Activity.RESULT_OK, null, null);
+                // Attempting to make this transition a little more clean, we are going
+                // to hold off on doing a configuration change until we have finished
+                // the broacast and started the home activity.
+                mHoldingConfiguration = true;
             }
+            
+            updateConfigurationLocked();
 
             // keep screen on when charging and in car mode
             boolean keepScreenOn = mCharging &&
@@ -685,6 +704,8 @@
                     pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
                     pw.print(" mComputedNightMode="); pw.println(mComputedNightMode);
             pw.print("  mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
+                    pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
+            pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
                     pw.print(" mSystemReady="); pw.println(mSystemReady);
             if (mLocation != null) {
                 pw.print("  mLocation="); pw.println(mLocation);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 34a7fc0..989fe2a 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -43,6 +43,7 @@
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
+import com.android.internal.policy.impl.PhoneWindowManager;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
@@ -984,10 +985,15 @@
         //Slog.i(TAG, "Placing input method @" + (i+1));
         if (w != null) {
             if (willMove) {
-                RuntimeException e = new RuntimeException();
-                if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
-                if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from "
-                        + mInputMethodTarget + " to " + w, e);
+                if (DEBUG_INPUT_METHOD) {
+                    RuntimeException e = null;
+                    if (!HIDE_STACK_CRAWLS) {
+                        e = new RuntimeException();
+                        e.fillInStackTrace();
+                    }
+                    Slog.w(TAG, "Moving IM target from "
+                            + mInputMethodTarget + " to " + w, e);
+                }
                 mInputMethodTarget = w;
                 if (w.mAppToken != null) {
                     setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment);
@@ -998,10 +1004,15 @@
             return i+1;
         }
         if (willMove) {
-            RuntimeException e = new RuntimeException();
-            if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
-            if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from "
-                    + mInputMethodTarget + " to null", e);
+            if (DEBUG_INPUT_METHOD) {
+                RuntimeException e = null;
+                if (!HIDE_STACK_CRAWLS) {
+                    e = new RuntimeException();
+                    e.fillInStackTrace();
+                }
+                Slog.w(TAG, "Moving IM target from "
+                        + mInputMethodTarget + " to null", e);
+            }
             mInputMethodTarget = null;
             setInputMethodAnimLayerAdjustment(0);
         }
@@ -2178,6 +2189,16 @@
         }
     }
 
+    private static void logSurface(WindowState w, String msg, RuntimeException where) {
+        String str = "  SURFACE " + Integer.toHexString(w.hashCode())
+                + ": " + msg + " / " + w.mAttrs.getTitle();
+        if (where != null) {
+            Slog.i(TAG, str, where);
+        } else {
+            Slog.i(TAG, str);
+        }
+    }
+    
     private void setTransparentRegionWindow(Session session, IWindow client, Region region) {
         long origId = Binder.clearCallingIdentity();
         try {
@@ -2187,9 +2208,8 @@
                     if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
                     Surface.openTransaction();
                     try {
-                        if (SHOW_TRANSACTIONS) Slog.i(
-                                TAG, "  SURFACE " + w.mSurface
-                                + ": transparentRegionHint=" + region);
+                        if (SHOW_TRANSACTIONS) logSurface(w,
+                                "transparentRegionHint=" + region, null);
                         w.mSurface.setTransparentRegionHint(region);
                     } finally {
                         if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION");
@@ -2654,8 +2674,11 @@
                     + " isEntrance=" + isEntrance);
             if (a != null) {
                 if (DEBUG_ANIM) {
-                    RuntimeException e = new RuntimeException();
-                    if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
+                    RuntimeException e = null;
+                    if (!HIDE_STACK_CRAWLS) {
+                        e = new RuntimeException();
+                        e.fillInStackTrace();
+                    }
                     Slog.v(TAG, "Loaded animation " + a + " for " + win, e);
                 }
                 win.setAnimation(a);
@@ -2777,8 +2800,11 @@
             }
             if (a != null) {
                 if (DEBUG_ANIM) {
-                    RuntimeException e = new RuntimeException();
-                    if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
+                    RuntimeException e = null;
+                    if (!HIDE_STACK_CRAWLS) {
+                        e = new RuntimeException();
+                        e.fillInStackTrace();
+                    }
                     Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
                 }
                 wtoken.setAnimation(a);
@@ -3569,8 +3595,11 @@
             }
 
             if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
-                RuntimeException e = new RuntimeException();
-                if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
+                RuntimeException e = null;
+                if (!HIDE_STACK_CRAWLS) {
+                    e = new RuntimeException();
+                    e.fillInStackTrace();
+                }
                 Slog.v(TAG, "setAppVisibility(" + token + ", " + visible
                         + "): mNextAppTransition=" + mNextAppTransition
                         + " hidden=" + wtoken.hidden
@@ -3671,8 +3700,11 @@
     public void startAppFreezingScreenLocked(AppWindowToken wtoken,
             int configChanges) {
         if (DEBUG_ORIENTATION) {
-            RuntimeException e = new RuntimeException();
-            if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
+            RuntimeException e = null;
+            if (!HIDE_STACK_CRAWLS) {
+                e = new RuntimeException();
+                e.fillInStackTrace();
+            }
             Slog.i(TAG, "Set freezing of " + wtoken.appToken
                     + ": hidden=" + wtoken.hidden + " freezing="
                     + wtoken.freezingScreen, e);
@@ -7305,7 +7337,8 @@
                             + mSession.mSurfaceSession
                             + ": pid=" + mSession.mPid + " format="
                             + mAttrs.format + " flags=0x"
-                            + Integer.toHexString(flags));
+                            + Integer.toHexString(flags)
+                            + " / " + this);
                 } catch (Surface.OutOfResourcesException e) {
                     Slog.w(TAG, "OutOfResourcesException creating surface");
                     reclaimSomeSurfaceMemoryLocked(this, "create");
@@ -7321,11 +7354,10 @@
                     + ", animLayer=" + mAnimLayer);
                 if (SHOW_TRANSACTIONS) {
                     Slog.i(TAG, ">>> OPEN TRANSACTION");
-                    Slog.i(TAG, "  SURFACE " + mSurface + ": CREATE ("
-                            + mAttrs.getTitle() + ") pos=(" +
-                          mFrame.left + "," + mFrame.top + ") (" +
-                          mFrame.width() + "x" + mFrame.height() + "), layer=" +
-                          mAnimLayer + " HIDE");
+                    if (SHOW_TRANSACTIONS) logSurface(this,
+                            "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +
+                            mFrame.width() + "x" + mFrame.height() + "), layer=" +
+                            mAnimLayer + " HIDE", null);
                 }
                 Surface.openTransaction();
                 try {
@@ -7335,8 +7367,7 @@
                         mSurface.setLayer(mAnimLayer);
                         mSurface.hide();
                         if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
-                            if (SHOW_TRANSACTIONS) Slog.i(TAG, "  SURFACE "
-                                    + mSurface + ": DITHER");
+                            if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null);
                             mSurface.setFlags(Surface.SURFACE_DITHER,
                                     Surface.SURFACE_DITHER);
                         }
@@ -7393,16 +7424,21 @@
 
                 try {
                     if (DEBUG_VISIBILITY) {
-                        RuntimeException e = new RuntimeException();
-                        if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
+                        RuntimeException e = null;
+                        if (!HIDE_STACK_CRAWLS) {
+                            e = new RuntimeException();
+                            e.fillInStackTrace();
+                        }
                         Slog.w(TAG, "Window " + this + " destroying surface "
                                 + mSurface + ", session " + mSession, e);
                     }
                     if (SHOW_TRANSACTIONS) {
-                        RuntimeException ex = new RuntimeException();
-                        if (!HIDE_STACK_CRAWLS) ex.fillInStackTrace();
-                        Slog.i(TAG, "  SURFACE " + mSurface + ": DESTROY ("
-                                + mAttrs.getTitle() + ")", ex);
+                        RuntimeException e = null;
+                        if (!HIDE_STACK_CRAWLS) {
+                            e = new RuntimeException();
+                            e.fillInStackTrace();
+                        }
+                        if (SHOW_TRANSACTIONS) logSurface(this, "DESTROY", e);
                     }
                     mSurface.destroy();
                 } catch (RuntimeException e) {
@@ -7445,15 +7481,18 @@
         // This must be called while inside a transaction.
         boolean performShowLocked() {
             if (DEBUG_VISIBILITY) {
-                RuntimeException e = new RuntimeException();
-                if (!HIDE_STACK_CRAWLS) e.fillInStackTrace();
+                RuntimeException e = null;
+                if (!HIDE_STACK_CRAWLS) {
+                    e = new RuntimeException();
+                    e.fillInStackTrace();
+                }
                 Slog.v(TAG, "performShow on " + this
                         + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay()
                         + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e);
             }
             if (mReadyToShow && isReadyForDisplay()) {
-                if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Slog.i(
-                        TAG, "  SURFACE " + mSurface + ": SHOW (performShowLocked)");
+                if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) logSurface(this,
+                        "SHOW (performShowLocked)", null);
                 if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
                         + " during animation: policyVis=" + mPolicyVisibility
                         + " attHidden=" + mAttachedHidden
@@ -7666,8 +7705,7 @@
             if (mSurface != null) {
                 mDestroySurface.add(this);
                 mDestroying = true;
-                if (SHOW_TRANSACTIONS) Slog.i(
-                        TAG, "  SURFACE " + mSurface + ": HIDE (finishExit)");
+                if (SHOW_TRANSACTIONS) logSurface(this, "HIDE (finishExit)", null);
                 try {
                     mSurface.hide();
                 } catch (RuntimeException e) {
@@ -7826,6 +7864,7 @@
             final AppWindowToken atoken = mAppToken;
             return mSurface != null && !mAttachedHidden
                     && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
+                    && !mDrawPending && !mCommitDrawPending
                     && !mExiting && !mDestroying;
         }
 
@@ -8045,6 +8084,18 @@
                 return false;
             }
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
+            if (doAnimation) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
+                        + mPolicyVisibility + " mAnimation=" + mAnimation);
+                if (mDisplayFrozen || !mPolicy.isScreenOn()) {
+                    doAnimation = false;
+                } else if (mPolicyVisibility && mAnimation == null) {
+                    // Check for the case where we are currently visible and
+                    // not animating; we do not want to do animation at such a
+                    // point to become visible when we already are.
+                    doAnimation = false;
+                }
+            }
             mPolicyVisibility = true;
             mPolicyVisibilityAfterAnim = true;
             if (doAnimation) {
@@ -8061,6 +8112,11 @@
         }
 
         boolean hideLw(boolean doAnimation, boolean requestAnim) {
+            if (doAnimation) {
+                if (mDisplayFrozen || !mPolicy.isScreenOn()) {
+                    doAnimation = false;
+                }
+            }
             boolean current = doAnimation ? mPolicyVisibilityAfterAnim
                     : mPolicyVisibility;
             if (!current) {
@@ -8082,6 +8138,9 @@
                 // for it to be displayed before enabling the display, that
                 // we allow the display to be enabled now.
                 enableScreenIfNeededLocked();
+                if (mCurrentFocus == this) {
+                    mFocusMayChange = true;
+                }
             }
             if (requestAnim) {
                 requestAnimationLocked(0);
@@ -9283,131 +9342,106 @@
         }
     }
 
-    private final void performLayoutLockedInner() {
+    private final int performLayoutLockedInner() {
+        if (!mLayoutNeeded) {
+            return 0;
+        }
+        
+        mLayoutNeeded = false;
+        
         final int dw = mDisplay.getWidth();
         final int dh = mDisplay.getHeight();
 
         final int N = mWindows.size();
-        int repeats = 0;
         int i;
 
         if (DEBUG_LAYOUT) Slog.v(TAG, "performLayout: needed="
                 + mLayoutNeeded + " dw=" + dw + " dh=" + dh);
         
-        // FIRST LOOP: Perform a layout, if needed.
+        mPolicy.beginLayoutLw(dw, dh);
 
-        while (mLayoutNeeded) {
-            mPolicy.beginLayoutLw(dw, dh);
+        int seq = mLayoutSeq+1;
+        if (seq < 0) seq = 0;
+        mLayoutSeq = seq;
+        
+        // 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);
 
-            int seq = mLayoutSeq+1;
-            if (seq < 0) seq = 0;
-            mLayoutSeq = seq;
+            // Don't do layout of a window if it is not visible, or
+            // soon won't be visible, to avoid wasting time and funky
+            // changes while a window is animating away.
+            final AppWindowToken atoken = win.mAppToken;
+            final boolean gone = win.mViewVisibility == View.GONE
+                    || !win.mRelayoutCalled
+                    || win.mRootToken.hidden
+                    || (atoken != null && atoken.hiddenRequested)
+                    || win.mAttachedHidden
+                    || win.mExiting || win.mDestroying;
+
+            if (!win.mLayoutAttached) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "First pass " + win
+                        + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
+                        + " mLayoutAttached=" + win.mLayoutAttached);
+                if (DEBUG_LAYOUT && gone) Slog.v(TAG, "  (mViewVisibility="
+                        + win.mViewVisibility + " mRelayoutCalled="
+                        + win.mRelayoutCalled + " hidden="
+                        + win.mRootToken.hidden + " hiddenRequested="
+                        + (atoken != null && atoken.hiddenRequested)
+                        + " mAttachedHidden=" + win.mAttachedHidden);
+            }
             
-            // 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);
-
-                // Don't do layout of a window if it is not visible, or
-                // soon won't be visible, to avoid wasting time and funky
-                // changes while a window is animating away.
-                final AppWindowToken atoken = win.mAppToken;
-                final boolean gone = win.mViewVisibility == View.GONE
-                        || !win.mRelayoutCalled
-                        || win.mRootToken.hidden
-                        || (atoken != null && atoken.hiddenRequested)
-                        || win.mAttachedHidden
-                        || win.mExiting || win.mDestroying;
-
+            // 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) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "First pass " + win
-                            + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
-                            + " mLayoutAttached=" + win.mLayoutAttached);
-                    if (DEBUG_LAYOUT && gone) Slog.v(TAG, "  (mViewVisibility="
-                            + win.mViewVisibility + " mRelayoutCalled="
-                            + win.mRelayoutCalled + " hidden="
-                            + win.mRootToken.hidden + " hiddenRequested="
-                            + (atoken != null && atoken.hiddenRequested)
-                            + " mAttachedHidden=" + win.mAttachedHidden);
-                }
-                
-                // 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);
-                        win.mLayoutSeq = seq;
-                        if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
-                                + win.mFrame + " mContainingFrame="
-                                + win.mContainingFrame + " mDisplayFrame="
-                                + win.mDisplayFrame);
-                    } 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 (DEBUG_LAYOUT) Slog.v(TAG, "Second pass " + win
-                            + " mHaveFrame=" + win.mHaveFrame
-                            + " mViewVisibility=" + win.mViewVisibility
-                            + " mRelayoutCalled=" + win.mRelayoutCalled);
-                    if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
-                            || !win.mHaveFrame) {
-                        mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
-                        win.mLayoutSeq = seq;
-                        if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
-                                + win.mFrame + " mContainingFrame="
-                                + win.mContainingFrame + " mDisplayFrame="
-                                + win.mDisplayFrame);
-                    }
-                }
-            }
-
-            int changes = mPolicy.finishLayoutLw();
-            if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
-                    assignLayersLocked();
-                }
-            }
-            if (changes == 0) {
-                mLayoutNeeded = false;
-            } else if (repeats > 2) {
-                Slog.w(TAG, "Layout repeat aborted after too many iterations");
-                mLayoutNeeded = false;
-                if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
-                    if (updateOrientationFromAppTokensLocked()) {
-                        mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
-                    }
-                }
-            } else {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "Repeating layout because changes=0x"
-                        + Integer.toHexString(changes));
-                repeats++;
-                if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
-                    if (updateOrientationFromAppTokensLocked()) {
-                        mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
-                    }
+                    mPolicy.layoutWindowLw(win, win.mAttrs, null);
+                    win.mLayoutSeq = seq;
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
+                            + win.mFrame + " mContainingFrame="
+                            + win.mContainingFrame + " mDisplayFrame="
+                            + win.mDisplayFrame);
+                } 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 (DEBUG_LAYOUT) Slog.v(TAG, "Second pass " + win
+                        + " mHaveFrame=" + win.mHaveFrame
+                        + " mViewVisibility=" + win.mViewVisibility
+                        + " mRelayoutCalled=" + win.mRelayoutCalled);
+                if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
+                        || !win.mHaveFrame) {
+                    mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
+                    win.mLayoutSeq = seq;
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
+                            + win.mFrame + " mContainingFrame="
+                            + win.mContainingFrame + " mDisplayFrame="
+                            + win.mDisplayFrame);
+                }
+            }
+        }
+
+        return mPolicy.finishLayoutLw();
     }
 
     private final void performLayoutAndPlaceSurfacesLockedInner(
@@ -9423,9 +9457,6 @@
             updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
         }
         
-        // FIRST LOOP: Perform a layout, if needed.
-        performLayoutLockedInner();
-
         if (mFxSession == null) {
             mFxSession = new SurfaceSession();
         }
@@ -9442,7 +9473,6 @@
             mExitingAppTokens.get(i).hasVisible = false;
         }
 
-        // SECOND LOOP: Execute animations and update visibility of windows.
         boolean orientationChangeComplete = true;
         Session holdScreen = null;
         float screenBrightness = -1;
@@ -9452,11 +9482,50 @@
 
         Surface.openTransaction();
         try {
-            boolean restart;
-            boolean forceHiding = false;
             boolean wallpaperForceHidingChanged = false;
-
+            int repeats = 0;
+            int changes = 0;
+            
             do {
+                repeats++;
+                if (repeats > 6) {
+                    Slog.w(TAG, "Animation repeat aborted after too many iterations");
+                    mLayoutNeeded = false;
+                    break;
+                }
+                
+                if ((changes&(WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER
+                        | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG
+                        | WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT)) != 0) {
+                    if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+                        if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+                            assignLayersLocked();
+                            mLayoutNeeded = true;
+                        }
+                    }
+                    if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+                        if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+                        if (updateOrientationFromAppTokensLocked()) {
+                            mLayoutNeeded = true;
+                            mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+                        }
+                    }
+                    if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+                        mLayoutNeeded = true;
+                    }
+                }
+                
+                // FIRST LOOP: Perform a layout, if needed.
+                if (repeats < 4) {
+                    changes = performLayoutLockedInner();
+                    if (changes != 0) {
+                        continue;
+                    }
+                } else {
+                    Slog.w(TAG, "Layout repeat skipped after too many iterations");
+                    changes = 0;
+                }
+                
                 final int transactionSequence = ++mTransactionSequence;
 
                 // Update animations of all applications, including those
@@ -9475,15 +9544,17 @@
                     }
                 }
 
+                // SECOND LOOP: Execute animations and update visibility of windows.
+                
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
                         + transactionSequence + " tokensAnimating="
                         + tokensAnimating);
                         
                 animating = tokensAnimating;
-                restart = false;
 
                 boolean tokenMayBeDrawn = false;
                 boolean wallpaperMayChange = false;
+                boolean forceHiding = false;
 
                 mPolicy.beginAnimationLw(dw, dh);
 
@@ -9536,7 +9607,7 @@
                                         "Now policy shown: " + w);
                                 if (changed) {
                                     if (wallpaperForceHidingChanged
-                                            && w.isReadyForDisplay()) {
+                                            && w.isVisibleNow() /*w.isReadyForDisplay()*/) {
                                         // Assume we will need to animate.  If
                                         // we don't (because the wallpaper will
                                         // stay with the lock screen), then we will
@@ -9609,9 +9680,7 @@
                     }
                 }
 
-                if (mPolicy.finishAnimationLw()) {
-                    restart = true;
-                }
+                changes |= mPolicy.finishAnimationLw();
 
                 if (tokenMayBeDrawn) {
                     // See if any windows have been drawn, so they (and others
@@ -9641,7 +9710,7 @@
                                         + " interesting=" + numInteresting
                                         + " drawn=" + wtoken.numDrawnWindows);
                                 wtoken.allDrawn = true;
-                                restart = true;
+                                changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
 
                                 // We can now show all of the drawn windows!
                                 if (!mOpeningApps.contains(wtoken)) {
@@ -9856,15 +9925,13 @@
 
                         // This has changed the visibility of windows, so perform
                         // a new layout to get them all up-to-date.
+                        changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
                         mLayoutNeeded = true;
                         if (!moveInputMethodWindowsIfNeededLocked(true)) {
                             assignLayersLocked();
                         }
-                        performLayoutLockedInner();
                         updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES);
                         mFocusMayChange = false;
-
-                        restart = true;
                     }
                 }
 
@@ -9880,10 +9947,9 @@
                     mToBottomApps.clear();
 
                     rebuildAppWindowListLocked();
-                    restart = true;
+                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
                     moveInputMethodWindowsIfNeededLocked(false);
                     wallpaperMayChange = true;
-                    mLayoutNeeded = true;
                     // Since the window list has been rebuilt, focus might
                     // have to be recomputed since the actual order of windows
                     // might have changed again.
@@ -9892,7 +9958,7 @@
 
                 int adjResult = 0;
 
-                if (wallpaperForceHidingChanged && !restart && !mAppTransitionReady) {
+                if (wallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
                     // At this point, there was a window with a wallpaper that
                     // was force hiding other windows behind it, but now it
                     // is going away.  This may be simple -- just animate
@@ -9913,7 +9979,7 @@
                             // actually started the animation...  let's completely
                             // re-evaluate everything.
                             mLowerWallpaperTarget = mUpperWallpaperTarget = null;
-                            restart = true;
+                            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
                         }
                     }
                     adjResult = adjustWallpaperWindowsLocked();
@@ -9954,33 +10020,30 @@
                 if ((adjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Wallpaper layer changed: assigning layers + relayout");
-                    restart = true;
-                    mLayoutNeeded = true;
+                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
                     assignLayersLocked();
                 } else if ((adjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Wallpaper visibility changed: relayout");
-                    restart = true;
-                    mLayoutNeeded = true;
+                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
                 }
 
                 if (mFocusMayChange) {
                     mFocusMayChange = false;
                     if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES)) {
-                        restart = true;
+                        changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
                         adjResult = 0;
                     }
                 }
 
                 if (mLayoutNeeded) {
-                    restart = true;
-                    performLayoutLockedInner();
+                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
                 }
 
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: restart="
-                        + restart);
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
+                        + Integer.toHexString(changes));
                 
-            } while (restart);
+            } while (changes != 0);
 
             // THIRD LOOP: Update the surfaces of all windows.
 
@@ -10022,10 +10085,9 @@
                         w.mLastRequestedHeight = height;
                         w.mLastShownFrame.set(w.mShownFrame);
                         try {
-                            if (SHOW_TRANSACTIONS) Slog.i(
-                                    TAG, "  SURFACE " + w.mSurface
-                                    + ": POS " + w.mShownFrame.left
-                                    + ", " + w.mShownFrame.top);
+                            if (SHOW_TRANSACTIONS) logSurface(w,
+                                    "POS " + w.mShownFrame.left
+                                    + ", " + w.mShownFrame.top, null);
                             w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
                         } catch (RuntimeException e) {
                             Slog.w(TAG, "Error positioning surface in " + w, e);
@@ -10045,12 +10107,11 @@
                         if (height < 1) height = 1;
                         if (w.mSurface != null) {
                             try {
-                                if (SHOW_TRANSACTIONS) Slog.i(
-                                        TAG, "  SURFACE " + w.mSurface + ": POS "
-                                        + w.mShownFrame.left + ","
+                                if (SHOW_TRANSACTIONS) logSurface(w,
+                                        "POS " + w.mShownFrame.left + ","
                                         + w.mShownFrame.top + " SIZE "
                                         + w.mShownFrame.width() + "x"
-                                        + w.mShownFrame.height());
+                                        + w.mShownFrame.height(), null);
                                 w.mSurface.setSize(width, height);
                                 w.mSurface.setPosition(w.mShownFrame.left,
                                         w.mShownFrame.top);
@@ -10136,8 +10197,8 @@
                         if (!w.mLastHidden) {
                             //dump();
                             w.mLastHidden = true;
-                            if (SHOW_TRANSACTIONS) Slog.i(
-                                    TAG, "  SURFACE " + w.mSurface + ": HIDE (performLayout)");
+                            if (SHOW_TRANSACTIONS) logSurface(w,
+                                    "HIDE (performLayout)", null);
                             if (w.mSurface != null) {
                                 try {
                                     w.mSurface.hide();
@@ -10176,13 +10237,12 @@
                         w.mLastDtDy = w.mDtDy;
                         w.mLastHScale = w.mHScale;
                         w.mLastVScale = w.mVScale;
-                        if (SHOW_TRANSACTIONS) Slog.i(
-                                TAG, "  SURFACE " + w.mSurface + ": alpha="
-                                + w.mShownAlpha + " layer=" + w.mAnimLayer
+                        if (SHOW_TRANSACTIONS) logSurface(w,
+                                "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer
                                 + " matrix=[" + (w.mDsDx*w.mHScale)
                                 + "," + (w.mDtDx*w.mVScale)
                                 + "][" + (w.mDsDy*w.mHScale)
-                                + "," + (w.mDtDy*w.mVScale) + "]");
+                                + "," + (w.mDtDy*w.mVScale) + "]", null);
                         if (w.mSurface != null) {
                             try {
                                 w.mSurface.setAlpha(w.mShownAlpha);
@@ -10201,8 +10261,8 @@
                         if (w.mLastHidden && !w.mDrawPending
                                 && !w.mCommitDrawPending
                                 && !w.mReadyToShow) {
-                            if (SHOW_TRANSACTIONS) Slog.i(
-                                    TAG, "  SURFACE " + w.mSurface + ": SHOW (performLayout)");
+                            if (SHOW_TRANSACTIONS) logSurface(w,
+                                    "SHOW (performLayout)", null);
                             if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
                                     + " during relayout");
                             if (showSurfaceRobustlyLocked(w)) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b53100f..f6289ae 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -13226,9 +13226,11 @@
                     ac.updateConfiguration(mConfiguration);
                 }
 
-                Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
-                msg.obj = new Configuration(mConfiguration);
-                mHandler.sendMessage(msg);
+                if (Settings.System.hasInterestingConfigurationChanges(changes)) {
+                    Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
+                    msg.obj = new Configuration(mConfiguration);
+                    mHandler.sendMessage(msg);
+                }
         
                 for (int i=mLruProcesses.size()-1; i>=0; i--) {
                     ProcessRecord app = mLruProcesses.get(i);