Update some policy around multi-window windowing mode
- Allow SysUI to start task in always-on-top mode to ensure
visibility and to allow SysUI to control the visibility using
the WCT setHidden call.
- Expose unregisterTaskOrganizer call for SysUI
- Skip dispatching status/nav bar insets for always on top mw
tasks. Since they are effectively floating, it doesn't make
sense for us to send bar insets
- Fix issue with preferred windowing mode being referenced too
early (before computeLaunchParams), and apply the windowing
mode to the root task when it is brought forward
Bug: 151392361
Test: atest WmTests:ActivityOptionsTest
Test: atest InsetsStateControllerTest
Change-Id: Ia3d8a369c902928e8de51a6b7479b10848ebe44a
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c7a1391..14ca7cb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -27,8 +27,10 @@
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -167,6 +169,8 @@
// The display to launch the activity onto, barring any strong reason to do otherwise.
private int mPreferredDisplayId;
+ // The windowing mode to apply to the root task, if possible
+ private int mPreferredWindowingMode;
private Task mInTask;
@VisibleForTesting
@@ -538,6 +542,7 @@
mStartFlags = starter.mStartFlags;
mSourceRecord = starter.mSourceRecord;
mPreferredDisplayId = starter.mPreferredDisplayId;
+ mPreferredWindowingMode = starter.mPreferredWindowingMode;
mInTask = starter.mInTask;
mAddingToTask = starter.mAddingToTask;
@@ -1493,8 +1498,6 @@
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor, restrictedBgActivity);
- final int preferredWindowingMode = mLaunchParams.mWindowingMode;
-
computeLaunchingTaskFlags();
computeSourceStack();
@@ -1564,6 +1567,14 @@
if (!mAvoidMoveToFront && mDoResume) {
mTargetStack.moveToFront("reuseOrNewTask");
+ if (mOptions != null) {
+ if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) {
+ mTargetStack.setWindowingMode(mPreferredWindowingMode);
+ }
+ if (mOptions.getTaskAlwaysOnTop()) {
+ mTargetStack.setAlwaysOnTop(true);
+ }
+ }
}
mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
@@ -1627,7 +1638,7 @@
// Update the recent tasks list immediately when the activity starts
mSupervisor.mRecentTasks.add(mStartActivity.getTask());
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
- preferredWindowingMode, mPreferredDisplayId, mTargetStack);
+ mPreferredWindowingMode, mPreferredDisplayId, mTargetStack);
return START_SUCCESS;
}
@@ -1680,9 +1691,10 @@
mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams);
- mPreferredDisplayId =
- mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
+ mPreferredDisplayId = mLaunchParams.hasPreferredDisplay()
+ ? mLaunchParams.mPreferredDisplayId
+ : DEFAULT_DISPLAY;
+ mPreferredWindowingMode = mLaunchParams.mWindowingMode;
}
private int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) {
@@ -2006,6 +2018,7 @@
mStartFlags = 0;
mSourceRecord = null;
mPreferredDisplayId = INVALID_DISPLAY;
+ mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED;
mInTask = null;
mAddingToTask = false;
@@ -2054,9 +2067,10 @@
// after we located a reusable task (which might be resided in another display).
mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
sourceRecord, options, PHASE_DISPLAY, mLaunchParams);
- mPreferredDisplayId =
- mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
+ mPreferredDisplayId = mLaunchParams.hasPreferredDisplay()
+ ? mLaunchParams.mPreferredDisplayId
+ : DEFAULT_DISPLAY;
+ mPreferredWindowingMode = mLaunchParams.mWindowingMode;
mLaunchMode = r.launchMode;
@@ -2098,7 +2112,7 @@
}
if (mOptions != null) {
- if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
+ if (mOptions.getLaunchTaskId() != INVALID_TASK_ID && mOptions.getTaskOverlay()) {
r.setTaskOverlay(true);
if (!mOptions.canTaskOverlayResume()) {
final Task task = mRootWindowContainer.anyTaskForId(
@@ -2291,6 +2305,15 @@
* if not or an ActivityRecord with the task into which the new activity should be added.
*/
private Task getReusableTask() {
+ // If a target task is specified, try to reuse that one
+ if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
+ Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
+ if (launchTask != null) {
+ return launchTask;
+ }
+ return null;
+ }
+
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
@@ -2304,12 +2327,7 @@
// same component, then instead of launching bring that one to the front.
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
- if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
- Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
- if (launchTask != null) {
- return launchTask;
- }
- } else if (putIntoExistingTask) {
+ if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ee36db9..a4bdfb3 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -87,7 +88,8 @@
final InsetsSourceProvider provider = target.getControllableInsetProvider();
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
- return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode());
+ return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode(),
+ target.isAlwaysOnTop());
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -95,7 +97,9 @@
final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
- return getInsetsForTypeAndWindowingMode(type, windowingMode);
+ final boolean alwaysOnTop = token != null
+ ? token.isAlwaysOnTop() : false;
+ return getInsetsForTypeAndWindowingMode(type, windowingMode, alwaysOnTop);
}
private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
@@ -113,7 +117,7 @@
/** @see #getInsetsForDispatch */
private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type,
- @WindowingMode int windowingMode) {
+ @WindowingMode int windowingMode, boolean isAlwaysOnTop) {
InsetsState state = mState;
if (type != ITYPE_INVALID) {
@@ -147,7 +151,8 @@
}
}
- if (WindowConfiguration.isFloating(windowingMode)) {
+ if (WindowConfiguration.isFloating(windowingMode)
+ || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
state = new InsetsState(state);
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8f09f3f..cc52cb4 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -231,7 +231,8 @@
}
}
- void unregisterTaskOrganizer(ITaskOrganizer organizer) {
+ @Override
+ public void unregisterTaskOrganizer(ITaskOrganizer organizer) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
state.unlinkDeath();
if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index c449049..2acb647 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -51,6 +51,7 @@
opts.setLaunchTaskId(Integer.MAX_VALUE);
opts.setLockTaskEnabled(true);
opts.setRotationAnimationHint(ROTATION_ANIMATION_ROTATE);
+ opts.setTaskAlwaysOnTop(true);
opts.setTaskOverlay(true, true);
opts.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
Bundle optsBundle = opts.toBundle();
@@ -67,6 +68,7 @@
assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchTaskId());
assertTrue(restoredOpts.getLockTaskMode());
assertEquals(ROTATION_ANIMATION_ROTATE, restoredOpts.getRotationAnimationHint());
+ assertTrue(restoredOpts.getTaskAlwaysOnTop());
assertTrue(restoredOpts.getTaskOverlay());
assertTrue(restoredOpts.canTaskOverlayResume());
assertEquals(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index a380ece..bfb126f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -131,6 +132,21 @@
}
@Test
+ public void testStripForDispatch_multiwindow_alwaysOnTop() {
+ final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ app.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ app.setAlwaysOnTop(true);
+
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+ }
+
+ @Test
public void testImeForDispatch() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");