Also freeze configuration when freezing bounds

We also need to freeze the override configuration so we don't report
the new configuration too early, which leads to bugs.

Bug: 27915587
Change-Id: Idffadbb02ab0311796caa760ae1f467fd2d17768
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index abbb5f4..b773a4e 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -34,6 +34,7 @@
 
 import android.annotation.NonNull;
 import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Message;
 import android.os.RemoteException;
@@ -133,6 +134,7 @@
     int mPendingRelaunchCount;
 
     ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
+    ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
 
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
             boolean _voiceInteraction) {
@@ -675,6 +677,16 @@
      */
     private void freezeBounds() {
         mFrozenBounds.offer(new Rect(mTask.mPreparedFrozenBounds));
+
+        if (mTask.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
+            // We didn't call prepareFreezingBounds on the task, so use the current value.
+            final Configuration config = new Configuration(service.mCurConfiguration);
+            config.updateFrom(mTask.mOverrideConfig);
+            mFrozenMergedConfig.offer(config);
+        } else {
+            mFrozenMergedConfig.offer(new Configuration(mTask.mPreparedFrozenMergedConfig));
+        }
+        mTask.mPreparedFrozenMergedConfig.setToDefaults();
     }
 
     /**
@@ -682,6 +694,7 @@
      */
     private void unfreezeBounds() {
         mFrozenBounds.remove();
+        mFrozenMergedConfig.remove();
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
             if (!win.mHasSurface) {
@@ -747,6 +760,7 @@
         }
         if (!mFrozenBounds.isEmpty()) {
             pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
+            pw.print(prefix); pw.print("mFrozenMergedConfig="); pw.println(mFrozenMergedConfig);
         }
         if (mPendingRelaunchCount != 0) {
             pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8f4197f..0d35354 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -62,6 +62,7 @@
     // Content limits relative to the DisplayContent this sits in.
     private Rect mBounds = new Rect();
     final Rect mPreparedFrozenBounds = new Rect();
+    final Configuration mPreparedFrozenMergedConfig = new Configuration();
 
     private Rect mPreScrollBounds = new Rect();
     private boolean mScrollValid;
@@ -77,7 +78,7 @@
 
     // Contains configurations settings that are different from the global configuration due to
     // stack specific operations. E.g. {@link #setBounds}.
-    Configuration mOverrideConfig;
+    Configuration mOverrideConfig = Configuration.EMPTY;
 
     // For comparison with DisplayContent bounds.
     private Rect mTmpRect = new Rect();
@@ -320,6 +321,8 @@
      */
     void prepareFreezingBounds() {
         mPreparedFrozenBounds.set(mBounds);
+        mPreparedFrozenMergedConfig.setTo(mService.mCurConfiguration);
+        mPreparedFrozenMergedConfig.updateFrom(mOverrideConfig);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 09428b1..1132758 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3737,6 +3737,7 @@
             if (!configChanged) {
                 return null;
             }
+            prepareFreezingAllTaskBounds();
             mCurConfiguration = new Configuration(config);
             return onConfigurationChanged();
         }
@@ -3752,6 +3753,16 @@
         }
     }
 
+    private void prepareFreezingAllTaskBounds() {
+        for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
+            ArrayList<TaskStack> stacks = mDisplayContents.valueAt(i).getStacks();
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final TaskStack stack = stacks.get(stackNdx);
+                stack.prepareFreezingTaskBounds();
+            }
+        }
+
+    }
     private int[] onConfigurationChanged() {
         mPolicy.onConfigurationChanged();
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c9d945a..c3f2367 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -190,8 +190,7 @@
 
     int mLayoutSeq = -1;
 
-    private Configuration mConfiguration = Configuration.EMPTY;
-    private Configuration mOverrideConfig = Configuration.EMPTY;
+    private final Configuration mTmpConfig = new Configuration();
     // Represents the changes from our override configuration applied
     // to the global configuration. This is the only form of configuration
     // which is suitable for delivery to the client.
@@ -1417,13 +1416,12 @@
     }
 
     boolean isConfigChanged() {
-        final Task task = getTask();
-        final Configuration overrideConfig =
-                (task != null) ? task.mOverrideConfig : Configuration.EMPTY;
-        final Configuration serviceConfig = mService.mCurConfiguration;
-        boolean configChanged =
-                (mConfiguration != serviceConfig && mConfiguration.diff(serviceConfig) != 0)
-                || (mOverrideConfig != overrideConfig && !mOverrideConfig.equals(overrideConfig));
+        getMergedConfig(mTmpConfig);
+
+        // If the merged configuration is still empty, it means that we haven't issues the
+        // configuration to the client yet and we need to return true so the configuration updates.
+        boolean configChanged = mMergedConfiguration.equals(Configuration.EMPTY)
+                || mTmpConfig.diff(mMergedConfiguration) != 0;
 
         if ((mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
             // Retain configuration changed status until resetConfiguration called.
@@ -1457,18 +1455,6 @@
         }
     }
 
-    private void setConfiguration(
-            final Configuration newConfig, final Configuration newOverrideConfig) {
-        mConfiguration = newConfig;
-        mOverrideConfig = newOverrideConfig;
-        mConfigHasChanged = false;
-
-        mMergedConfiguration.setTo(newConfig);
-        if (newOverrideConfig != null && newOverrideConfig != Configuration.EMPTY) {
-            mMergedConfiguration.updateFrom(newOverrideConfig);
-        }
-    }
-
     void setHasSurface(boolean hasSurface) {
         mHasSurface = hasSurface;
     }
@@ -2266,18 +2252,32 @@
      * @return A configuration suitable for sending to the client.
      */
     private Configuration updateConfiguration() {
-        final Task task = getTask();
-        final Configuration overrideConfig =
-            (task != null) ? task.mOverrideConfig : Configuration.EMPTY;
         final boolean configChanged = isConfigChanged();
+        getMergedConfig(mMergedConfiguration);
+        mConfigHasChanged = false;
         if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
             Slog.i(TAG, "Sending new config to window " + this + ": " +
-                    " / config=" + mService.mCurConfiguration + " overrideConfig=" + overrideConfig);
+                    " / mergedConfig=" + mMergedConfiguration);
         }
-        setConfiguration(mService.mCurConfiguration, overrideConfig);
         return mMergedConfiguration;
     }
 
+    private void getMergedConfig(Configuration outConfig) {
+        if (mAppToken != null && mAppToken.mFrozenMergedConfig.size() > 0) {
+            outConfig.setTo(mAppToken.mFrozenMergedConfig.peek());
+            return;
+        }
+        final Task task = getTask();
+        final Configuration overrideConfig = task != null
+                ? task.mOverrideConfig
+                : Configuration.EMPTY;
+        final Configuration serviceConfig = mService.mCurConfiguration;
+        outConfig.setTo(serviceConfig);
+        if (overrideConfig != Configuration.EMPTY) {
+            outConfig.updateFrom(overrideConfig);
+        }
+    }
+
     void reportResized() {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
         try {
@@ -2572,10 +2572,7 @@
                 getTouchableRegion(region);
                 pw.print(prefix); pw.print("touchable region="); pw.println(region);
             }
-            pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration);
-            if (mOverrideConfig != Configuration.EMPTY) {
-                pw.print(prefix); pw.print("mOverrideConfig="); pw.println(mOverrideConfig);
-            }
+            pw.print(prefix); pw.print("mMergedConfiguration="); pw.println(mMergedConfiguration);
         }
         pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
                 pw.print(" mShownPosition="); mShownPosition.printShortString(pw);