diff options
9 files changed, 134 insertions, 25 deletions
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 0032b9ce0512..e10f7c838c74 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -73,11 +73,17 @@ interface ITaskOrganizerController { /** * Controls whether ignore orientation request logic in {@link - * com.android.server.wm.DisplayArea} is disabled at runtime. + * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some + * requested orientations to others. * * @param isDisabled when {@code true}, the system always ignores the value of {@link * com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} and app * requested orientation is respected. + * @param fromOrientations The orientations we want to map to the correspondent orientations + * in toOrientation. + * @param toOrientations The orientations we map to the ones in fromOrientations at the same + * index */ - void setIsIgnoreOrientationRequestDisabled(boolean isDisabled); + void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled, + in int[] fromOrientations, in int[] toOrientations); } diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index d4728c1187d7..2913faf9d74d 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -267,17 +267,24 @@ public class TaskOrganizer extends WindowOrganizer { /** * Controls whether ignore orientation request logic in {@link - * com.android.server.wm.DisplayArea} is disabled at runtime. + * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some + * requested orientation to others. * - * @param isDisabled when {@code true}, the system always ignores the value of {@link - * com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} and app - * requested orientation is respected. + * @param isIgnoreOrientationRequestDisabled when {@code true}, the system always ignores the + * value of {@link com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} + * and app requested orientation is respected. + * @param fromOrientations The orientations we want to map to the correspondent orientations + * in toOrientation. + * @param toOrientations The orientations we map to the ones in fromOrientations at the same + * index * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) - public void setIsIgnoreOrientationRequestDisabled(boolean isDisabled) { + public void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled, + @Nullable int[] fromOrientations, @Nullable int[] toOrientations) { try { - mTaskOrganizerController.setIsIgnoreOrientationRequestDisabled(isDisabled); + mTaskOrganizerController.setOrientationRequestPolicy(isIgnoreOrientationRequestDisabled, + fromOrientations, toOrientations); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java index 2d84d211e30a..318a49a8de31 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java @@ -21,6 +21,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; import static android.view.Display.DEFAULT_DISPLAY; import android.app.ActivityManager; @@ -33,6 +35,7 @@ import android.graphics.Rect; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.view.Display; import android.view.InsetsSource; import android.view.InsetsState; import android.view.SurfaceControl; @@ -44,6 +47,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -80,6 +84,12 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer { private final DisplayController mDisplayController; private final DisplayInsetsController mDisplayInsetsController; + /** + * The value of the {@link R.bool.config_reverseDefaultRotation} property which defines how + * {@link Display#getRotation} values are mapped to screen orientations + */ + private final boolean mReverseDefaultRotationEnabled; + @VisibleForTesting ActivityManager.RunningTaskInfo mLaunchRootTask; @VisibleForTesting @@ -188,6 +198,8 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer { mDisplayInsetsController = displayInsetsController; mKidsModeSettingsObserver = kidsModeSettingsObserver; shellInit.addInitCallback(this::onInit, this); + mReverseDefaultRotationEnabled = context.getResources().getBoolean( + R.bool.config_reverseDefaultRotation); } public KidsModeTaskOrganizer( @@ -211,6 +223,8 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer { mDisplayController = displayController; mDisplayInsetsController = displayInsetsController; shellInit.addInitCallback(this::onInit, this); + mReverseDefaultRotationEnabled = context.getResources().getBoolean( + R.bool.config_reverseDefaultRotation); } /** @@ -294,7 +308,14 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer { // Needed since many Kids apps aren't optimised to support both orientations and it will be // hard for kids to understand the app compat mode. // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once possible. - setIsIgnoreOrientationRequestDisabled(true); + if (mReverseDefaultRotationEnabled) { + setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true, + /* fromOrientations */ new int[]{SCREEN_ORIENTATION_REVERSE_LANDSCAPE}, + /* toOrientations */ new int[]{SCREEN_ORIENTATION_LANDSCAPE}); + } else { + setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true, + /* fromOrientations */ null, /* toOrientations */ null); + } final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY); if (displayLayout != null) { mDisplayWidth = displayLayout.width(); @@ -315,7 +336,8 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer { @VisibleForTesting void disable() { - setIsIgnoreOrientationRequestDisabled(false); + setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ false, + /* fromOrientations */ null, /* toOrientations */ null); mDisplayInsetsController.removeInsetsChangedListener(DEFAULT_DISPLAY, mOnInsetsChangedListener); mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java index ecfb427dbced..58e91cb50c7a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.content.res.Resources; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -77,6 +78,7 @@ public class KidsModeTaskOrganizerTest extends ShellTestCase { @Mock private ShellInit mShellInit; @Mock private ShellCommandHandler mShellCommandHandler; @Mock private DisplayInsetsController mDisplayInsetsController; + @Mock private Resources mResources; KidsModeTaskOrganizer mOrganizer; @@ -89,10 +91,12 @@ public class KidsModeTaskOrganizerTest extends ShellTestCase { } catch (RemoteException e) { } // NOTE: KidsModeTaskOrganizer should have a null CompatUIController. - mOrganizer = spy(new KidsModeTaskOrganizer(mContext, mShellInit, mShellCommandHandler, - mTaskOrganizerController, mSyncTransactionQueue, mDisplayController, - mDisplayInsetsController, Optional.empty(), Optional.empty(), mObserver, - mTestExecutor, mHandler)); + doReturn(mResources).when(mContext).getResources(); + final KidsModeTaskOrganizer kidsModeTaskOrganizer = new KidsModeTaskOrganizer(mContext, + mShellInit, mShellCommandHandler, mTaskOrganizerController, mSyncTransactionQueue, + mDisplayController, mDisplayInsetsController, Optional.empty(), Optional.empty(), + mObserver, mTestExecutor, mHandler); + mOrganizer = spy(kidsModeTaskOrganizer); doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction(); doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY); } @@ -112,6 +116,8 @@ public class KidsModeTaskOrganizerTest extends ShellTestCase { verify(mOrganizer, times(1)).registerOrganizer(); verify(mOrganizer, times(1)).createRootTask( eq(DEFAULT_DISPLAY), eq(WINDOWING_MODE_FULLSCREEN), eq(mOrganizer.mCookie)); + verify(mOrganizer, times(1)) + .setOrientationRequestPolicy(eq(true), any(), any()); final ActivityManager.RunningTaskInfo rootTask = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN, mOrganizer.mCookie); @@ -132,10 +138,11 @@ public class KidsModeTaskOrganizerTest extends ShellTestCase { doReturn(false).when(mObserver).isEnabled(); mOrganizer.updateKidsModeState(); - verify(mOrganizer, times(1)).disable(); verify(mOrganizer, times(1)).unregisterOrganizer(); verify(mOrganizer, times(1)).deleteRootTask(rootTask.token); + verify(mOrganizer, times(1)) + .setOrientationRequestPolicy(eq(false), any(), any()); assertThat(mOrganizer.mLaunchRootLeash).isNull(); assertThat(mOrganizer.mLaunchRootTask).isNull(); } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 5db39fc8434c..980a9418725b 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -447,6 +447,10 @@ final class LetterboxUiController { @ScreenOrientation int overrideOrientationIfNeeded(@ScreenOrientation int candidate) { + // In some cases (e.g. Kids app) we need to map the candidate orientation to some other + // orientation. + candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate); + if (FALSE.equals(mBooleanPropertyAllowOrientationOverride)) { return candidate; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b13136534de3..93c8c3666706 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -1166,12 +1166,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void setIsIgnoreOrientationRequestDisabled(boolean isDisabled) { - enforceTaskPermission("setIsIgnoreOrientationRequestDisabled()"); + public void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled, + @Nullable int[] fromOrientations, @Nullable int[] toOrientations) { + enforceTaskPermission("setOrientationRequestPolicy()"); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - mService.mWindowManager.setIsIgnoreOrientationRequestDisabled(isDisabled); + mService.mWindowManager + .setOrientationRequestPolicy(isIgnoreOrientationRequestDisabled, + fromOrientations, toOrientations); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index be42f36f7d30..f7641f5bfb54 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -237,6 +237,7 @@ import android.util.MergedConfiguration; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.TypedValue; import android.util.proto.ProtoOutputStream; @@ -594,6 +595,13 @@ public class WindowManagerService extends IWindowManager.Stub /** List of window currently causing non-system overlay windows to be hidden. */ private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>(); + /** + * In some cases (e.g. when {@link R.bool.config_reverseDefaultRotation} has value + * {@value true}) we need to map some orientation to others. This {@link SparseIntArray} + * contains the relation between the source orientation and the one to use. + */ + private final SparseIntArray mOrientationMapping = new SparseIntArray(); + final AccessibilityController mAccessibilityController; private RecentsAnimationController mRecentsAnimationController; @@ -4111,25 +4119,52 @@ public class WindowManagerService extends IWindowManager.Stub /** * Controls whether ignore orientation request logic in {@link DisplayArea} is disabled - * at runtime. + * at runtime and how to optionally map some requested orientations to others. * * <p>Note: this assumes that {@link #mGlobalLock} is held by the caller. * - * @param isDisabled when {@code true}, the system always ignores the value of {@link - * DisplayArea#getIgnoreOrientationRequest} and app requested orientation is - * respected. + * @param isIgnoreOrientationRequestDisabled when {@code true}, the system always ignores the + * value of {@link DisplayArea#getIgnoreOrientationRequest} and app requested + * orientation is respected. + * @param fromOrientations The orientations we want to map to the correspondent orientations + * in toOrientation. + * @param toOrientations The orientations we map to the ones in fromOrientations at the same + * index */ - void setIsIgnoreOrientationRequestDisabled(boolean isDisabled) { - if (isDisabled == mIsIgnoreOrientationRequestDisabled) { + void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled, + @Nullable int[] fromOrientations, @Nullable int[] toOrientations) { + mOrientationMapping.clear(); + if (fromOrientations != null && toOrientations != null + && fromOrientations.length == toOrientations.length) { + for (int i = 0; i < fromOrientations.length; i++) { + mOrientationMapping.put(fromOrientations[i], toOrientations[i]); + } + } + if (isIgnoreOrientationRequestDisabled == mIsIgnoreOrientationRequestDisabled) { return; } - mIsIgnoreOrientationRequestDisabled = isDisabled; + mIsIgnoreOrientationRequestDisabled = isIgnoreOrientationRequestDisabled; for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { mRoot.getChildAt(i).onIsIgnoreOrientationRequestDisabledChanged(); } } /** + * When {@link mIsIgnoreOrientationRequestDisabled} is {@value true} this method returns the + * orientation to use in place of the one in input. It returns the same requestedOrientation in + * input otherwise. + * + * @param requestedOrientation The orientation that can be mapped. + * @return The orientation to use in place of requestedOrientation. + */ + int mapOrientationRequest(int requestedOrientation) { + if (!mIsIgnoreOrientationRequestDisabled) { + return requestedOrientation; + } + return mOrientationMapping.get(requestedOrientation, requestedOrientation); + } + + /** * Whether the system ignores the value of {@link DisplayArea#getIgnoreOrientationRequest} and * app requested orientation is respected. * diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index da078a22c5ff..9b4cb134e427 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -55,6 +55,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; import android.annotation.Nullable; import android.compat.testing.PlatformCompatChangeRule; @@ -526,6 +527,16 @@ public class LetterboxUiControllerTest extends WindowTestsBase { // overrideOrientationIfNeeded @Test + public void testOverrideOrientationIfNeeded_mapInvokedOnRequest() throws Exception { + mController = new LetterboxUiController(mWm, mActivity); + spyOn(mWm); + + mController.overrideOrientationIfNeeded(SCREEN_ORIENTATION_PORTRAIT); + + verify(mWm).mapOrientationRequest(SCREEN_ORIENTATION_PORTRAIT); + } + + @Test @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT}) public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsPortrait() throws Exception { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 677ec46007ff..ba6b3b6c9378 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -117,6 +117,20 @@ public class WindowManagerServiceTests extends WindowTestsBase { ADD_TRUSTED_DISPLAY); @Test + public void testIsRequestedOrientationMapped() { + mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ true, + /* fromOrientations */ new int[]{1}, /* toOrientations */ new int[]{2}); + assertThat(mWm.mapOrientationRequest(1)).isEqualTo(2); + assertThat(mWm.mapOrientationRequest(3)).isEqualTo(3); + + // Mapping disabled + mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ false, + /* fromOrientations */ null, /* toOrientations */ null); + assertThat(mWm.mapOrientationRequest(1)).isEqualTo(1); + assertThat(mWm.mapOrientationRequest(3)).isEqualTo(3); + } + + @Test public void testAddWindowToken() { IBinder token = mock(IBinder.class); mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId(), null /* options */); |