Report orientation change when visibility changes.

AppWindowToken#getOrientation(int) consults visibility to decide if it
returns requested orientation or SCREEN_ORIENTATION_UNSET. Therefore
if the requested orientation is not unset, the requested orientation
exposed will be changed.

This gives corresponding task a chance to letterbox when the visibility
of top activity changes, which covers cases where top activity changes.

To make this change less risky at this moment, only call it when we're
sure that display doesn't rotate for app requested orientation.

Add a null check in Task#onDescednantOrientationChanged() because it now
may be called when app is closing and TaskRecord has been detached from
its ActivityStack.

Bug: 123716525
Test: Manual tests. go/wm-smoke on default mode.
atest AppWindowTokenTests
Change-Id: I6f4fba9f98c134e64f1d06e8556069b5609ef925
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 88c8b95..ce5a047 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.