Use ConfigurationContainer for holding configs in AM hierarchy

Extract configuration holding and handling into separate class
and use it both in hierarchy in AM.

Change-Id: I19ca20152f7473af2c4a7bbedeff69422b1454eb
Test: ConfigurationContainerTests
Test: Existing and manual tests still pass.
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4d7c4d3..af0e926 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -56,6 +56,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
@@ -74,8 +76,6 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-import static android.content.res.Configuration.SCREENLAYOUT_LONG_MASK;
-import static android.content.res.Configuration.SCREENLAYOUT_SIZE_MASK;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
@@ -93,7 +93,7 @@
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
 
-final class TaskRecord {
+final class TaskRecord extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -271,8 +271,8 @@
     // This number will be assigned when we evaluate OOM scores for all visible tasks.
     int mLayerRank = -1;
 
-    /** Contains configurations settings that are different from the parent's configuration. */
-    Configuration mOverrideConfig = Configuration.EMPTY;
+    /** Helper object used for updating override configuration. */
+    private Configuration mTmpConfig = new Configuration();
 
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
@@ -531,8 +531,10 @@
         return mStack;
     }
 
+    /** Must be used for setting parent stack because it performs configuration updates. */
     void setStack(ActivityStack stack) {
         mStack = stack;
+        onParentChanged();
     }
 
     /**
@@ -542,6 +544,21 @@
         return mStack != null ? mStack.mStackId : INVALID_STACK_ID;
     }
 
+    @Override
+    protected int getChildCount() {
+        return 0;
+    }
+
+    @Override
+    protected ConfigurationContainer getChildAt(int index) {
+        return null;
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return mStack;
+    }
+
     // Close up recents linked list.
     void closeRecentsChain() {
         if (mPrevAffiliate != null) {
@@ -591,7 +608,6 @@
      * @return whether the thumbnail was set
      */
     boolean setLastThumbnailLocked(Bitmap thumbnail) {
-        final Configuration serviceConfig = mService.mGlobalConfiguration;
         int taskWidth = 0;
         int taskHeight = 0;
         if (mBounds != null) {
@@ -607,7 +623,11 @@
         } else {
             Slog.e(TAG, "setLastThumbnailLocked() called on Task without stack");
         }
-        return setLastThumbnailLocked(thumbnail, taskWidth, taskHeight, serviceConfig.orientation);
+        // We need to provide the current orientation of the display on which this task resides,
+        // not the orientation of the task.
+        final int orientation =
+                getStack().mActivityContainer.mActivityDisplay.getConfiguration().orientation;
+        return setLastThumbnailLocked(thumbnail, taskWidth, taskHeight, orientation);
     }
 
     /**
@@ -1487,8 +1507,9 @@
         if (Objects.equals(mBounds, bounds)) {
             return false;
         }
-        final Configuration oldConfig = mOverrideConfig;
+        mTmpConfig.setTo(getOverrideConfiguration());
         final boolean oldFullscreen = mFullscreen;
+        final Configuration newConfig = getOverrideConfiguration();
 
         mFullscreen = bounds == null;
         if (mFullscreen) {
@@ -1496,7 +1517,7 @@
                 mLastNonFullscreenBounds = mBounds;
             }
             mBounds = null;
-            mOverrideConfig = Configuration.EMPTY;
+            newConfig.unset();
         } else {
             mTmpRect.set(bounds);
             adjustForMinimalTaskDimensions(mTmpRect);
@@ -1508,15 +1529,16 @@
             if (mStack == null || StackId.persistTaskBounds(mStack.mStackId)) {
                 mLastNonFullscreenBounds = mBounds;
             }
-            mOverrideConfig = calculateOverrideConfig(mTmpRect, insetBounds,
+            calculateOverrideConfig(newConfig, mTmpRect, insetBounds,
                     mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
         }
+        onOverrideConfigurationChanged(newConfig);
 
         if (mFullscreen != oldFullscreen) {
             mService.mStackSupervisor.scheduleReportMultiWindowModeChanged(this);
         }
 
-        return !mOverrideConfig.equals(oldConfig);
+        return !mTmpConfig.equals(newConfig);
     }
 
     private void subtractNonDecorInsets(Rect inOutBounds, Rect inInsetBounds,
@@ -1541,8 +1563,9 @@
         inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
     }
 
-    private Configuration calculateOverrideConfig(Rect bounds, Rect insetBounds,
-                                                  boolean overrideWidth, boolean overrideHeight) {
+    /** Clears passed config and fills it with new override values. */
+    private Configuration calculateOverrideConfig(Configuration config, Rect bounds,
+            Rect insetBounds, boolean overrideWidth, boolean overrideHeight) {
         mTmpNonDecorBounds.set(bounds);
         mTmpStableBounds.set(bounds);
         subtractNonDecorInsets(
@@ -1552,16 +1575,16 @@
                 mTmpStableBounds, insetBounds != null ? insetBounds : bounds,
                 overrideWidth, overrideHeight);
 
-        // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen area,
+        // For calculating screenWidthDp, screenHeightDp, we use the stable inset screen area,
         // i.e. the screen area without the system bars.
-        final Configuration serviceConfig = mService.mGlobalConfiguration;
-        final Configuration config = new Configuration(Configuration.EMPTY);
-        // TODO(multidisplay): Update Dp to that of display stack is on.
-        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        // Additionally task dimensions should not be bigger than its parents dimensions.
+        final Configuration parentConfig = getParent().getConfiguration();
+        config.unset();
+        final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
         config.screenWidthDp =
-                Math.min((int)(mTmpStableBounds.width() / density), serviceConfig.screenWidthDp);
+                Math.min((int)(mTmpStableBounds.width() / density), parentConfig.screenWidthDp);
         config.screenHeightDp =
-                Math.min((int)(mTmpStableBounds.height() / density), serviceConfig.screenHeightDp);
+                Math.min((int)(mTmpStableBounds.height() / density), parentConfig.screenHeightDp);
 
         // TODO: Orientation?
         config.orientation = (config.screenWidthDp <= config.screenHeightDp)
@@ -1573,9 +1596,11 @@
         // never go away in Honeycomb.
         final int compatScreenWidthDp = (int)(mTmpNonDecorBounds.width() / density);
         final int compatScreenHeightDp = (int)(mTmpNonDecorBounds.height() / density);
-        final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout);
+        // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
+        // calculation with partial default.
+        final int sl = Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_XLARGE;
         final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
-        final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);;
+        final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
         config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
 
         config.smallestScreenWidthDp = mService.mWindowManager.getSmallestWidthForTaskBounds(
@@ -1589,12 +1614,14 @@
      * {@param config}.
      */
     Configuration extractOverrideConfig(Configuration config) {
-        final Configuration extracted = new Configuration(Configuration.EMPTY);
+        final Configuration extracted = new Configuration();
         extracted.screenWidthDp = config.screenWidthDp;
         extracted.screenHeightDp = config.screenHeightDp;
         extracted.smallestScreenWidthDp = config.smallestScreenWidthDp;
         extracted.orientation = config.orientation;
-        extracted.screenLayout = config.screenLayout;
+        // We're only overriding LONG, SIZE and COMPAT parts of screenLayout.
+        extracted.screenLayout = config.screenLayout & (Configuration.SCREENLAYOUT_LONG_MASK
+                | Configuration.SCREENLAYOUT_SIZE_MASK | Configuration.SCREENLAYOUT_COMPAT_NEEDED);
         return extracted;
     }
 
@@ -1607,29 +1634,6 @@
         return bounds;
     }
 
-    /**
-     * Update fields that are not overridden for task from global configuration.
-     *
-     * @param globalConfig global configuration to update from.
-     */
-    void sanitizeOverrideConfiguration(Configuration globalConfig) {
-        // If it's fullscreen, the override config should be empty and we should leave it alone.
-        if (mFullscreen) {
-            return;
-        }
-
-        // screenLayout field is set in #calculateOverrideConfig but only part of it is really
-        // overridden - aspect ratio and size. Other flags (like layout direction) can be updated
-        // separately in global config and they also must be updated in override config.
-        int overrideScreenLayout = mOverrideConfig.screenLayout;
-        int newScreenLayout = globalConfig.screenLayout;
-        newScreenLayout = (newScreenLayout & ~SCREENLAYOUT_LONG_MASK)
-                | (overrideScreenLayout & SCREENLAYOUT_LONG_MASK);
-        newScreenLayout = (newScreenLayout & ~SCREENLAYOUT_SIZE_MASK)
-                | (overrideScreenLayout & SCREENLAYOUT_SIZE_MASK);
-        mOverrideConfig.screenLayout = newScreenLayout;
-    }
-
     static Rect validateBounds(Rect bounds) {
         if (bounds != null && bounds.isEmpty()) {
             Slog.wtf(TAG, "Received strange task bounds: " + bounds, new Throwable());