Merge "Handle alwaysOnTop in DisplayAreas." into tm-qpr-dev
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index e3430a6..567b164 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -693,7 +693,39 @@
.build();
mHierarchyOps.add(hierarchyOp);
return this;
+ }
+ /**
+ * Sets/removes the always on top flag for this {@code windowContainer}. See
+ * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
+ * Please note that this method is only intended to be used for a
+ * {@link com.android.server.wm.DisplayArea}.
+ *
+ * <p>
+ * Setting always on top to {@code True} will also make the {@code windowContainer} to move
+ * to the top.
+ * </p>
+ * <p>
+ * Setting always on top to {@code False} will make this {@code windowContainer} to move
+ * below the other always on top sibling containers.
+ * </p>
+ *
+ * @param windowContainer the container which the flag need to be updated for.
+ * @param alwaysOnTop denotes whether or not always on top flag should be set.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setAlwaysOnTop(
+ @NonNull WindowContainerToken windowContainer,
+ boolean alwaysOnTop) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP)
+ .setContainer(windowContainer.asBinder())
+ .setAlwaysOnTop(alwaysOnTop)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
}
/**
@@ -1121,6 +1153,7 @@
public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16;
public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17;
public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18;
+ public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1171,6 +1204,8 @@
@Nullable
private ShortcutInfo mShortcutInfo;
+ private boolean mAlwaysOnTop;
+
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1268,6 +1303,7 @@
mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
mPendingIntent = copy.mPendingIntent;
mShortcutInfo = copy.mShortcutInfo;
+ mAlwaysOnTop = copy.mAlwaysOnTop;
}
protected HierarchyOp(Parcel in) {
@@ -1289,6 +1325,7 @@
mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR);
+ mAlwaysOnTop = in.readBoolean();
}
public int getType() {
@@ -1354,6 +1391,10 @@
return mActivityIntent;
}
+ public boolean isAlwaysOnTop() {
+ return mAlwaysOnTop;
+ }
+
@Nullable
public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
return mTaskFragmentCreationOptions;
@@ -1422,6 +1463,9 @@
+ " insetsType=" + Arrays.toString(mInsetsTypes) + "}";
case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT:
return "{requestFocusOnTaskFragment: container=" + mContainer + "}";
+ case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
+ return "{setAlwaysOnTop: container=" + mContainer
+ + " alwaysOnTop=" + mAlwaysOnTop + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop
@@ -1451,6 +1495,7 @@
dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
dest.writeTypedObject(mPendingIntent, flags);
dest.writeTypedObject(mShortcutInfo, flags);
+ dest.writeBoolean(mAlwaysOnTop);
}
@Override
@@ -1509,6 +1554,8 @@
@Nullable
private ShortcutInfo mShortcutInfo;
+ private boolean mAlwaysOnTop;
+
Builder(int type) {
mType = type;
}
@@ -1568,6 +1615,11 @@
return this;
}
+ Builder setAlwaysOnTop(boolean alwaysOnTop) {
+ mAlwaysOnTop = alwaysOnTop;
+ return this;
+ }
+
Builder setTaskFragmentCreationOptions(
@Nullable TaskFragmentCreationParams taskFragmentCreationOptions) {
mTaskFragmentCreationOptions = taskFragmentCreationOptions;
@@ -1596,6 +1648,7 @@
hierarchyOp.mLaunchOptions = mLaunchOptions;
hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent;
+ hierarchyOp.mAlwaysOnTop = mAlwaysOnTop;
hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
hierarchyOp.mShortcutInfo = mShortcutInfo;
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 863782a..0422906 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -207,6 +207,23 @@
return false;
}
+ @Override
+ public void setAlwaysOnTop(boolean alwaysOnTop) {
+ if (isAlwaysOnTop() == alwaysOnTop) {
+ return;
+ }
+ super.setAlwaysOnTop(alwaysOnTop);
+ // positionChildAtTop() must be called even when always on top gets turned off because
+ // we need to make sure that the display area is moved from among always on top containers
+ // to below other always on top containers. Since the position the display area should be
+ // inserted into is calculated properly in {@link DisplayContent#getTopInsertPosition()}
+ // in both cases, we can just request that the root task is put at top here.
+ if (getParent().asDisplayArea() != null) {
+ getParent().asDisplayArea().positionChildAt(POSITION_TOP, this,
+ false /* includingParents */);
+ }
+ }
+
boolean getIgnoreOrientationRequest() {
// Adding an exception for when ignoreOrientationRequest is overridden at runtime for all
// DisplayArea-s. For example, this is needed for the Kids Mode since many Kids apps aren't
@@ -234,6 +251,18 @@
// The min possible position we can insert the child at.
int minPosition = findMinPositionForChildDisplayArea(child);
+ // Place all non-always-on-top containers below always-on-top ones.
+ int alwaysOnTopCount = 0;
+ for (int i = minPosition; i <= maxPosition; i++) {
+ if (mChildren.get(i).isAlwaysOnTop()) {
+ alwaysOnTopCount++;
+ }
+ }
+ if (child.isAlwaysOnTop()) {
+ minPosition = maxPosition - alwaysOnTopCount + 1;
+ } else {
+ maxPosition -= alwaysOnTopCount;
+ }
return Math.max(Math.min(requestPosition, maxPosition), minPosition);
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5a2100d..3b9cd36 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -37,6 +37,7 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
@@ -1079,6 +1080,18 @@
WindowContainer.fromBinder(hop.getContainer())
.removeLocalInsetsSourceProvider(hop.getInsetsTypes());
break;
+ case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
+ final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+ if (container == null || container.asDisplayArea() == null
+ || !container.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached display area: "
+ + container);
+ break;
+ }
+ container.setAlwaysOnTop(hop.isAlwaysOnTop());
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ break;
+
}
return effects;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index d94e6c9..b87c5a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -399,9 +400,9 @@
parentBounds.right / 2, parentBounds.bottom);
final Rect childBounds2 = new Rect(parentBounds.right / 2, parentBounds.top,
parentBounds.right, parentBounds.bottom);
- TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds);
- TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1);
- TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2);
+ TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds, "Parent");
+ TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1, "Child1");
+ TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2, "Child2");
parentDa.addChild(childDa1, 0);
parentDa.addChild(childDa2, 1);
@@ -619,9 +620,67 @@
controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST);
}
+ @Test
+ public void testSetAlwaysOnTop_movesDisplayAreaToTop() {
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
+ parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ DisplayArea<WindowContainer> child1 = new TestDisplayArea(mWm, bounds, "Child1");
+ child1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ DisplayArea<WindowContainer> child2 = new TestDisplayArea(mWm, bounds, "Child2");
+ child2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ parent.addChild(child2, 0);
+ parent.addChild(child1, 1);
+
+ child2.setAlwaysOnTop(true);
+
+ assertEquals(parent.getChildAt(1), child2);
+ assertThat(child2.isAlwaysOnTop()).isTrue();
+ }
+
+ @Test
+ public void testDisplayAreaRequestsTopPosition_alwaysOnTopSiblingExists_doesNotMoveToTop() {
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
+ parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds,
+ "AlwaysOnTopChild");
+ alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child");
+ child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ parent.addChild(alwaysOnTopChild, 0);
+ parent.addChild(child, 1);
+ alwaysOnTopChild.setAlwaysOnTop(true);
+
+ parent.positionChildAt(POSITION_TOP, child, false /* includingParents */);
+
+ assertEquals(parent.getChildAt(1), alwaysOnTopChild);
+ assertEquals(parent.getChildAt(0), child);
+ }
+
+ @Test
+ public void testAlwaysOnTopDisplayArea_requestsNonTopLocation_doesNotMove() {
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
+ parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds,
+ "AlwaysOnTopChild");
+ alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child");
+ child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ parent.addChild(alwaysOnTopChild, 0);
+ parent.addChild(child, 1);
+ alwaysOnTopChild.setAlwaysOnTop(true);
+
+ parent.positionChildAt(POSITION_BOTTOM, alwaysOnTopChild, false /* includingParents */);
+
+ assertEquals(parent.getChildAt(1), alwaysOnTopChild);
+ assertEquals(parent.getChildAt(0), child);
+ }
+
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
- private TestDisplayArea(WindowManagerService wms, Rect bounds) {
- super(wms, ANY, "half display area");
+ private TestDisplayArea(WindowManagerService wms, Rect bounds, String name) {
+ super(wms, ANY, name);
setBounds(bounds);
}