Move orientation request into hierarchy.

This CL also removes a hidden method in IWindowManager that's only used
inside of WM right now.

Also wire up Task level logic.

Bug: 113252523
Test: Manual test that app requested orientation still rotates the
display and WmTests.
Change-Id: I7c5046e9beaea79de4ec471eabc792e3593583a2
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4d8440a8..2cd0168 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2486,36 +2486,20 @@
     }
 
     void setRequestedOrientation(int requestedOrientation) {
-        final int displayId = getDisplayId();
-        final Configuration displayConfig =
-                mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
-
-        final Configuration config = setOrientation(requestedOrientation,
-                displayId, displayConfig, mayFreezeScreenLocked(app));
-        if (config != null) {
-            frozenBeforeDestroy = true;
-            if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this,
-                    false /* deferResume */, displayId)) {
-                mRootActivityContainer.resumeFocusedStacksTopActivities();
-            }
-        }
+        setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
         mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                 task.taskId, requestedOrientation);
     }
 
-    Configuration setOrientation(int requestedOrientation, int displayId,
-            Configuration displayConfig, boolean freezeScreenIfNeeded) {
+    private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
         if (mAppWindowToken == null) {
             Slog.w(TAG_WM,
                     "Attempted to set orientation of non-existing app token: " + appToken);
-            return null;
+            return;
         }
 
-        mAppWindowToken.setOrientation(requestedOrientation);
-
         final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
-        return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
-                displayId);
+        mAppWindowToken.setOrientation(requestedOrientation, binder, this);
     }
 
     int getOrientation() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1943efc..a5ceee2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -397,10 +397,11 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final Region mTmpRegion = new Region();
 
-
     /** Used for handing back size of display */
     private final Rect mTmpBounds = new Rect();
 
+    private final Configuration mTmpConfiguration = new Configuration();
+
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
@@ -1156,6 +1157,36 @@
         mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
     }
 
+    @Override
+    boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+            ConfigurationContainer requestingContainer) {
+        final Configuration config = updateOrientationFromAppTokens(
+                getRequestedOverrideConfiguration(), freezeDisplayToken, false);
+        // If display rotation class tells us that it doesn't consider app requested orientation,
+        // this display won't rotate just because of an app changes its requested orientation. Thus
+        // it indicates that this display chooses not to handle this request.
+        final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+        if (config == null) {
+            return handled;
+        }
+
+        if (handled && requestingContainer instanceof ActivityRecord) {
+            final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
+            final boolean kept = mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+                    config, activityRecord, false /* deferResume */, getDisplayId());
+            activityRecord.frozenBeforeDestroy = true;
+            if (!kept) {
+                mWmService.mAtmService.mRootActivityContainer.resumeFocusedStacksTopActivities();
+            }
+        } else {
+            // We have a new configuration to push so we need to update ATMS for now.
+            // TODO: Clean up display configuration push between ATMS and WMS after unification.
+            mWmService.mAtmService.updateDisplayOverrideConfigurationLocked(
+                    config, null /* starting */, false /* deferResume */, getDisplayId());
+        }
+        return handled;
+    }
+
     /**
      * Determine the new desired orientation of this display.
      *
@@ -1169,7 +1200,56 @@
         return updateOrientationFromAppTokens(false /* forceUpdate */);
     }
 
-    boolean updateOrientationFromAppTokens(boolean forceUpdate) {
+    /**
+     * Update orientation of the target display, returning a non-null new Configuration if it has
+     * changed from the current orientation. If a non-null configuration is returned, someone must
+     * call {@link WindowManagerService#setNewDisplayOverrideConfiguration(Configuration,
+     * DisplayContent)} to tell the window manager it can unfreeze the screen. This will typically
+     * be done by calling {@link WindowManagerService#sendNewConfiguration(int)}.
+     */
+    Configuration updateOrientationFromAppTokens(Configuration currentConfig,
+            IBinder freezeDisplayToken, boolean forceUpdate) {
+        if (!mDisplayReady) {
+            return null;
+        }
+
+        Configuration config = null;
+        if (updateOrientationFromAppTokens(forceUpdate)) {
+            // If we changed the orientation but mOrientationChangeComplete is already true,
+            // we used seamless rotation, and we don't need to freeze the screen.
+            if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) {
+                final AppWindowToken atoken = getAppWindowToken(freezeDisplayToken);
+                if (atoken != null) {
+                    atoken.startFreezingScreen();
+                }
+            }
+            config = new Configuration();
+            computeScreenConfiguration(config);
+        } else if (currentConfig != null) {
+            // No obvious action we need to take, but if our current state mismatches the
+            // activity manager's, update it, disregarding font scale, which should remain set
+            // to the value of the previous configuration.
+            // Here we're calling Configuration#unset() instead of setToDefaults() because we
+            // need to keep override configs clear of non-empty values (e.g. fontSize).
+            mTmpConfiguration.unset();
+            mTmpConfiguration.updateFrom(currentConfig);
+            computeScreenConfiguration(mTmpConfiguration);
+            if (currentConfig.diff(mTmpConfiguration) != 0) {
+                mWaitingForConfig = true;
+                setLayoutNeeded();
+                int[] anim = new int[2];
+                getDisplayPolicy().selectRotationAnimationLw(anim);
+
+                mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
+                config = new Configuration(mTmpConfiguration);
+            }
+        }
+
+        return config;
+    }
+
+
+    private boolean updateOrientationFromAppTokens(boolean forceUpdate) {
         final int req = getOrientation();
         if (req != mLastOrientation || forceUpdate) {
             mLastOrientation = req;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 7aabc15..bcc7be4 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -329,6 +329,15 @@
         return mFixedToUserRotation;
     }
 
+    /**
+     * Returns {@code true} if this display rotation takes app requested orientation into
+     * consideration; {@code false} otherwise. For the time being the only case where this is {@code
+     * false} is when {@link #isFixedToUserRotation()} is {@code true}.
+     */
+    boolean respectAppRequestedOrientation() {
+        return !mFixedToUserRotation;
+    }
+
     public int getLandscapeRotation() {
         return mLandscapeRotation;
     }
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 8ec97c5..325f95f 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -597,11 +597,15 @@
 
         // Force-update the orientation from the WindowManager, since we need the true configuration
         // to send to the client now.
-        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                getDisplayOverrideConfiguration(displayId),
-                starting != null && starting.mayFreezeScreenLocked(starting.app)
-                        ? starting.appToken : null,
-                displayId, true /* forceUpdate */);
+        final DisplayContent displayContent = mRootWindowContainer.getDisplayContent(displayId);
+        Configuration config = null;
+        if (displayContent != null) {
+            config = displayContent.updateOrientationFromAppTokens(
+                    getDisplayOverrideConfiguration(displayId),
+                    starting != null && starting.mayFreezeScreenLocked(starting.app)
+                            ? starting.appToken : null,
+                    true /* forceUpdate */);
+        }
         if (starting != null && markFrozenIfConfigChanged && config != null) {
             starting.frozenBeforeDestroy = true;
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b10fd31..d334bd2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -44,6 +44,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -328,6 +329,23 @@
         return boundsChange;
     }
 
+    @Override
+    public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
+            ConfigurationContainer requestingContainer) {
+        if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
+            return true;
+        }
+
+        // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill
+        // it if possible.
+        // TODO: Move to TaskRecord after unification is done.
+        if (mTaskRecord != null) {
+            mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration());
+            return true;
+        }
+        return false;
+    }
+
     void resize(boolean relayout, boolean forced) {
         if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
             getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 651089d..32c5a3b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -31,10 +31,12 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.Pools;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -698,8 +700,58 @@
         }
     }
 
+    /**
+     * Called when this container or one of its descendants changed its requested orientation, and
+     * wants this container to handle it or pass it to its parent.
+     *
+     * @param freezeDisplayToken freeze this app window token if display needs to freeze
+     * @param requestingContainer the container which orientation request has changed
+     * @return {@code true} if handled; {@code false} otherwise.
+     */
+    boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
+            @Nullable ConfigurationContainer requestingContainer) {
+        final WindowContainer parent = getParent();
+        if (parent == null) {
+            return false;
+        }
+        return parent.onDescendantOrientationChanged(freezeDisplayToken,
+                requestingContainer);
+    }
+
+    /**
+     * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
+     * parameters.
+     *
+     * @param orientation the specified orientation.
+     */
     void setOrientation(int orientation) {
+        setOrientation(orientation, null /* freezeDisplayToken */,
+                null /* ActivityRecord */);
+    }
+
+    /**
+     * Sets the specified orientation of this container. It percolates this change upward along the
+     * hierarchy to let each level of the hierarchy a chance to respond to it.
+     *
+     * @param orientation the specified orientation. Needs to be one of {@link
+     *      android.content.pm.ActivityInfo.ScreenOrientation}.
+     * @param freezeDisplayToken uses this token to freeze display if orientation change is not
+     *                           done. Display will not be frozen if this is {@code null}, which
+     *                           should only happen in tests.
+     * @param requestingContainer the container which orientation request has changed. Mostly used
+     *                            to ensure it gets correct configuration.
+     */
+    void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
+            @Nullable ConfigurationContainer requestingContainer) {
+        final boolean changed = mOrientation != orientation;
         mOrientation = orientation;
+        if (!changed) {
+            return;
+        }
+        final WindowContainer parent = getParent();
+        if (parent != null) {
+            onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+        }
     }
 
     int getOrientation() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e3ced83..b6a4a51 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2383,85 +2383,6 @@
         }
     }
 
-    @Override
-    public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId) {
-        return updateOrientationFromAppTokens(currentConfig, freezeThisOneIfNeeded, displayId,
-                false /* forceUpdate */);
-    }
-
-    public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
-        if (!checkCallingPermission(MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) {
-            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
-        }
-
-        final Configuration config;
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
-                        displayId, forceUpdate);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-
-        return config;
-    }
-
-    /**
-     * Update orientation of the target display, returning a non-null new Configuration if it has
-     * changed from the current orientation. If a non-null configuration is returned, someone must
-     * call {@link #setNewDisplayOverrideConfiguration(Configuration, int)} to tell the window
-     * manager it can unfreeze the screen. This will typically be done by calling
-     * {@link #sendNewConfiguration(int)}.
-     *
-     * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
-     */
-    private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
-            IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
-        if (!mDisplayReady) {
-            return null;
-        }
-        Configuration config = null;
-
-        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-        if (dc != null && dc.updateOrientationFromAppTokens(forceUpdate)) {
-            // If we changed the orientation but mOrientationChangeComplete is already true,
-            // we used seamless rotation, and we don't need to freeze the screen.
-            if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
-                final AppWindowToken atoken = mRoot.getAppWindowToken(freezeThisOneIfNeeded);
-                if (atoken != null) {
-                    atoken.startFreezingScreen();
-                }
-            }
-            config = computeNewConfigurationLocked(displayId);
-
-        } else if (currentConfig != null) {
-            // No obvious action we need to take, but if our current state mismatches the activity
-            // manager's, update it, disregarding font scale, which should remain set to the value
-            // of the previous configuration.
-            // Here we're calling Configuration#unset() instead of setToDefaults() because we need
-            // to keep override configs clear of non-empty values (e.g. fontSize).
-            mTempConfiguration.unset();
-            mTempConfiguration.updateFrom(currentConfig);
-            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            displayContent.computeScreenConfiguration(mTempConfiguration);
-            if (currentConfig.diff(mTempConfiguration) != 0) {
-                displayContent.mWaitingForConfig = true;
-                displayContent.setLayoutNeeded();
-                int anim[] = new int[2];
-                displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
-
-                startFreezingDisplayLocked(anim[0], anim[1], displayContent);
-                config = new Configuration(mTempConfiguration);
-            }
-        }
-
-        return config;
-    }
-
     void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
             @NonNull DisplayContent dc) {
         if (dc.mWaitingForConfig) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 92b4dbb..bc62de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -155,15 +155,17 @@
 
         // Set initial orientation and update.
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
-                null, mDisplayContent.getDisplayId());
+        mDisplayContent.updateOrientationFromAppTokens(
+                mDisplayContent.getRequestedOverrideConfiguration(),
+                null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.mResizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
         mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
-        mWm.updateOrientationFromAppTokens(mDisplayContent.getRequestedOverrideConfiguration(),
-                null, mDisplayContent.getDisplayId());
+        mDisplayContent.updateOrientationFromAppTokens(
+                mDisplayContent.getRequestedOverrideConfiguration(),
+                null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
         mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
         assertTrue(appWindow.mResizeReported);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 8430616..3826fac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -31,7 +31,13 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -60,9 +66,11 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -475,6 +483,13 @@
     @SuppressLint("InlinedApi")
     public void testOrientationDefinedByKeyguard() {
         final DisplayContent dc = createNewDisplay();
+
+        // When display content is created its configuration is not yet initialized, which could
+        // cause unnecessary configuration propagation, so initialize it here.
+        final Configuration config = new Configuration();
+        dc.computeScreenConfiguration(config);
+        dc.onRequestedOverrideConfigurationChanged(config);
+
         // Create a window that requests landscape orientation. It will define device orientation
         // by default.
         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
@@ -567,6 +582,52 @@
         assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
     }
 
+    @Test
+    public void testOnDescendantOrientationRequestChanged() {
+        final DisplayContent dc = createNewDisplay();
+        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+                ? SCREEN_ORIENTATION_PORTRAIT
+                : SCREEN_ORIENTATION_LANDSCAPE;
+
+        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+        window.mAppToken.setOrientation(newOrientation);
+
+        ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+        assertTrue("Display should rotate to handle orientation request by default.",
+                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+
+        final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
+        verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
+                same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+        final Configuration newDisplayConfig = captor.getValue();
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+    }
+
+    @Test
+    public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
+        final DisplayContent dc = createNewDisplay();
+        dc.getDisplayRotation().setFixedToUserRotation(true);
+        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
+                ? SCREEN_ORIENTATION_PORTRAIT
+                : SCREEN_ORIENTATION_LANDSCAPE;
+
+        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
+        window.mAppToken.setOrientation(newOrientation);
+
+        ActivityRecord activityRecord = mock(ActivityRecord.class);
+
+        assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
+                        + " user rotation.",
+                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+        verify(mWm.mAtmService, never()).updateDisplayOverrideConfigurationLocked(any(),
+                eq(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index bf4b52e..6b31e6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -33,6 +33,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.ContentResolver;
@@ -234,7 +235,7 @@
     }
 
     @Test
-    public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+    public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()
             throws Exception {
         mBuilder.build();
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
@@ -604,6 +605,26 @@
                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
     }
 
+    // ========================
+    // Non-rotation API Tests
+    // ========================
+    @Test
+    public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+        mBuilder.build();
+
+        assertTrue("Display rotation should respect app requested orientation by"
+                + " default.", mTarget.respectAppRequestedOrientation());
+    }
+
+    @Test
+    public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+        mBuilder.build();
+        mTarget.setFixedToUserRotation(true);
+
+        assertFalse("Display rotation shouldn't respect app requested orientation if"
+                + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+    }
+
     /**
      * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
      * according to given parameters.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 60f957f..e156143 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -38,8 +39,10 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -559,8 +562,7 @@
         builder.setLayer(2).setIsVisible(true);
         final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent =
                 visibleUnspecifiedRootChild.addChildWindow(builder);
-        visibleUnspecifiedRootChildChildFillsParent.setOrientation(
-                SCREEN_ORIENTATION_PORTRAIT);
+        visibleUnspecifiedRootChildChildFillsParent.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(SCREEN_ORIENTATION_PORTRAIT,
                 visibleUnspecifiedRootChildChildFillsParent.getOrientation());
         assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
@@ -724,6 +726,19 @@
         verify(grandChild, times(1)).onParentResize();
     }
 
+    @Test
+    public void testOnDescendantOrientationRequestChangedPropagation() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+        final TestWindowContainer root = spy(builder.build());
+
+        final IBinder binder = mock(IBinder.class);
+        final ActivityRecord activityRecord = mock(ActivityRecord.class);
+        final TestWindowContainer child = root.addChildWindow();
+
+        child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord);
+        verify(root).onDescendantOrientationChanged(binder, activityRecord);
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;