Support for specifying orientation in WindowContainer

Also,
- Fixed failing tests when they are ran as a package vs.
individual classes due to multiple window manager instance.
- Correct some isVisible logic to so a window container that
is invisible is not said to be visible, because one of its
children is visible.

Bug: 30060889
Change-Id: I1bb8730361be2a9f5ce88cd59b7d57d5a459bde6
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index af68137..305e47f 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -146,7 +146,7 @@
 
         this.mSkipFirstFrame = skipFirstFrame;
 
-        if (!mAppToken.appFullscreen) {
+        if (!mAppToken.fillsParent()) {
             anim.setBackgroundColor(0);
         }
         if (mClearProlongedAnimation) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d7e7f81..e176c44 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.StackId;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -40,7 +41,6 @@
 import com.android.server.wm.WindowManagerService.H;
 
 import android.annotation.NonNull;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -75,9 +75,8 @@
     final boolean voiceInteraction;
 
     Task mTask;
-    // TODO: Have a fillParent variable in WindowContainer to this?
-    boolean appFullscreen;
-    int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    /** @see WindowContainer#fillsParent() */
+    private boolean mFillsParent;
     boolean layoutConfigChanges;
     boolean showForAllUsers;
     int targetSdk;
@@ -985,6 +984,18 @@
         }
     }
 
+    /**
+     * We override because this class doesn't want its children affecting its reported orientation
+     * in anyway.
+     */
+    @Override
+    int getOrientation() {
+        if (hidden || hiddenRequested) {
+            return SCREEN_ORIENTATION_UNSET;
+        }
+        return mOrientation;
+    }
+
     @Override
     AppWindowToken asAppWindowToken() {
         // I am an app window token!
@@ -992,14 +1003,23 @@
     }
 
     @Override
+    boolean fillsParent() {
+        return mFillsParent;
+    }
+
+    void setFillsParent(boolean fillsParent) {
+        mFillsParent = fillsParent;
+    }
+
+    @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
         if (appToken != null) {
             pw.print(prefix); pw.print("app=true voiceInteraction="); pw.println(voiceInteraction);
         }
         pw.print(prefix); pw.print("task="); pw.println(mTask);
-        pw.print(prefix); pw.print(" appFullscreen="); pw.print(appFullscreen);
-                pw.print(" requestedOrientation="); pw.println(requestedOrientation);
+        pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
+                pw.print(" mOrientation="); pw.println(mOrientation);
         pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
                 pw.print(" clientHidden="); pw.print(clientHidden);
                 pw.print(" reportedDrawn="); pw.print(reportedDrawn);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3a3c017..69d01ed 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -17,13 +17,18 @@
 package com.android.server.wm;
 
 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.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+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.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
 
@@ -201,6 +206,50 @@
         return null;
     }
 
+    int getOrientation() {
+        // TODO: Most of the logic here can be removed once this class is converted to use
+        // WindowContainer which has an abstract implementation of getOrientation that
+        // should cover this.
+        if (mService.isStackVisibleLocked(DOCKED_STACK_ID)
+                || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+            // Apps and their containers are not allowed to specify an orientation while the docked
+            // or freeform stack is visible...except for the home stack/task if the docked stack is
+            // minimized and it actually set something.
+            if (mHomeStack.isVisible() && mDividerControllerLocked.isMinimizedDock()) {
+                final int orientation = mHomeStack.getOrientation();
+                if (orientation != SCREEN_ORIENTATION_UNSET) {
+                    return orientation;
+                }
+            }
+            return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            if (!stack.isVisible()) {
+                continue;
+            }
+
+            final int orientation = stack.getOrientation();
+
+            if (orientation == SCREEN_ORIENTATION_BEHIND) {
+                continue;
+            }
+
+            if (orientation != SCREEN_ORIENTATION_UNSET) {
+                if (stack.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                    return orientation;
+                }
+            }
+        }
+
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                "No app is requesting an orientation, return " + mService.mLastOrientation);
+        // The next app has not been requested to be visible, so we keep the current orientation
+        // to prevent freezing/unfreezing the display too early.
+        return mService.mLastOrientation;
+    }
+
     void updateDisplayInfo() {
         mDisplay.getDisplayInfo(mDisplayInfo);
         mDisplay.getMetrics(mDisplayMetrics);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 545a439..f2f2474 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -21,6 +21,9 @@
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+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 com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -681,6 +684,33 @@
         return false;
     }
 
+    boolean fillsParent() {
+        return mFullscreen || !StackId.isTaskResizeAllowed(mStack.mStackId);
+    }
+
+    // TODO: Remove once switched to use WindowContainer
+    int getOrientation() {
+        int candidate = SCREEN_ORIENTATION_UNSET;
+
+        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
+            final AppWindowToken token = mAppTokens.get(i);
+            final int orientation = token.getOrientation();
+
+            if (orientation == SCREEN_ORIENTATION_BEHIND) {
+                candidate = orientation;
+                continue;
+            }
+
+            if (orientation != SCREEN_ORIENTATION_UNSET) {
+                if (token.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                    return orientation;
+                }
+            }
+        }
+
+        return candidate;
+    }
+
     @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index cf31310..f5fa9fd 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -21,6 +21,9 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+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.DENSITY_DPI_UNDEFINED;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.DOCKED_BOTTOM;
@@ -1305,4 +1308,40 @@
             mTasks.get(i).overridePlayingAppAnimations(a);
         }
     }
+
+    // TODO: Remove once switched to use WindowContainer
+    int getOrientation() {
+        if (!StackId.canSpecifyOrientation(mStackId)) {
+            return SCREEN_ORIENTATION_UNSET;
+        }
+
+        int candidate = SCREEN_ORIENTATION_UNSET;
+
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final Task task = mTasks.get(i);
+
+            if (!task.isVisible()) {
+                continue;
+            }
+
+            final int orientation = task.getOrientation();
+            if (orientation == SCREEN_ORIENTATION_BEHIND) {
+                candidate = orientation;
+                continue;
+            }
+
+            if (orientation != SCREEN_ORIENTATION_UNSET) {
+                if (task.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                    return orientation;
+                }
+            }
+        }
+
+        return candidate;
+    }
+
+    // TODO: Remove once switched to use WindowContainer
+    boolean fillsParent() {
+        return mFullscreen;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fa05c0c..0ed3a33 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -21,9 +21,13 @@
 import java.util.Comparator;
 import java.util.LinkedList;
 
+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;
+
 /**
  * Defines common functionality for classes that can hold windows directly or through their
- * children.
+ * children in a hierarchy form.
  * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
  * changes are made to this class.
  */
@@ -36,7 +40,10 @@
     // screen with the top-most window container at the tail of the list.
     protected final LinkedList<WindowContainer> mChildren = new LinkedList();
 
-    protected WindowContainer getParent() {
+    // The specified orientation for this window container.
+    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+    final protected WindowContainer getParent() {
         return mParent;
     }
 
@@ -221,6 +228,10 @@
      * the container has any content to display.
      */
     boolean isVisible() {
+        // TODO: Will this be more correct if it checks the visibility of its parents? Yes.
+        // That is this container is only visible if its parents are visible vs visible if it has a
+        // visible child. In that case all overrides will need to call super and return false if
+        // this returns false.
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
             if (wc.isVisible()) {
@@ -234,4 +245,70 @@
     WindowContainer getTop() {
         return mChildren.isEmpty() ? this : mChildren.peekLast();
     }
+
+    void setOrientation(int orientation) {
+        mOrientation = orientation;
+    }
+
+    /**
+     * Returns the specified orientation for this window container or one of its children is there
+     * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
+     * specification is set.
+     * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
+     * specification...
+     */
+    int getOrientation() {
+
+        if (!fillsParent() || !isVisible()) {
+            // Ignore invisible containers or containers that don't completely fills their parents.
+            return SCREEN_ORIENTATION_UNSET;
+        }
+
+        // The container fills its parent so we can use it orientation if it has one specified,
+        // otherwise we prefer to use the orientation of its topmost child that has one
+        // specified and fall back on this container's unset or unspecified value as a candidate
+        // if none of the children have a better candidate for the orientation.
+        if (mOrientation != SCREEN_ORIENTATION_UNSET
+                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+            return mOrientation;
+        }
+        int candidate = mOrientation;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+
+            final int orientation = wc.getOrientation();
+            if (orientation == SCREEN_ORIENTATION_BEHIND) {
+                // container wants us to use the orientation of the container behind it. See if we
+                // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
+                // look behind this container.
+                candidate = orientation;
+                continue;
+            }
+
+            if (orientation == SCREEN_ORIENTATION_UNSET) {
+                continue;
+            }
+
+            if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                // Use the orientation if the container fills its parent or requested an explicit
+                // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
+                return orientation;
+            }
+        }
+
+        return candidate;
+    }
+
+    /**
+     * Returns true if this container is opaque and fills all the space made available by its parent
+     * container.
+     *
+     * NOTE: It is possible for this container to occupy more space than the parent has (or less),
+     * this is just a signal from the client to window manager stating its intent, but not what it
+     * actually does.
+     */
+    boolean fillsParent() {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1cc7dad..5e279fe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2839,10 +2839,10 @@
             }
             atoken = new AppWindowToken(this, token, voiceInteraction);
             atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
-            atoken.appFullscreen = fullscreen;
+            atoken.setFillsParent(fullscreen);
             atoken.showForAllUsers = showForAllUsers;
             atoken.targetSdk = targetSdkVersion;
-            atoken.requestedOrientation = requestedOrientation;
+            atoken.setOrientation(requestedOrientation);
             atoken.layoutConfigChanges = (configChanges &
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
             atoken.mLaunchTaskBehind = launchTaskBehind;
@@ -2947,7 +2947,7 @@
                 AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
                         null : winShowWhenLocked.mAppToken;
                 if (appShowWhenLocked != null) {
-                    int req = appShowWhenLocked.requestedOrientation;
+                    int req = appShowWhenLocked.getOrientation();
                     if (req == SCREEN_ORIENTATION_BEHIND) {
                         req = mLastKeyguardForcedOrientation;
                     }
@@ -2962,91 +2962,7 @@
         }
 
         // Top system windows are not requesting an orientation. Start searching from apps.
-        return getAppSpecifiedOrientation();
-    }
-
-    private int getAppSpecifiedOrientation() {
-        int lastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-        boolean findingBehind = false;
-        boolean lastFullscreen = false;
-        DisplayContent displayContent = getDefaultDisplayContentLocked();
-        final ArrayList<Task> tasks = displayContent.getTasks();
-        final boolean inMultiWindow = isStackVisibleLocked(DOCKED_STACK_ID)
-                || isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID);
-        final boolean dockMinimized =
-                getDefaultDisplayContentLocked().mDividerControllerLocked.isMinimizedDock();
-        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-            final int firstToken = tokens.size() - 1;
-            for (int tokenNdx = firstToken; tokenNdx >= 0; --tokenNdx) {
-                final AppWindowToken atoken = tokens.get(tokenNdx);
-
-                if (DEBUG_APP_ORIENTATION) Slog.v(TAG_WM, "Checking app orientation: " + atoken);
-
-                // if we're about to tear down this window and not seek for
-                // the behind activity, don't use it for orientation
-                if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Skipping " + atoken + " -- going to hide");
-                    continue;
-                }
-
-                if (tokenNdx == firstToken) {
-                    // If we have hit a new Task, and the bottom of the previous group didn't
-                    // explicitly say to use the orientation behind it, and the last app was
-                    // full screen, then we'll stick with the user's orientation.
-                    if (lastOrientation != SCREEN_ORIENTATION_BEHIND && lastFullscreen) {
-                        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + atoken
-                                + " -- end of group, return " + lastOrientation);
-                        return lastOrientation;
-                    }
-                }
-
-                // We ignore any hidden applications on the top.
-                if (atoken.hiddenRequested) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Skipping " + atoken + " -- hidden on top");
-                    continue;
-                }
-
-                // No app except the home app may specify the screen orientation in multi-window,
-                // and only if the docked stack is minimized to avoid weirdness when home task
-                // temporarily gets moved to the front.
-                if (inMultiWindow && (!atoken.mTask.isHomeTask() || !dockMinimized)) {
-                    continue;
-                }
-
-                if (tokenNdx == 0) {
-                    // Last token in this task.
-                    lastOrientation = atoken.requestedOrientation;
-                }
-
-                int or = atoken.requestedOrientation;
-                // If this application is fullscreen, and didn't explicitly say
-                // to use the orientation behind it, then just take whatever
-                // orientation it has and ignores whatever is under it.
-                lastFullscreen = atoken.appFullscreen;
-                if (lastFullscreen && or != SCREEN_ORIENTATION_BEHIND) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Done at " + atoken + " -- full screen, return " + or);
-                    return or;
-                }
-                // If this application has requested an explicit orientation, then use it.
-                if (or != SCREEN_ORIENTATION_UNSPECIFIED && or != SCREEN_ORIENTATION_BEHIND) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Done at " + atoken + " -- explicitly set, return " + or);
-                    return or;
-                }
-                findingBehind |= (or == SCREEN_ORIENTATION_BEHIND);
-            }
-        }
-        if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                "No app is requesting an orientation, return " + mLastOrientation);
-        // The next app has not been requested to be visible, so we keep the current orientation
-        // to prevent freezing/unfreezing the display too early unless we are in multi-window, in
-        // which we don't let the app customize the orientation unless it was the home task that
-        // is handled above.
-        return inMultiWindow ? SCREEN_ORIENTATION_UNSPECIFIED : mLastOrientation;
+        return getDefaultDisplayContentLocked().getOrientation();
     }
 
     @Override
@@ -3222,13 +3138,13 @@
         }
 
         synchronized(mWindowMap) {
-            AppWindowToken atoken = findAppWindowToken(token.asBinder());
+            final AppWindowToken atoken = findAppWindowToken(token.asBinder());
             if (atoken == null) {
                 Slog.w(TAG_WM, "Attempted to set orientation of non-existing app token: " + token);
                 return;
             }
 
-            atoken.requestedOrientation = requestedOrientation;
+            atoken.setOrientation(requestedOrientation);
         }
     }
 
@@ -3240,7 +3156,7 @@
                 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             }
 
-            return wtoken.requestedOrientation;
+            return wtoken.getOrientation();
         }
     }
 
@@ -3556,9 +3472,9 @@
 
     public void setAppFullscreen(IBinder token, boolean toOpaque) {
         synchronized (mWindowMap) {
-            AppWindowToken atoken = findAppWindowToken(token);
+            final AppWindowToken atoken = findAppWindowToken(token);
             if (atoken != null) {
-                atoken.appFullscreen = toOpaque;
+                atoken.setFillsParent(toOpaque);
                 setWindowOpaqueLocked(token, toOpaque);
                 mWindowPlacerLocked.requestTraversal();
             }
@@ -3963,7 +3879,6 @@
         mInputMonitor.setUpdateInputWindowsNeededLw();
         mWindowPlacerLocked.performSurfacePlacement();
         mInputMonitor.updateInputWindowsLw(false /*force*/);
-        //dump();
     }
 
     public void moveTaskToTop(int taskId) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fccd2d9..7b14c6b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1202,7 +1202,7 @@
             // has been hidden.
             return true;
         }
-        return super.isVisible();
+        return false;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index e03456f..a2d02a3 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1154,7 +1154,7 @@
 
             voiceInteraction |= wtoken.voiceInteraction;
 
-            if (wtoken.appFullscreen) {
+            if (wtoken.fillsParent()) {
                 WindowState ws = wtoken.findMainWindow();
                 if (ws != null) {
                     animLp = ws.mAttrs;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 32fd43a..740d30c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -46,17 +46,12 @@
 public class AppWindowTokenTests {
 
     private static WindowManagerService sWm = null;
-    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
     private final IWindow mIWindow = new TestIWindow();
 
     @Before
     public void setUp() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
-        if (sWm == null) {
-            // We only want to do this once for the test process as we don't want WM to try to
-            // register a bunch of local services again.
-            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
-        }
+        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 8c6b007..0afbd0c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -82,6 +82,18 @@
 public class TestWindowManagerPolicy implements WindowManagerPolicy {
     private static final String TAG = "TestWindowManagerPolicy";
 
+    private static WindowManagerService sWm = null;
+
+    static synchronized WindowManagerService getWindowManagerService(Context context) {
+        if (sWm == null) {
+            // We only want to do this once for the test process as we don't want WM to try to
+            // register a bunch of local services again.
+            sWm = WindowManagerService.main(
+                    context, null, true, false, false, new TestWindowManagerPolicy());
+        }
+        return sWm;
+    }
+
     @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
             throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index a15b74b..354aa34 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -23,8 +23,12 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import java.util.Comparator;
-import java.util.LinkedList;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -195,7 +199,7 @@
         final TestWindowContainer child12 = child1.addChildWindow(builder.setIsVisible(true));
         final TestWindowContainer child21 = child2.addChildWindow();
 
-        assertTrue(root.isVisible());
+        assertFalse(root.isVisible());
         assertTrue(child1.isVisible());
         assertFalse(child11.isVisible());
         assertTrue(child12.isVisible());
@@ -230,15 +234,105 @@
         assertTrue(gotException);
     }
 
+    @Test
+    public void testGetOrientation_Unset() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+        // Unspecified well because we didn't specify anything...
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
+    }
+
+    @Test
+    public void testGetOrientation_InvisibleParentUnsetVisibleChildren() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+
+        builder.setIsVisible(false).setLayer(-1);
+        final TestWindowContainer invisible = root.addChildWindow(builder);
+        builder.setIsVisible(true).setLayer(-2);
+        final TestWindowContainer invisibleChild1VisibleAndSet = invisible.addChildWindow(builder);
+        invisibleChild1VisibleAndSet.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        // Landscape well because the container is visible and that is what we set on it above.
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisibleChild1VisibleAndSet.getOrientation());
+        // Unset because the container isn't visible even though it has a child that thinks it is
+        // visible.
+        assertEquals(SCREEN_ORIENTATION_UNSET, invisible.getOrientation());
+        // Unspecified because we are visible and we didn't specify an orientation and there isn't
+        // a visible child.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
+
+        builder.setIsVisible(true).setLayer(-3);
+        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
+        visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
+        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
+
+    }
+
+    @Test
+    public void testGetOrientation_setBehind() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+
+        builder.setIsVisible(true).setLayer(-1);
+        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
+        visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
+
+        builder.setIsVisible(true).setLayer(-2);
+        final TestWindowContainer visibleUnsetChild1VisibleSetBehind =
+                visibleUnset.addChildWindow(builder);
+        visibleUnsetChild1VisibleSetBehind.setOrientation(SCREEN_ORIENTATION_BEHIND);
+        // Setting to visible behind will be used by the parents if there isn't another other
+        // container behind this one that has an orientation set.
+        assertEquals(SCREEN_ORIENTATION_BEHIND,
+                visibleUnsetChild1VisibleSetBehind.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_BEHIND, visibleUnset.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
+    }
+
+    @Test
+    public void testGetOrientation_fillsParent() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+
+        builder.setIsVisible(true).setLayer(-1);
+        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
+        visibleUnset.setOrientation(SCREEN_ORIENTATION_BEHIND);
+
+        builder.setLayer(1).setIsVisible(true);
+        final TestWindowContainer visibleUnspecifiedRootChild = root.addChildWindow(builder);
+        visibleUnspecifiedRootChild.setFillsParent(false);
+        visibleUnspecifiedRootChild.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+        // Unset because the child doesn't fill the parent. May as well be invisible...
+        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
+        // The parent uses whatever orientation is set behind this container since it doesn't fill
+        // the parent.
+        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
+
+        // Test case of child filling its parent, but its parent isn't filling its own parent.
+        builder.setLayer(2).setIsVisible(true);
+        final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent =
+                visibleUnspecifiedRootChild.addChildWindow(builder);
+        visibleUnspecifiedRootChildChildFillsParent.setOrientation(
+                SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT,
+                visibleUnspecifiedRootChildChildFillsParent.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
+
+
+        visibleUnspecifiedRootChild.setFillsParent(true);
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, visibleUnspecifiedRootChild.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation());
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private class TestWindowContainer extends WindowContainer {
         private final int mLayer;
-        private final LinkedList<String> mUsers = new LinkedList();
         private final boolean mCanDetach;
         private boolean mIsAnimating;
         private boolean mIsVisible;
-        private int mRemoveIfPossibleCount;
-        private int mRemoveImmediatelyCount;
+        private boolean mFillsParent;
 
         /**
          * Compares 2 window layers and returns -1 if the first is lesser than the second in terms
@@ -256,13 +350,12 @@
             return 1;
         };
 
-        TestWindowContainer(int layer, LinkedList<String> users, boolean canDetach,
-                boolean isAnimating, boolean isVisible) {
+        TestWindowContainer(int layer, boolean canDetach, boolean isAnimating, boolean isVisible) {
             mLayer = layer;
-            mUsers.addAll(users);
             mCanDetach = canDetach;
             mIsAnimating = isAnimating;
             mIsVisible = isVisible;
+            mFillsParent = true;
         }
 
         TestWindowContainer getParentWindow() {
@@ -299,36 +392,31 @@
 
         @Override
         boolean isVisible() {
-            return mIsVisible || super.isVisible();
+            return mIsVisible;
         }
 
         @Override
-        void removeImmediately() {
-            super.removeImmediately();
-            mRemoveImmediatelyCount++;
+        boolean fillsParent() {
+            return mFillsParent;
         }
 
-        @Override
-        void removeIfPossible() {
-            super.removeIfPossible();
-            mRemoveIfPossibleCount++;
+        void setFillsParent(boolean fillsParent) {
+            mFillsParent = fillsParent;
         }
     }
 
     private class TestWindowContainerBuilder {
         private int mLayer;
-        private LinkedList<String> mUsers = new LinkedList();
         private boolean mCanDetach;
         private boolean mIsAnimating;
         private boolean mIsVisible;
 
-        TestWindowContainerBuilder setLayer(int layer) {
-            mLayer = layer;
-            return this;
+        public TestWindowContainerBuilder() {
+            reset();
         }
 
-        TestWindowContainerBuilder addUser(String user) {
-            mUsers.add(user);
+        TestWindowContainerBuilder setLayer(int layer) {
+            mLayer = layer;
             return this;
         }
 
@@ -349,7 +437,6 @@
 
         TestWindowContainerBuilder reset() {
             mLayer = 0;
-            mUsers.clear();
             mCanDetach = false;
             mIsAnimating = false;
             mIsVisible = false;
@@ -357,7 +444,7 @@
         }
 
         TestWindowContainer build() {
-            return new TestWindowContainer(mLayer, mUsers, mCanDetach, mIsAnimating, mIsVisible);
+            return new TestWindowContainer(mLayer, mCanDetach, mIsAnimating, mIsVisible);
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 4b29a60..789c067 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -21,6 +21,7 @@
 import org.junit.runner.RunWith;
 
 import android.content.Context;
+import android.os.Binder;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -50,18 +51,13 @@
 
     private static WindowManagerService sWm = null;
     private WindowToken mWindowToken;
-    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
     private final IWindow mIWindow = new TestIWindow();
 
     @Before
     public void setUp() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
-        if (sWm == null) {
-            // We only want to do this once for the test process as we don't want WM to try to
-            // register a bunch of local services again.
-            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
-        }
-        mWindowToken = new WindowToken(sWm, null, 0, false);
+        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
+        mWindowToken = new WindowToken(sWm, new Binder(), 0, false);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 4505254..1a0ce5b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -46,18 +46,13 @@
 @RunWith(AndroidJUnit4.class)
 public class WindowTokenTests {
 
-    private static WindowManagerService sWm = null;
-    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
+    private WindowManagerService mWm = null;
     private final IWindow mIWindow = new TestIWindow();
 
     @Before
     public void setUp() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
-        if (sWm == null) {
-            // We only want to do this once for the test process as we don't want WM to try to
-            // register a bunch of local services again.
-            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
-        }
+        mWm = TestWindowManagerPolicy.getWindowManagerService(context);
     }
 
     @Test
@@ -161,15 +156,15 @@
     private WindowState createWindow(WindowState parent, int type, WindowToken token) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
 
-        return new WindowState(sWm, null, mIWindow, token, parent, 0, 0, attrs, 0,
-                sWm.getDefaultDisplayContentLocked(), 0);
+        return new WindowState(mWm, null, mIWindow, token, parent, 0, 0, attrs, 0,
+                mWm.getDefaultDisplayContentLocked(), 0);
     }
 
     /* Used so we can gain access to some protected members of the {@link WindowToken} class */
     private class TestWindowToken extends WindowToken {
 
         TestWindowToken() {
-            super(sWm, null, 0, false);
+            super(mWm, null, 0, false);
         }
 
         int getWindowsCount() {