diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 1a619bd4475ac..2d06ee8d06bcc 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -205,4 +205,15 @@ oneway interface ITaskStackListener { * @param {@code true} if the task got focus, {@code false} if it lost it. */ void onTaskFocusChanged(int taskId, boolean focused); + + /** + * Called when a task changes its requested orientation. It is different from {@link + * #onActivityRequestedOrientationChanged(int, int)} in the sense that this method is called + * when a task changes requested orientation due to activity launch, dimiss or reparenting. + * + * @param taskId id of the task. + * @param requestedOrientation the new requested orientation of this task as screen orientations + * in {@link android.content.pm.ActivityInfo}. + */ + void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation); } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index bfa91aabc6f5a..5d8daf88a8de6 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -195,4 +195,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override public void onTaskFocusChanged(int taskId, boolean focused) { } + + @Override + public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) { + } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c66ff330edbdf..4638a4d5b2179 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1392,6 +1392,13 @@ class DisplayContent extends WindowContainer { @Surface.Rotation private int mRotation; + /** + * Last requested orientation reported to DisplayContent. This is different from {@link + * #mOrientation} in the sense that this takes activities' requested orientation into + * account. Start with {@link ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} so that we don't need + * to notify for activities that don't specify any orientation. + */ + int mLastReportedRequestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + // For comparison with DisplayContent bounds. private Rect mTmpRect = new Rect(); // For handling display rotations. diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 4b0e293e831ed..df0fa9cc3272a 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -60,6 +60,7 @@ class TaskChangeNotificationController { private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25; private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26; private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27; + private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 28; // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -178,6 +179,10 @@ class TaskChangeNotificationController { l.onTaskFocusChanged(m.arg1, m.arg2 != 0); }; + private final TaskStackConsumer mNotifyTaskRequestedOrientationChanged = (l, m) -> { + l.onTaskRequestedOrientationChanged(m.arg1, m.arg2); + }; + @FunctionalInterface public interface TaskStackConsumer { void accept(ITaskStackListener t, Message m) throws RemoteException; @@ -269,6 +274,9 @@ class TaskChangeNotificationController { case NOTIFY_TASK_FOCUS_CHANGED_MSG: forAllRemoteListeners(mNotifyTaskFocusChanged, msg); break; + case NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG: + forAllRemoteListeners(mNotifyTaskRequestedOrientationChanged, msg); + break; } if (msg.obj instanceof SomeArgs) { ((SomeArgs) msg.obj).recycle(); @@ -558,4 +566,12 @@ class TaskChangeNotificationController { forAllLocalListeners(mNotifyTaskFocusChanged, msg); msg.sendToTarget(); } + + /** @see android.app.ITaskStackListener#onTaskRequestedOrientationChanged(int, int) */ + void notifyTaskRequestedOrientationChanged(int taskId, int requestedOrientation) { + final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG, + taskId, requestedOrientation); + forAllLocalListeners(mNotifyTaskRequestedOrientationChanged, msg); + msg.sendToTarget(); + } } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 123bb079dbbb8..c0b92c291b959 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -54,6 +54,10 @@ + diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index 9872faaf6582d..4e92ea0f82e69 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -22,6 +22,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.app.Activity; @@ -41,6 +42,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.RemoteException; +import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.support.test.uiautomator.UiDevice; import android.text.TextUtils; @@ -56,8 +58,10 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; /** * Build/Install/Run: @@ -391,6 +395,42 @@ public class TaskStackChangedListenerTest { } }; + @Presubmit + @FlakyTest(bugId = 150409355) + @Test + public void testNotifyTaskRequestedOrientationChanged() throws Exception { + final ArrayBlockingQueue taskIdAndOrientationQueue = new ArrayBlockingQueue<>(10); + registerTaskStackChangedListener(new TaskStackListener() { + @Override + public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) { + int[] taskIdAndOrientation = new int[2]; + taskIdAndOrientation[0] = taskId; + taskIdAndOrientation[1] = requestedOrientation; + taskIdAndOrientationQueue.offer(taskIdAndOrientation); + } + }); + + final LandscapeActivity activity = + (LandscapeActivity) startTestActivity(LandscapeActivity.class); + + int[] taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, + candidate -> candidate[0] == activity.getTaskId()); + assertNotNull(taskIdAndOrientation); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, taskIdAndOrientation[1]); + + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, + candidate -> candidate[0] == activity.getTaskId()); + assertNotNull(taskIdAndOrientation); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, taskIdAndOrientation[1]); + + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue, + candidate -> candidate[0] == activity.getTaskId()); + assertNotNull(taskIdAndOrientation); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, taskIdAndOrientation[1]); + } + /** * Starts the provided activity and returns the started instance. */ @@ -432,6 +472,19 @@ public class TaskStackChangedListenerTest { } } + private T waitForResult(ArrayBlockingQueue queue, Predicate predicate) { + try { + final long timeout = SystemClock.uptimeMillis() + TimeUnit.SECONDS.toMillis(15); + T result; + do { + result = queue.poll(timeout - SystemClock.uptimeMillis(), TimeUnit.MILLISECONDS); + } while (result != null && !predicate.test(result)); + return result; + } catch (InterruptedException e) { + return null; + } + } + public static class TestActivity extends Activity { boolean mIsResumed = false; @@ -563,4 +616,6 @@ public class TaskStackChangedListenerTest { // Activity that has {@link android.R.attr#resizeableActivity} attribute set to {@code true} public static class ActivityInActivityView extends TestActivity {} + + public static class LandscapeActivity extends TestActivity {} }