Introducing windowing mode in WindowConfiguration.

Currently Stacks (specifically their ids) are used to determine
windowing mode for activities. This isn't flexible when it comes
to changing/mixing various windowing modes at different levels
of the window hierarchy. E.g. how do you have the non-default
display support freeform or split-screen or how do you
interleave freeform windows with fullscreen windows.
To help with this problem we are introducing windowing mode
in WindowConfiguration that can be used to set the windowing
mode for any WindowContainer in the hierarchy.

Currently all displays are set to fullscreen windowing mode and
stacks windowing modes are set based on their id.

Test: bit FrameworksServicesTests:com.android.server.wm.WindowConfigurationTests
Test: adb shell am instrument -w -e package com.android.server.wm com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
Test: go/wm-smoke
Change-Id: Iccdc3212cda651998d6ad76ce5261d089bff897a
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 86abaa3..e4ec8c3 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -44,15 +44,42 @@
      */
     private Rect mAppBounds;
 
-    @IntDef(flag = true,
-            value = {
-                    WINDOW_CONFIG_APP_BOUNDS,
-            })
+    /** The current windowing mode of the configuration. */
+    private @WindowingMode int mWindowingMode;
+
+    /** Windowing mode is currently not defined. */
+    public static final int WINDOWING_MODE_UNDEFINED = 0;
+    /** Occupies the full area of the screen or the parent container. */
+    public static final int WINDOWING_MODE_FULLSCREEN = 1;
+    /** Always on-top (always visible). of other siblings in its parent container. */
+    public static final int WINDOWING_MODE_PINNED = 2;
+    /** Occupies a dedicated region of the screen or its parent container. */
+    public static final int WINDOWING_MODE_DOCKED = 3;
+    /** Can be freely resized within its parent container. */
+    public static final int WINDOWING_MODE_FREEFORM = 4;
+
+    @IntDef(value = {
+            WINDOWING_MODE_UNDEFINED,
+            WINDOWING_MODE_FULLSCREEN,
+            WINDOWING_MODE_PINNED,
+            WINDOWING_MODE_DOCKED,
+            WINDOWING_MODE_FREEFORM,
+    })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface WindowConfig {}
+    public @interface WindowingMode {}
 
     /** Bit that indicates that the {@link #mAppBounds} changed. */
     public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 0;
+    /** Bit that indicates that the {@link #mWindowingMode} changed. */
+    public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 1;
+
+    @IntDef(flag = true,
+            value = {
+                    WINDOW_CONFIG_APP_BOUNDS,
+                    WINDOW_CONFIG_WINDOWING_MODE,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WindowConfig {}
 
     public WindowConfiguration() {
         unset();
@@ -69,10 +96,12 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(mAppBounds, flags);
+        dest.writeInt(mWindowingMode);
     }
 
     private void readFromParcel(Parcel source) {
         mAppBounds = source.readParcelable(Rect.class.getClassLoader());
+        mWindowingMode = source.readInt();
     }
 
     @Override
@@ -125,8 +154,18 @@
         return mAppBounds;
     }
 
+    public void setWindowingMode(@WindowingMode int windowingMode) {
+        mWindowingMode = windowingMode;
+    }
+
+    @WindowingMode
+    public int getWindowingMode() {
+        return mWindowingMode;
+    }
+
     public void setTo(WindowConfiguration other) {
         setAppBounds(other.mAppBounds);
+        setWindowingMode(other.mWindowingMode);
     }
 
     /** Set this object to completely undefined. */
@@ -136,6 +175,7 @@
 
     public void setToDefaults() {
         setAppBounds(null);
+        setWindowingMode(WINDOWING_MODE_UNDEFINED);
     }
 
     /**
@@ -151,6 +191,11 @@
             changed |= WINDOW_CONFIG_APP_BOUNDS;
             setAppBounds(delta.mAppBounds);
         }
+        if (delta.mWindowingMode != WINDOWING_MODE_UNDEFINED
+                && mWindowingMode != delta.mWindowingMode) {
+            changed |= WINDOW_CONFIG_WINDOWING_MODE;
+            setWindowingMode(delta.mWindowingMode);
+        }
         return changed;
     }
 
@@ -174,6 +219,11 @@
             changes |= WINDOW_CONFIG_APP_BOUNDS;
         }
 
+        if ((compareUndefined || other.mWindowingMode != WINDOWING_MODE_UNDEFINED)
+                && mWindowingMode != other.mWindowingMode) {
+            changes |= WINDOW_CONFIG_WINDOWING_MODE;
+        }
+
         return changes;
     }
 
@@ -194,6 +244,8 @@
             n = mAppBounds.bottom - that.mAppBounds.bottom;
             if (n != 0) return n;
         }
+        n = mWindowingMode - that.mWindowingMode;
+        if (n != 0) return n;
 
         // if (n != 0) return n;
         return n;
@@ -215,11 +267,24 @@
         if (mAppBounds != null) {
             result = 31 * result + mAppBounds.hashCode();
         }
+        result = 31 * result + mWindowingMode;
         return result;
     }
 
     @Override
     public String toString() {
-        return "{mAppBounds=" + mAppBounds + "}";
+        return "{mAppBounds=" + mAppBounds
+                + " mWindowingMode=" + windowingModeToString(mWindowingMode) + "}";
+    }
+
+    private static String windowingModeToString(@WindowingMode int windowingMode) {
+        switch (windowingMode) {
+            case WINDOWING_MODE_UNDEFINED: return "undefined";
+            case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
+            case WINDOWING_MODE_PINNED: return "pinned";
+            case WINDOWING_MODE_DOCKED: return "docked";
+            case WINDOWING_MODE_FREEFORM: return "freeform";
+        }
+        return String.valueOf(windowingMode);
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e9f3608..923cf0e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -460,14 +460,18 @@
         mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
                 ? new LaunchingTaskPositioner() : null;
         mTmpRect2.setEmpty();
+        final Configuration overrideConfig = getOverrideConfiguration();
         mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
-                mTmpRect2);
+                mTmpRect2, overrideConfig);
+        onOverrideConfigurationChanged(overrideConfig);
         mStackSupervisor.mStacks.put(mStackId, this);
         postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
     }
 
-    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+    T createStackWindowController(int displayId, boolean onTop, Rect outBounds,
+            Configuration outOverrideConfig) {
+        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
+                outOverrideConfig);
     }
 
     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 392fbb2..c825b314 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -39,8 +39,9 @@
 
     @Override
     PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
-            Rect outBounds) {
-        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+            Rect outBounds, Configuration outOverrideConfig) {
+        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
+                outOverrideConfig);
     }
 
     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 64bfbd0..5752982 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 
 /**
@@ -109,6 +110,12 @@
         }
     }
 
+    /** Sets the windowing mode for the configuration container. */
+    void setWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
+        mOverrideConfiguration.windowConfiguration.setWindowingMode(windowingMode);
+        onOverrideConfigurationChanged(mOverrideConfiguration);
+    }
+
     /**
      * Must be called when new parent for the container was set.
      */
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2e7f822..c295241 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -20,9 +20,12 @@
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Surface.ROTATION_0;
@@ -109,6 +112,7 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
+import android.app.WindowConfiguration;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -1191,8 +1195,10 @@
 
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
-        config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
-                Configuration.ORIENTATION_LANDSCAPE;
+        config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+        // TODO: Probably best to set this based on some setting in the display content object,
+        // so the display can be configured for things like fullscreen.
+        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
         config.screenWidthDp =
                 (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation,
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 989e8f2..3f81b38 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -24,6 +24,7 @@
 import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
 
 import android.app.RemoteAction;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 
 import com.android.server.UiThread;
@@ -39,8 +40,9 @@
     private Rect mTmpToBounds = new Rect();
 
     public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds) {
-        super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+            int displayId, boolean onTop, Rect outBounds, Configuration outOverrideConfig) {
+        super(stackId, listener, displayId, onTop, outBounds, outOverrideConfig,
+                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 358b8be..189689b 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -57,13 +57,15 @@
     private final Rect mTmpDisplayBounds = new Rect();
 
     public StackWindowController(int stackId, StackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds) {
-        this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+            int displayId, boolean onTop, Rect outBounds, Configuration outOverriderConfig) {
+        this(stackId, listener, displayId, onTop, outBounds, outOverriderConfig,
+                WindowManagerService.getInstance());
     }
 
     @VisibleForTesting
     public StackWindowController(int stackId, StackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
+            int displayId, boolean onTop, Rect outBounds, Configuration outOverrideConfig,
+            WindowManagerService service) {
         super(listener, service);
         mStackId = stackId;
         mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
@@ -78,6 +80,7 @@
             final TaskStack stack = dc.addStackToDisplay(stackId, onTop);
             stack.setController(this);
             getRawBounds(outBounds);
+            outOverrideConfig.setTo(mContainer.getOverrideConfiguration());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 474edaa..6dbdcc4 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,9 +18,18 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_DOCKED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -43,6 +52,7 @@
 import static com.android.server.wm.proto.StackProto.TASKS;
 
 import android.app.ActivityManager.StackId;
+import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -153,6 +163,32 @@
         mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_minimize_thickness);
         EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
+
+        // TODO: Remove once we are no longer using Stacks for windowing mode or grouping tasks.
+        final int windowingMode;
+        switch (stackId) {
+            case FULLSCREEN_WORKSPACE_STACK_ID:
+            case HOME_STACK_ID:
+            case RECENTS_STACK_ID:
+            case ASSISTANT_STACK_ID:
+                windowingMode = WINDOWING_MODE_FULLSCREEN;
+                break;
+            case PINNED_STACK_ID:
+                windowingMode = WINDOWING_MODE_PINNED;
+                break;
+            case DOCKED_STACK_ID:
+                windowingMode = WINDOWING_MODE_DOCKED;
+                break;
+            case FREEFORM_WORKSPACE_STACK_ID:
+                windowingMode = WINDOWING_MODE_FREEFORM;
+                break;
+            default :
+                windowingMode = WINDOWING_MODE_UNDEFINED;
+        }
+
+        if (windowingMode != WINDOWING_MODE_UNDEFINED) {
+            setWindowingMode(windowingMode);
+        }
     }
 
     DisplayContent getDisplayContent() {
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 4ad92c7..f64de00 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -271,8 +271,8 @@
         }
 
         @Override
-        protected T createStackWindowController(int displayId, boolean onTop,
-                Rect outBounds) {
+        protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds,
+                Configuration outOverrideConfig) {
             mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController();
             return mContainerController;
         }
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 5feda41..efe7667 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 
+import android.content.res.Configuration;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.Before;
@@ -51,7 +52,8 @@
     public void setUp() throws Exception {
         super.setUp();
         mPinnedStack = new StackWindowController(PINNED_STACK_ID, null,
-                mDisplayContent.getDisplayId(), true /* onTop */, new Rect(), sWm).mContainer;
+                mDisplayContent.getDisplayId(), true /* onTop */, new Rect(), new Configuration(),
+                sWm).mContainer;
 
         // 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/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index 78643bd..bd0e0df 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -27,7 +27,10 @@
 import android.view.DisplayInfo;
 
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
+import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
 import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -71,6 +74,10 @@
         assertEquals(WINDOW_CONFIG_APP_BOUNDS,
                 winConfig1.diff(winConfig2, false /* compareUndefined */));
 
+        winConfig2.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE,
+                winConfig1.diff(winConfig2, false /* compareUndefined */));
+
         assertEquals(0, config1.diff(config3));
         assertEquals(0, config1.diffPublicOnly(config3));
         assertEquals(0, winConfig1.diff(winConfig3, false /* compareUndefined */));
@@ -92,9 +99,14 @@
         assertEquals(config1.compareTo(config2), 0);
         assertEquals(winConfig1.compareTo(winConfig2), 0);
 
+        // Different windowing mode
+        winConfig2.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertNotEquals(config1.compareTo(config2), 0);
+        assertNotEquals(winConfig1.compareTo(winConfig2), 0);
+        winConfig2.setWindowingMode(winConfig1.getWindowingMode());
+
         // Different bounds
         winConfig2.setAppBounds(0, 2, 3, 4);
-
         assertNotEquals(config1.compareTo(config2), 0);
         assertNotEquals(winConfig1.compareTo(winConfig2), 0);
 
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 6618a69..eb8cf91 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.View.VISIBLE;
 
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
 import android.view.Display;
@@ -238,7 +239,7 @@
     StackWindowController createStackControllerOnStackOnDisplay(int stackId,
             DisplayContent dc) {
         return new StackWindowController(stackId, null, dc.getDisplayId(),
-                true /* onTop */, new Rect(), sWm);
+                true /* onTop */, new Rect(), new Configuration(), sWm);
     }
 
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */