Introducing ConfigurationContainerListener

Class provides a structured way for window containers on the window
manager side to listener for configuration changes happening in the
window controllers on the activity manager side so things stay in
sync and they don't miss anything.

Test: bit FrameworksServicesTests:com.android.server.wm.WindowContainerControllerTests
Test: bit FrameworksServicesTests:com.android.server.wm.ConfigurationContainerTests
Test: go/wm-smoke
Change-Id: I6a6bc98a35bda89b3aaa7a29a9814905f0c3a9ee
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 651d3a6..0ccb45f 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -191,10 +191,8 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_AM;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
-    private static final String TAG_SCREENSHOTS = TAG + POSTFIX_SCREENSHOTS;
     private static final String TAG_STATES = TAG + POSTFIX_STATES;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    private static final String TAG_THUMBNAILS = TAG + POSTFIX_THUMBNAILS;
     private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     private static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -937,8 +935,7 @@
                 (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
                 task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
                 appInfo.targetSdkVersion, mRotationAnimationHint,
-                ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L,
-                new Configuration(getOverrideConfiguration()), mBounds);
+                ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L, mBounds);
 
         task.addActivityToTop(this);
 
@@ -1122,7 +1119,7 @@
      * @return whether this activity supports PiP multi-window and can be put in the pinned stack.
      */
     boolean supportsPictureInPicture() {
-        return service.mSupportsPictureInPicture && isActivityTypeStandard()
+        return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
                 && info.supportsPictureInPicture();
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index cc45e02..84bf18a0 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -466,15 +466,13 @@
         mTmpRect2.setEmpty();
         updateOverrideConfiguration();
         mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
-                mTmpRect2, getOverrideConfiguration());
+                mTmpRect2);
         mStackSupervisor.mStacks.put(mStackId, this);
         postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
     }
 
-    T createStackWindowController(int displayId, boolean onTop, Rect outBounds,
-            Configuration overrideConfig) {
-        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
-                overrideConfig);
+    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
+        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
     }
 
     T getWindowContainerController() {
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 86ee3f4..a601ee1 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -39,9 +39,8 @@
 
     @Override
     PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
-            Rect outBounds, Configuration overrideConfig) {
-        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
-                overrideConfig);
+            Rect outBounds) {
+        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
     }
 
     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fc1c02f..62afcd2 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -430,9 +430,8 @@
         }
 
         final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
-        final Configuration overrideConfig = getOverrideConfiguration();
         setWindowContainerController(new TaskWindowContainerController(taskId, this,
-                getStack().getWindowContainerController(), userId, bounds, overrideConfig,
+                getStack().getWindowContainerController(), userId, bounds,
                 mResizeMode, mSupportsPictureInPicture, onTop,
                 showForAllUsers, lastTaskDescription));
     }
@@ -1175,9 +1174,6 @@
         }
         // Only set this based on the first activity
         if (mActivities.isEmpty()) {
-            // TODO: propagating this change to the WM side...Should probably be done by having
-            // ConfigurationContainer change listener that the WindowContainerController registers
-            // for.
             if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
                 // Normally non-standard activity type for the activity record will be set when the
                 // object is created, however we delay setting the standard application type until
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 66e0a15..769ceac78 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -181,12 +181,12 @@
             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
             boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
             int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
-            Configuration overrideConfig, Rect bounds) {
+            Rect bounds) {
         this(taskController, token, listener, index, requestedOrientation, fullscreen,
                 showForAllUsers,
                 configChanges, voiceInteraction, launchTaskBehind, alwaysFocusable,
                 targetSdkVersion, rotationAnimationHint, inputDispatchingTimeoutNanos,
-                WindowManagerService.getInstance(), overrideConfig, bounds);
+                WindowManagerService.getInstance(), bounds);
     }
 
     public AppWindowContainerController(TaskWindowContainerController taskController,
@@ -194,7 +194,7 @@
             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
             boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
             int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
-            WindowManagerService service, Configuration overrideConfig, Rect bounds) {
+            WindowManagerService service, Rect bounds) {
         super(listener, service);
         mHandler = new H(service.mH.getLooper());
         mToken = token;
@@ -215,7 +215,7 @@
             atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
                     requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
-                    alwaysFocusable, this, overrideConfig, bounds);
+                    alwaysFocusable, this, bounds);
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
                     + " controller=" + taskController + " at " + index);
             task.addChild(atoken, index);
@@ -227,12 +227,11 @@
             boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
             boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
             int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-            boolean alwaysFocusable, AppWindowContainerController controller,
-            Configuration overrideConfig, Rect bounds) {
+            boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) {
         return new AppWindowToken(service, token, voiceInteraction, dc,
                 inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
                 rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                controller, overrideConfig, bounds);
+                controller, bounds);
     }
 
     public void removeContainer(int displayId) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index f70035c..ea2f305 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -187,7 +187,7 @@
 
     private boolean mDisablePreviewScreenshots;
 
-    Task mLastParent;
+    private Task mLastParent;
 
     /**
      * See {@link #canTurnScreenOn()}
@@ -198,8 +198,8 @@
             DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
             boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
             int configChanges, boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller, Configuration overrideConfig, Rect bounds) {
-        this(service, token, voiceInteraction, dc, fullscreen, overrideConfig, bounds);
+            AppWindowContainerController controller, Rect bounds) {
+        this(service, token, voiceInteraction, dc, fullscreen, bounds);
         setController(controller);
         mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
         mShowForAllUsers = showForAllUsers;
@@ -216,7 +216,7 @@
     }
 
     AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
-            DisplayContent dc, boolean fillsParent, Configuration overrideConfig, Rect bounds) {
+            DisplayContent dc, boolean fillsParent, Rect bounds) {
         super(service, token != null ? token.asBinder() : null, TYPE_APPLICATION, true, dc,
                 false /* ownerCanManageAppTokens */);
         appToken = token;
@@ -224,9 +224,6 @@
         mFillsParent = fillsParent;
         mInputApplicationHandle = new InputApplicationHandle(this);
         mAppAnimator = new AppWindowAnimator(this, service);
-        if (overrideConfig != null) {
-            onOverrideConfigurationChanged(overrideConfig);
-        }
         if (bounds != null) {
             mBounds.set(bounds);
         }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 21b67f1..cbf4fc0 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -26,6 +26,8 @@
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 
+import java.util.ArrayList;
+
 /**
  * Contains common logic for classes that have override configurations and are organized in a
  * hierarchy.
@@ -48,6 +50,8 @@
      */
     private Configuration mMergedOverrideConfiguration = new Configuration();
 
+    private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>();
+
     // TODO: Can't have ag/2592611 soon enough!
     private final Configuration mTmpConfig = new Configuration();
 
@@ -90,6 +94,11 @@
         onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
         // Update merged override config of this container and all its children.
         onMergedOverrideConfigurationChanged();
+
+        // Inform listeners of the change.
+        for (int i = mChangeListeners.size() - 1; i >=0; --i) {
+            mChangeListeners.get(i).onOverrideConfigurationChanged(overrideConfiguration);
+        }
     }
 
     /**
@@ -173,6 +182,11 @@
         return getActivityType() == ACTIVITY_TYPE_STANDARD;
     }
 
+    public boolean isActivityTypeStandardOrUndefined() {
+        /*@WindowConfiguration.ActivityType*/ final int activityType = getActivityType();
+        return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED;
+    }
+
     public boolean hasCompatibleActivityType(ConfigurationContainer other) {
         /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType();
         /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType();
@@ -182,6 +196,18 @@
                 || otherType == ACTIVITY_TYPE_UNDEFINED;
     }
 
+    public void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
+        if (mChangeListeners.contains(listener)) {
+            return;
+        }
+        mChangeListeners.add(listener);
+        listener.onOverrideConfigurationChanged(mOverrideConfiguration);
+    }
+
+    public void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
+        mChangeListeners.remove(listener);
+    }
+
     /**
      * Must be called when new parent for the container was set.
      */
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
new file mode 100644
index 0000000..ff14d97
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.wm;
+
+import android.content.res.Configuration;
+
+/**
+ * Interface for listening to changes in a {@link ConfigurationContainer}.
+ */
+public interface ConfigurationContainerListener {
+
+    void onOverrideConfigurationChanged(Configuration overrideConfiguration);
+}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index b5c9b99..590ac6e 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -40,9 +40,8 @@
     private Rect mTmpToBounds = new Rect();
 
     public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, Configuration overrideConfig) {
-        super(stackId, listener, displayId, onTop, outBounds, overrideConfig,
-                WindowManagerService.getInstance());
+            int displayId, boolean onTop, Rect outBounds) {
+        super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index aaacef5..a50ed71 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -57,16 +57,14 @@
     private final Rect mTmpNonDecorInsets = new Rect();
     private final Rect mTmpDisplayBounds = new Rect();
 
-    public StackWindowController(int stackId, StackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, Configuration overriderConfig) {
-        this(stackId, listener, displayId, onTop, outBounds, overriderConfig,
-                WindowManagerService.getInstance());
+    public StackWindowController(int stackId, StackWindowListener listener, int displayId,
+            boolean onTop, Rect outBounds) {
+        this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
     }
 
     @VisibleForTesting
     public StackWindowController(int stackId, StackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, Configuration overrideConfig,
-            WindowManagerService service) {
+            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
         super(listener, service);
         mStackId = stackId;
         mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
@@ -79,9 +77,6 @@
             }
 
             final TaskStack stack = dc.addStackToDisplay(stackId, onTop);
-            if (overrideConfig != null) {
-                stack.onOverrideConfigurationChanged(overrideConfig);
-            }
             stack.setController(this);
             getRawBounds(outBounds);
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f57238e..e9f8457 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -106,8 +106,8 @@
     private boolean mPreserveNonFloatingState = false;
 
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
-            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
-            TaskDescription taskDescription, TaskWindowContainerController controller) {
+            int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription,
+            TaskWindowContainerController controller) {
         mTaskId = taskId;
         mStack = stack;
         mUserId = userId;
@@ -115,7 +115,7 @@
         mResizeMode = resizeMode;
         mSupportsPictureInPicture = supportsPictureInPicture;
         setController(controller);
-        setBounds(bounds, overrideConfig);
+        setBounds(bounds, getOverrideConfiguration());
         mTaskDescription = taskDescription;
 
         // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
@@ -273,6 +273,9 @@
     }
 
     /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
+    // TODO: There is probably not a need to pass in overrideConfig anymore since any change to it
+    // will be automatically propagated from the AM. Also, mBound is going to be in
+    // WindowConfiguration long term.
     private int setBounds(Rect bounds, Configuration overrideConfig) {
         if (overrideConfig == null) {
             overrideConfig = Configuration.EMPTY;
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index d8929c9..65f8cdf 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -49,19 +49,18 @@
     private final H mHandler;
 
     public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
-            StackWindowController stackController, int userId, Rect bounds,
-            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
-            boolean toTop, boolean showForAllUsers, TaskDescription taskDescription) {
-        this(taskId, listener, stackController, userId, bounds, overrideConfig, resizeMode,
+            StackWindowController stackController, int userId, Rect bounds, int resizeMode,
+            boolean supportsPictureInPicture, boolean toTop, boolean showForAllUsers,
+            TaskDescription taskDescription) {
+        this(taskId, listener, stackController, userId, bounds, resizeMode,
                 supportsPictureInPicture, toTop, showForAllUsers, taskDescription,
                 WindowManagerService.getInstance());
     }
 
     public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
-            StackWindowController stackController, int userId, Rect bounds,
-            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
-            boolean toTop, boolean showForAllUsers, TaskDescription taskDescription,
-            WindowManagerService service) {
+            StackWindowController stackController, int userId, Rect bounds, int resizeMode,
+            boolean supportsPictureInPicture, boolean toTop, boolean showForAllUsers,
+            TaskDescription taskDescription, WindowManagerService service) {
         super(listener, service);
         mTaskId = taskId;
         mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
@@ -76,7 +75,7 @@
                         + stackController);
             }
             EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
-            final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
+            final Task task = createTask(taskId, stack, userId, bounds, resizeMode,
                     supportsPictureInPicture, taskDescription);
             final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
             // We only want to move the parents to the parents if we are creating this task at the
@@ -86,10 +85,9 @@
     }
 
     @VisibleForTesting
-    Task createTask(int taskId, TaskStack stack, int userId, Rect bounds,
-            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
-            TaskDescription taskDescription) {
-        return new Task(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode,
+    Task createTask(int taskId, TaskStack stack, int userId, Rect bounds, int resizeMode,
+            boolean supportsPictureInPicture, TaskDescription taskDescription) {
+        return new Task(taskId, stack, userId, mService, bounds, resizeMode,
                 supportsPictureInPicture, taskDescription, this);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainerController.java b/services/core/java/com/android/server/wm/WindowContainerController.java
index c4a6837..eb23faf 100644
--- a/services/core/java/com/android/server/wm/WindowContainerController.java
+++ b/services/core/java/com/android/server/wm/WindowContainerController.java
@@ -16,9 +16,7 @@
 
 package com.android.server.wm;
 
-import android.os.IBinder;
-
-import java.util.HashMap;
+import android.content.res.Configuration;
 
 /**
  * Class that allows the owner/creator of a {@link WindowContainer} to communicate directly with the
@@ -29,7 +27,8 @@
  *
  * Test class: {@link WindowContainerControllerTests}
  */
-class WindowContainerController<E extends WindowContainer, I extends WindowContainerListener> {
+class WindowContainerController<E extends WindowContainer, I extends WindowContainerListener>
+        implements ConfigurationContainerListener {
 
     final WindowManagerService mService;
     final RootWindowContainer mRoot;
@@ -53,18 +52,32 @@
                     + " for controller=" + this + " Already set to=" + mContainer);
         }
         mContainer = container;
+        if (mContainer != null && mListener != null) {
+            mListener.registerConfigurationChangeListener(this);
+        }
     }
 
     void removeContainer() {
         // TODO: See if most uses cases should support removeIfPossible here.
         //mContainer.removeIfPossible();
-        if (mContainer != null) {
-            mContainer.setController(null);
-            mContainer = null;
+        if (mContainer == null) {
+            return;
+        }
+
+        mContainer.setController(null);
+        mContainer = null;
+        if (mListener != null) {
+            mListener.unregisterConfigurationChangeListener(this);
         }
     }
 
-    boolean checkCallingPermission(String permission, String func) {
-        return mService.checkCallingPermission(permission, func);
+    @Override
+    public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                return;
+            }
+            mContainer.onOverrideConfigurationChanged(overrideConfiguration);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java
index ab9d71a..4b3cd36 100644
--- a/services/core/java/com/android/server/wm/WindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/WindowContainerListener.java
@@ -21,5 +21,6 @@
  * @see WindowContainerController
  */
 public interface WindowContainerListener {
-
+    void registerConfigurationChangeListener(ConfigurationContainerListener listener);
+    void unregisterConfigurationChangeListener(ConfigurationContainerListener listener);
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 5b8df299..b534544 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -281,8 +281,7 @@
         }
 
         @Override
-        protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds,
-                Configuration overrideConfig) {
+        protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
             mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController();
             return mContainerController;
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
index d441df0..c37b0d8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -27,6 +27,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+import static android.content.res.Configuration.EMPTY;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -55,22 +56,22 @@
     public void testConfigurationInit() throws Exception {
         // Check root container initial config.
         final TestConfigurationContainer root = new TestConfigurationContainer();
-        assertEquals(Configuration.EMPTY, root.getOverrideConfiguration());
-        assertEquals(Configuration.EMPTY, root.getMergedOverrideConfiguration());
-        assertEquals(Configuration.EMPTY, root.getConfiguration());
+        assertEquals(EMPTY, root.getOverrideConfiguration());
+        assertEquals(EMPTY, root.getMergedOverrideConfiguration());
+        assertEquals(EMPTY, root.getConfiguration());
 
         // Check child initial config.
         final TestConfigurationContainer child1 = root.addChild();
-        assertEquals(Configuration.EMPTY, child1.getOverrideConfiguration());
-        assertEquals(Configuration.EMPTY, child1.getMergedOverrideConfiguration());
-        assertEquals(Configuration.EMPTY, child1.getConfiguration());
+        assertEquals(EMPTY, child1.getOverrideConfiguration());
+        assertEquals(EMPTY, child1.getMergedOverrideConfiguration());
+        assertEquals(EMPTY, child1.getConfiguration());
 
         // Check child initial config if root has overrides.
         final Configuration rootOverrideConfig = new Configuration();
         rootOverrideConfig.fontScale = 1.3f;
         root.onOverrideConfigurationChanged(rootOverrideConfig);
         final TestConfigurationContainer child2 = root.addChild();
-        assertEquals(Configuration.EMPTY, child2.getOverrideConfiguration());
+        assertEquals(EMPTY, child2.getOverrideConfiguration());
         assertEquals(rootOverrideConfig, child2.getMergedOverrideConfiguration());
         assertEquals(rootOverrideConfig, child2.getConfiguration());
 
@@ -83,7 +84,7 @@
         rootFullConfig.updateFrom(rootOverrideConfig);
 
         final TestConfigurationContainer child3 = root.addChild();
-        assertEquals(Configuration.EMPTY, child3.getOverrideConfiguration());
+        assertEquals(EMPTY, child3.getOverrideConfiguration());
         assertEquals(rootOverrideConfig, child3.getMergedOverrideConfiguration());
         assertEquals(rootFullConfig, child3.getConfiguration());
     }
@@ -263,6 +264,22 @@
 
     }
 
+    @Test
+    public void testRegisterConfigurationChangeListener() throws Exception {
+        final TestConfigurationContainer container = new TestConfigurationContainer();
+        final TestConfigurationChangeListener listener = new TestConfigurationChangeListener();
+        final Configuration config = new Configuration();
+        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        config.windowConfiguration.setAppBounds(10, 10, 10, 10);
+        container.onOverrideConfigurationChanged(config);
+        container.registerConfigurationChangeListener(listener);
+        // Assert listener got the current config. of the container after it was registered.
+        assertEquals(config, listener.mOverrideConfiguration);
+        // Assert listener gets changes to override configuration.
+        container.onOverrideConfigurationChanged(EMPTY);
+        assertEquals(EMPTY, listener.mOverrideConfiguration);
+    }
+
     /**
      * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
      * for testing.
@@ -303,4 +320,13 @@
             return mParent;
         }
     }
+
+    private class TestConfigurationChangeListener implements ConfigurationContainerListener {
+
+        final Configuration mOverrideConfiguration = new Configuration();
+
+        public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+            mOverrideConfiguration.setTo(overrideConfiguration);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
index 52f66ae..5f1c011 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -56,8 +56,8 @@
         overrideConfig.windowConfiguration.setWindowingMode(
                 getWindowingModeForStackId(PINNED_STACK_ID));
         mPinnedStack = new StackWindowController(PINNED_STACK_ID, null,
-                mDisplayContent.getDisplayId(), true /* onTop */, new Rect(),
-                overrideConfig, sWm).mContainer;
+                mDisplayContent.getDisplayId(), true /* onTop */, new Rect(), sWm).mContainer;
+        mPinnedStack.onOverrideConfigurationChanged(overrideConfig);
 
         // Stack should contain visible app window to be considered visible.
         final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
index f1e15d9..8df7568 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -16,12 +16,16 @@
 
 package com.android.server.wm;
 
+import android.app.WindowConfiguration;
+import android.content.res.Configuration;
 import org.junit.Test;
 
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.res.Configuration.EMPTY;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -80,4 +84,27 @@
         controller.removeContainer();
         assertNull(controller.mContainer);
     }
+
+    @Test
+    public void testOnOverrideConfigurationChanged() throws Exception {
+        final WindowContainerController controller = new WindowContainerController(null, sWm);
+        final WindowContainer container = new WindowContainer();
+
+        controller.setContainer(container);
+        assertEquals(controller.mContainer, container);
+        assertEquals(EMPTY, container.getOverrideConfiguration());
+
+        final Configuration config = new Configuration();
+        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        config.windowConfiguration.setAppBounds(10, 10, 10, 10);
+
+        // Assert that the config change through the controller is propagated to the container.
+        controller.onOverrideConfigurationChanged(config);
+        assertEquals(config, container.getOverrideConfiguration());
+
+        // Assert the container configuration isn't changed after removal from the controller.
+        controller.removeContainer();
+        controller.onOverrideConfigurationChanged(EMPTY);
+        assertEquals(config, container.getOverrideConfiguration());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index df3f755..e39ccca 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -75,7 +75,7 @@
         final Rect mInsetBounds = new Rect();
         boolean mFullscreenForTest = true;
         TaskWithBounds(Rect bounds) {
-            super(0, mStubStack, 0, sWm, null, null, 0, false, new TaskDescription(), null);
+            super(0, mStubStack, 0, sWm, null, 0, false, new TaskDescription(), null);
             mBounds = bounds;
         }
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index ebe00ce..2ae10aa 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -66,7 +66,7 @@
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
     public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
             int userId) {
-        final Task newTask = new Task(sNextTaskId++, stack, userId, service, null, EMPTY, 0, false,
+        final Task newTask = new Task(sNextTaskId++, stack, userId, service, null, 0, false,
                 new ActivityManager.TaskDescription(), null);
         stack.addTask(newTask, POSITION_TOP);
         return newTask;
@@ -94,19 +94,17 @@
         TestAppWindowToken(DisplayContent dc) {
             super(dc.mService, new IApplicationToken.Stub() {
                 public String getName() {return null;}
-                }, false, dc, true /* fillsParent */,
-                    null /* overrideConfig */, null /* bounds */);
+                }, false, dc, true /* fillsParent */, null /* bounds */);
         }
 
         TestAppWindowToken(WindowManagerService service, IApplicationToken token,
                 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
                 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
                 int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-                boolean alwaysFocusable, AppWindowContainerController controller,
-                Configuration overrideConfig, Rect bounds) {
+                boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) {
             super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen,
                     showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges,
-                    launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds);
+                    launchTaskBehind, alwaysFocusable, controller, bounds);
         }
 
         int getWindowsCount() {
@@ -174,10 +172,10 @@
         private boolean mIsAnimating = false;
 
         TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
-                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+                int resizeMode, boolean supportsPictureInPicture,
                 TaskWindowContainerController controller) {
-            super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode,
-                    supportsPictureInPicture, new ActivityManager.TaskDescription(), controller);
+            super(taskId, stack, userId, service, bounds, resizeMode, supportsPictureInPicture,
+                    new ActivityManager.TaskDescription(), controller);
         }
 
         boolean shouldDeferRemoval() {
@@ -218,6 +216,18 @@
         TestTaskWindowContainerController(StackWindowController stackController) {
             super(sNextTaskId++, new TaskWindowContainerListener() {
                         @Override
+                        public void registerConfigurationChangeListener(
+                                ConfigurationContainerListener listener) {
+
+                        }
+
+                        @Override
+                        public void unregisterConfigurationChangeListener(
+                                ConfigurationContainerListener listener) {
+
+                        }
+
+                        @Override
                         public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
 
                         }
@@ -226,18 +236,16 @@
                         public void requestResize(Rect bounds, int resizeMode) {
 
                         }
-                    }, stackController, 0 /* userId */, null /* bounds */,
-                    EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE,
+                    }, stackController, 0 /* userId */, null /* bounds */, RESIZE_MODE_UNRESIZEABLE,
                     false /* supportsPictureInPicture */, true /* toTop*/,
                     true /* showForAllUsers */, new ActivityManager.TaskDescription(),
                     stackController.mService);
         }
 
         @Override
-        TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds,
-                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
-                ActivityManager.TaskDescription taskDescription) {
-            return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode,
+        TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, int resizeMode,
+                boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
+            return new TestTask(taskId, stack, userId, mService, bounds, resizeMode,
                     supportsPictureInPicture, this);
         }
     }
@@ -258,7 +266,7 @@
                     false /* launchTaskBehind */, false /* alwaysFocusable */,
                     0 /* targetSdkVersion */, 0 /* rotationAnimationHint */,
                     0 /* inputDispatchingTimeoutNanos */, taskController.mService,
-                    null /* overrideConfig */, null /* bounds */);
+                    null /* bounds */);
             mToken = token;
         }
 
@@ -267,13 +275,12 @@
                 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
                 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
                 int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-                boolean alwaysFocusable, AppWindowContainerController controller,
-                Configuration overrideConfig, Rect bounds) {
+                boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) {
             return new TestAppWindowToken(service, token, voiceInteraction, dc,
                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
                     orientation,
                     rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                    controller, overrideConfig, bounds);
+                    controller, bounds);
         }
 
         AppWindowToken getAppWindowToken(DisplayContent dc) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index bce740b..817f2b4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -237,12 +237,14 @@
         return createStackControllerOnStackOnDisplay(stackId, dc);
     }
 
-    StackWindowController createStackControllerOnStackOnDisplay(int stackId,
-            DisplayContent dc) {
+    StackWindowController createStackControllerOnStackOnDisplay(int stackId, DisplayContent dc) {
         final Configuration overrideConfig = new Configuration();
-        overrideConfig.windowConfiguration.setWindowingMode(getWindowingModeForStackId(stackId));
-        return new StackWindowController(stackId, null, dc.getDisplayId(),
-                true /* onTop */, new Rect(), overrideConfig, sWm);
+        overrideConfig.windowConfiguration.setWindowingMode(
+                getWindowingModeForStackId(stackId));
+        final StackWindowController controller = new StackWindowController(stackId, null,
+                dc.getDisplayId(), true /* onTop */, new Rect(), sWm);
+        controller.onOverrideConfigurationChanged(overrideConfig);
+        return controller;
     }
 
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */