Merge "Report orientation change when visibility changes."
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 89315d1..78199d44 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -582,6 +582,8 @@
                     }
                 }
             }
+            // Changes in opening apps and closing apps may cause orientation change.
+            reportDescendantOrientationChangeIfNeeded();
             return;
         }
 
@@ -729,11 +731,31 @@
                 }
                 SurfaceControl.closeTransaction();
             }
+
+            // Visibility changes may cause orientation request change.
+            reportDescendantOrientationChangeIfNeeded();
         }
 
         return delayed;
     }
 
+    private void reportDescendantOrientationChangeIfNeeded() {
+        // Orientation request is exposed only when we're visible. Therefore visibility change
+        // will change requested orientation. Notify upward the hierarchy ladder to adjust
+        // configuration. This is important to cases where activities with incompatible
+        // orientations launch, or user goes back from an activity of bi-orientation to an
+        // activity with specified orientation.
+        if (mActivityRecord.getRequestedConfigurationOrientation() == getConfiguration().orientation
+                || getOrientationIgnoreVisibility() == SCREEN_ORIENTATION_UNSET) {
+            return;
+        }
+
+        final IBinder freezeToken =
+                mActivityRecord.mayFreezeScreenLocked(mActivityRecord.app)
+                        ? mActivityRecord.appToken : null;
+        onDescendantOrientationChanged(freezeToken, mActivityRecord);
+    }
+
     /**
      * @return The to top most child window for which {@link LayoutParams#isFullscreen()} returns
      *         true.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 888d741..499cbaf 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -330,7 +330,7 @@
         // 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) {
+        if (mTaskRecord != null && mTaskRecord.getParent() != null) {
             mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration());
             return true;
         }
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 2c575f5..68b40b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -18,11 +18,11 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED;
 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_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
@@ -42,12 +42,16 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.verify;
 
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.view.Display;
 import android.view.Surface;
 import android.view.WindowManager;
 
@@ -55,6 +59,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 /**
  * Tests for the {@link AppWindowToken} class.
@@ -332,6 +337,46 @@
     }
 
     @Test
+    public void testReportOrientationChangeOnVisibilityChange() {
+        synchronized (mWm.mGlobalLock) {
+            mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+            mDisplayContent.getDisplayRotation().setFixedToUserRotation(
+                    DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
+
+            doReturn(Configuration.ORIENTATION_LANDSCAPE).when(mToken.mActivityRecord)
+                    .getRequestedConfigurationOrientation();
+
+            mTask.mTaskRecord = Mockito.mock(TaskRecord.class, RETURNS_DEEP_STUBS);
+            mToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
+                    true /* performLayout */, false /* isVoiceInteraction */);
+        }
+
+        verify(mTask.mTaskRecord).onConfigurationChanged(any(Configuration.class));
+    }
+
+    @Test
+    public void testReportOrientationChangeOnOpeningClosingAppChange() {
+        synchronized (mWm.mGlobalLock) {
+            mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+            mDisplayContent.getDisplayRotation().setFixedToUserRotation(
+                    DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
+            mDisplayContent.getDisplayInfo().state = Display.STATE_ON;
+            mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_CLOSE,
+                    false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
+
+            doReturn(Configuration.ORIENTATION_LANDSCAPE).when(mToken.mActivityRecord)
+                    .getRequestedConfigurationOrientation();
+
+            mTask.mTaskRecord = Mockito.mock(TaskRecord.class, RETURNS_DEEP_STUBS);
+            mToken.setVisibility(false, false);
+        }
+
+        verify(mTask.mTaskRecord).onConfigurationChanged(any(Configuration.class));
+    }
+
+    @Test
     public void testCreateRemoveStartingWindow() {
         mToken.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index da1defa..0dec8ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -82,6 +82,8 @@
             }, new ComponentName("", ""), false, dc, true /* fillsParent */);
             mTargetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT;
             mSkipOnParentChanged = skipOnParentChanged;
+            mActivityRecord = mock(ActivityRecord.class);
+            mActivityRecord.app = mock(WindowProcessController.class);
         }
 
         int getWindowsCount() {