Add API to create new TaskDisplayArea at runtime
Allow DA organizer to create new TaskDisplayArea.
Fix: 173455508
Test: atest WmTests:DisplayAreaOrganizerTest
Change-Id: I7d4e88f43ae14561720c6942ca0e9f82dc28e9f6
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 6e20452..1ac188c 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -101,6 +101,19 @@
public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1;
/**
+ * Last possible vendor specific display area id.
+ * @hide
+ */
+ public static final int FEATURE_VENDOR_LAST = FEATURE_VENDOR_FIRST + 10_000;
+
+ /**
+ * Task display areas that can be created at runtime start with this value.
+ * @see #createTaskDisplayArea(int, int, String)
+ * @hide
+ */
+ public static final int FEATURE_RUNTIME_TASK_CONTAINER_FIRST = FEATURE_VENDOR_LAST + 1;
+
+ /**
* Registers a DisplayAreaOrganizer to manage display areas for a given feature. A feature can
* not be registered by multiple organizers at the same time.
*
@@ -132,6 +145,50 @@
}
/**
+ * Creates a persistent task display area. It will be added to be the top most task display area
+ * in the root.
+ *
+ * The new created TDA is organized by the organizer, and will be deleted on calling
+ * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}.
+ *
+ * @param displayId the display to create the new task display area in.
+ * @param rootFeatureId the root display area to create the new task display area in. Caller can
+ * use {@link #FEATURE_ROOT} as the root of the logical display.
+ * @param name the name for the new task display area.
+ * @return the new created task display area.
+ * @throws IllegalArgumentException if failed to create a new task display area.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ @CallSuper
+ @NonNull
+ public DisplayAreaAppearedInfo createTaskDisplayArea(int displayId, int rootFeatureId,
+ @NonNull String name) {
+ try {
+ return getController().createTaskDisplayArea(
+ mInterface, displayId, rootFeatureId, name);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Deletes a persistent task display area. It can only be one that created by an organizer.
+ *
+ * @throws IllegalArgumentException if failed to delete the task display area.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ @CallSuper
+ public void deleteTaskDisplayArea(@NonNull WindowContainerToken taskDisplayArea) {
+ try {
+ getController().deleteTaskDisplayArea(taskDisplayArea);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called when a DisplayArea of the registered window type can be controlled by this organizer.
* It will not be called for the DisplayAreas that exist when {@link #registerOrganizer(int)} is
* called.
diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl
index edabcf8..26fa434 100644
--- a/core/java/android/window/IDisplayAreaOrganizerController.aidl
+++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl
@@ -19,6 +19,7 @@
import android.content.pm.ParceledListSlice;
import android.window.DisplayAreaAppearedInfo;
import android.window.IDisplayAreaOrganizer;
+import android.window.WindowContainerToken;
/** @hide */
interface IDisplayAreaOrganizerController {
@@ -37,4 +38,28 @@
* Unregisters a previously registered display area organizer.
*/
void unregisterOrganizer(in IDisplayAreaOrganizer organizer);
+
+ /**
+ * Creates a persistent task display area. It will be added to be the top most task display area
+ * in the root.
+ *
+ * The new created TDA is organized by the organizer, and will be deleted on calling
+ * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}.
+ *
+ * @param displayId the display to create the new task display area in.
+ * @param rootFeatureId the root display area to create the new task display area in. Caller can
+ * use {@link #FEATURE_ROOT} as the root of the logical display.
+ * @param name the name for the new task display area.
+ * @return the new created task display area.
+ * @throws IllegalArgumentException if failed to create a new task display area.
+ */
+ DisplayAreaAppearedInfo createTaskDisplayArea(in IDisplayAreaOrganizer organizer, int displayId,
+ int rootFeatureId, in String name);
+
+ /**
+ * Deletes a persistent task display area. It can only be one that created by an organizer.
+ *
+ * @throws IllegalArgumentException if failed to delete the task display area.
+ */
+ void deleteTaskDisplayArea(in WindowContainerToken taskDisplayArea);
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 52da707..1031023 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -961,6 +961,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
+ "-948446688": {
+ "message": "Create TaskDisplayArea uid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+ },
"-937498525": {
"message": "Executing finish of failed to pause activity: %s",
"level": "VERBOSE",
@@ -1273,6 +1279,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-597091183": {
+ "message": "Delete TaskDisplayArea uid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+ },
"-593535526": {
"message": "Binding proc %s with config %s",
"level": "VERBOSE",
@@ -1495,12 +1507,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-371630969": {
- "message": "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/AppTransitionController.java"
- },
"-354571697": {
"message": "Existence Changed in transition %d: %s",
"level": "VERBOSE",
@@ -2101,12 +2107,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "355940361": {
- "message": "Config is destroying non-running %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"371173718": {
"message": "finishSync cancel=%b for %s",
"level": "VERBOSE",
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 8ad2958..48e0300 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -382,6 +382,13 @@
@Nullable
@Override
+ <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) {
+ final R item = super.getItemFromDisplayAreas(callback);
+ return item != null ? item : callback.apply(this);
+ }
+
+ @Nullable
+ @Override
<R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
boolean traverseTopToBottom) {
// Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 43b9a21..c475da3 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -16,7 +16,10 @@
package com.android.server.wm;
+import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.DisplayArea.Type.ANY;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
@@ -26,6 +29,7 @@
import android.window.DisplayAreaAppearedInfo;
import android.window.IDisplayAreaOrganizer;
import android.window.IDisplayAreaOrganizerController;
+import android.window.WindowContainerToken;
import com.android.internal.protolog.common.ProtoLog;
@@ -36,6 +40,12 @@
public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
private static final String TAG = "DisplayAreaOrganizerController";
+ /**
+ * Next available feature id for a runtime task display area.
+ * @see #createTaskDisplayArea(IDisplayAreaOrganizer organizer, int, int, String)
+ */
+ private int mNextTaskDisplayAreaFeatureId = FEATURE_RUNTIME_TASK_CONTAINER_FIRST;
+
final ActivityTaskManagerService mService;
private final WindowManagerGlobalLock mGlobalLock;
private final HashMap<Integer, IDisplayAreaOrganizer> mOrganizersByFeatureIds = new HashMap();
@@ -92,10 +102,8 @@
final List<DisplayAreaAppearedInfo> displayAreaInfos = new ArrayList<>();
mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
if (da.mFeatureId != feature) return;
- da.setOrganizer(organizer, true /* skipDisplayAreaAppeared */);
- displayAreaInfos.add(new DisplayAreaAppearedInfo(da.getDisplayAreaInfo(),
- new SurfaceControl(da.getSurfaceControl(),
- "DisplayAreaOrganizerController.registerOrganizer")));
+ displayAreaInfos.add(organizeDisplayArea(organizer, da,
+ "DisplayAreaOrganizerController.registerOrganizer"));
});
mOrganizersByFeatureIds.put(feature, organizer);
@@ -124,6 +132,77 @@
}
}
+ @Override
+ public DisplayAreaAppearedInfo createTaskDisplayArea(IDisplayAreaOrganizer organizer,
+ int displayId, int rootFeatureId, String name) {
+ enforceTaskPermission("createTaskDisplayArea()");
+ final long uid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create TaskDisplayArea uid=%d", uid);
+
+ final DisplayContent display =
+ mService.mRootWindowContainer.getDisplayContent(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException("createTaskDisplayArea unknown displayId="
+ + displayId);
+ }
+
+ final DisplayArea root = display.getItemFromDisplayAreas(da ->
+ da.asRootDisplayArea() != null && da.mFeatureId == rootFeatureId
+ ? da
+ : null);
+ if (root == null) {
+ throw new IllegalArgumentException("Can't find RootDisplayArea with featureId="
+ + rootFeatureId);
+ }
+
+ final int taskDisplayAreaFeatureId = mNextTaskDisplayAreaFeatureId++;
+ final DeathRecipient dr = new DeathRecipient(organizer, taskDisplayAreaFeatureId);
+ try {
+ organizer.asBinder().linkToDeath(dr, 0);
+ } catch (RemoteException e) {
+ // Oh well...
+ }
+
+ final TaskDisplayArea tda = createTaskDisplayArea(root.asRootDisplayArea(), name,
+ taskDisplayAreaFeatureId);
+ return organizeDisplayArea(organizer, tda,
+ "DisplayAreaOrganizerController.createTaskDisplayArea");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void deleteTaskDisplayArea(WindowContainerToken token) {
+ enforceTaskPermission("deleteTaskDisplayArea()");
+ final long uid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete TaskDisplayArea uid=%d", uid);
+
+ final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
+ if (wc == null || wc.asTaskDisplayArea() == null) {
+ throw new IllegalArgumentException("Can't resolve TaskDisplayArea from token");
+ }
+ final TaskDisplayArea taskDisplayArea = wc.asTaskDisplayArea();
+ if (!taskDisplayArea.mCreatedByOrganizer) {
+ throw new IllegalArgumentException(
+ "Attempt to delete TaskDisplayArea not created by organizer "
+ + "TaskDisplayArea=" + taskDisplayArea);
+ }
+
+ deleteTaskDisplayArea(taskDisplayArea);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName());
try {
@@ -157,8 +236,71 @@
IBinder organizerBinder = organizer.asBinder();
mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
if (da.mOrganizer != null && da.mOrganizer.asBinder().equals(organizerBinder)) {
- da.setOrganizer(null);
+ if (da.isTaskDisplayArea() && da.asTaskDisplayArea().mCreatedByOrganizer) {
+ // Delete the organizer created TDA when unregister.
+ deleteTaskDisplayArea(da.asTaskDisplayArea());
+ } else {
+ da.setOrganizer(null);
+ }
}
});
}
+
+ private DisplayAreaAppearedInfo organizeDisplayArea(IDisplayAreaOrganizer organizer,
+ DisplayArea displayArea, String callsite) {
+ displayArea.setOrganizer(organizer, true /* skipDisplayAreaAppeared */);
+ return new DisplayAreaAppearedInfo(displayArea.getDisplayAreaInfo(),
+ new SurfaceControl(displayArea.getSurfaceControl(), callsite));
+ }
+
+ private TaskDisplayArea createTaskDisplayArea(RootDisplayArea root, String name,
+ int taskDisplayAreaFeatureId) {
+ final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(root.mDisplayContent,
+ root.mWmService, name, taskDisplayAreaFeatureId, true /* createdByOrganizer */);
+
+ // Find the top most DA that can contain Task (either a TDA or a DisplayAreaGroup).
+ final DisplayArea topTaskContainer = root.getItemFromDisplayAreas(da -> {
+ if (da.mType != ANY) {
+ return null;
+ }
+
+ final RootDisplayArea rootDA = da.getRootDisplayArea();
+ if (rootDA == root || rootDA == da) {
+ // Either it is the top TDA below the root or it is a DisplayAreaGroup.
+ return da;
+ }
+ return null;
+ });
+ if (topTaskContainer == null) {
+ throw new IllegalStateException("Root must either contain TDA or DAG root=" + root);
+ }
+
+ // Insert the TaskDisplayArea as the top Task container.
+ final WindowContainer parent = topTaskContainer.getParent();
+ final int index = parent.mChildren.indexOf(topTaskContainer) + 1;
+ parent.addChild(taskDisplayArea, index);
+
+ return taskDisplayArea;
+ }
+
+ private void deleteTaskDisplayArea(TaskDisplayArea taskDisplayArea) {
+ taskDisplayArea.setOrganizer(null);
+ mService.mRootWindowContainer.mTaskSupervisor.beginDeferResume();
+
+ // TaskDisplayArea#remove() move the stacks to the default TaskDisplayArea.
+ Task lastReparentedStack;
+ try {
+ lastReparentedStack = taskDisplayArea.remove();
+ } finally {
+ mService.mRootWindowContainer.mTaskSupervisor.endDeferResume();
+ }
+
+ taskDisplayArea.removeImmediately();
+
+ // Only update focus/visibility for the last one because there may be many stacks are
+ // reparented and the intermediate states are unnecessary.
+ if (lastReparentedStack != null) {
+ lastReparentedStack.postReparent();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 80ec722..6a42087 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -172,10 +172,16 @@
throw new IllegalStateException("Root must be set for the display area policy.");
}
+ final Set<Integer> rootIdSet = new ArraySet<>();
+ rootIdSet.add(mRootHierarchyBuilder.mRoot.mFeatureId);
boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null;
boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder);
for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
+ if (!rootIdSet.add(hierarchyBuilder.mRoot.mFeatureId)) {
+ throw new IllegalStateException("There should not be two RootDisplayAreas with id "
+ + hierarchyBuilder.mRoot.mFeatureId);
+ }
if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) {
throw new IllegalStateException(
"DisplayAreaGroup must contain at least one TaskDisplayArea.");
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
index 1e5d045..da04f43 100644
--- a/services/core/java/com/android/server/wm/RootDisplayArea.java
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -57,6 +57,11 @@
return this;
}
+ @Override
+ RootDisplayArea asRootDisplayArea() {
+ return this;
+ }
+
/** Whether the orientation (based on dimensions) of this root is different from the Display. */
boolean isOrientationDifferentFromDisplay() {
return false;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c02e7ad..81b8200 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -157,12 +157,24 @@
*/
private int mLastLeafTaskToFrontId;
+ /**
+ * Whether this TaskDisplayArea was created by a {@link android.window.DisplayAreaOrganizer}.
+ * If {@code true}, this will be removed when the organizer is unregistered.
+ */
+ final boolean mCreatedByOrganizer;
+
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
int displayAreaFeature) {
+ this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */);
+ }
+
+ TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
+ int displayAreaFeature, boolean createdByOrganizer) {
super(service, Type.ANY, name, displayAreaFeature);
mDisplayContent = displayContent;
mRootWindowContainer = service.mRoot;
mAtmService = service.mAtmService;
+ mCreatedByOrganizer = createdByOrganizer;
}
/**
@@ -1914,6 +1926,11 @@
}
@Override
+ TaskDisplayArea asTaskDisplayArea() {
+ return this;
+ }
+
+ @Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
pw.println(prefix + "TaskDisplayArea " + getName());
final String doublePrefix = prefix + " ";
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 4574be7..06449c6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1814,7 +1814,7 @@
/**
* For all {@link TaskDisplayArea} at or below this container call the callback.
* @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
- * returns {@code true}.
+ * returns {@code true}.
* @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
* terms of z-order, else from bottom-to-top.
* @return {@code true} if the search ended before we reached the end of the hierarchy due to
@@ -1837,7 +1837,7 @@
* For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from
* top to bottom in terms of z-order.
* @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
- * returns {@code true}.
+ * returns {@code true}.
* @return {@code true} if the search ended before we reached the end of the hierarchy due to
* callback returning {@code true}.
*/
@@ -1873,7 +1873,7 @@
* Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the
* provided initial value and an accumulation function, and returns the reduced value.
* @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result
- * from the previous call.
+ * from the previous call.
* @param initValue The initial value to pass to the accumulating function with the first
* {@link TaskDisplayArea}.
* @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
@@ -1899,7 +1899,7 @@
* provided initial value and an accumulation function, and returns the reduced value. Traverses
* from top to bottom in terms of z-order.
* @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result
- * from the previous call.
+ * from the previous call.
* @param initValue The initial value to pass to the accumulating function with the first
* {@link TaskDisplayArea}.
* @return the accumulative result.
@@ -1912,9 +1912,29 @@
/**
* Finds the first non {@code null} return value from calling the callback on all
+ * {@link DisplayArea} at or below this container. Traverses from top to bottom in terms of
+ * z-order.
+ * @param callback Applies on each {@link DisplayArea} found and stops the search if it
+ * returns non {@code null}.
+ * @return the first returned object that is not {@code null}. Returns {@code null} if not
+ * found.
+ */
+ @Nullable
+ <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ R result = (R) mChildren.get(i).getItemFromDisplayAreas(callback);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds the first non {@code null} return value from calling the callback on all
* {@link TaskDisplayArea} at or below this container.
* @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
- * returns non {@code null}.
+ * returns non {@code null}.
* @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
* terms of z-order, else from bottom-to-top.
* @return the first returned object that is not {@code null}. Returns {@code null} if not
@@ -1941,7 +1961,7 @@
* {@link TaskDisplayArea} at or below this container. Traverses from top to bottom in terms of
* z-order.
* @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
- * returns non {@code null}.
+ * returns non {@code null}.
* @return the first returned object that is not {@code null}. Returns {@code null} if not
* found.
*/
@@ -2884,6 +2904,16 @@
return null;
}
+ /** Cheap way of doing cast and instanceof. */
+ RootDisplayArea asRootDisplayArea() {
+ return null;
+ }
+
+ /** Cheap way of doing cast and instanceof. */
+ TaskDisplayArea asTaskDisplayArea() {
+ return null;
+ }
+
/**
* @return {@code true} if window container is manage by a
* {@link android.window.WindowOrganizer}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index 3220d1d..1198ee2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -16,8 +16,13 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
+import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -25,6 +30,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -55,9 +61,12 @@
public class DisplayAreaOrganizerTest extends WindowTestsBase {
private DisplayArea mTestDisplayArea;
+ private DisplayAreaOrganizerController mOrganizerController;
@Before
public void setUp() {
+ mOrganizerController =
+ mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
WindowContainer parentWindow = mDisplayContent.getDefaultTaskDisplayArea().getParent();
mTestDisplayArea = new DisplayArea(mWm, DisplayArea.Type.ANY,
"TestDisplayArea", FEATURE_VENDOR_FIRST);
@@ -76,8 +85,7 @@
private IDisplayAreaOrganizer registerMockOrganizer(int feature, Binder binder) {
final IDisplayAreaOrganizer organizer = createMockOrganizer(binder);
- mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
- .registerOrganizer(organizer, feature);
+ mOrganizerController.registerOrganizer(organizer, feature);
return organizer;
}
@@ -87,16 +95,10 @@
return organizer;
}
- private void unregisterMockOrganizer(IDisplayAreaOrganizer organizer) {
- mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
- .unregisterOrganizer(organizer);
- }
-
@Test
public void testRegisterOrganizer() throws RemoteException {
- IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
- List<DisplayAreaAppearedInfo> infos = mWm.mAtmService.mWindowOrganizerController
- .mDisplayAreaOrganizerController
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ List<DisplayAreaAppearedInfo> infos = mOrganizerController
.registerOrganizer(organizer, FEATURE_VENDOR_FIRST).getList();
// Return a list contains the DA, and no onDisplayAreaAppeared triggered.
@@ -108,16 +110,135 @@
}
@Test
+ public void testRegisterOrganizer_alreadyRegisteredFeature() {
+ registerMockOrganizer(FEATURE_VENDOR_FIRST);
+ assertThrows(IllegalStateException.class,
+ () -> registerMockOrganizer(FEATURE_VENDOR_FIRST));
+ }
+
+ @Test
+ public void testCreateTaskDisplayArea() {
+ final String newTdaName = "testTda";
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName);
+
+ final int newTdaIndex =
+ mTestDisplayArea.getParent().mChildren.indexOf(mTestDisplayArea) + 1;
+ final WindowContainer wc = mTestDisplayArea.getParent().getChildAt(newTdaIndex);
+
+ // A new TaskDisplayArea is created on the top.
+ assertThat(wc).isInstanceOf(TaskDisplayArea.class);
+ assertThat(tdaInfo.getDisplayAreaInfo().displayId).isEqualTo(DEFAULT_DISPLAY);
+ assertThat(tdaInfo.getDisplayAreaInfo().token)
+ .isEqualTo(wc.mRemoteToken.toWindowContainerToken());
+
+ final TaskDisplayArea tda = wc.asTaskDisplayArea();
+
+ assertThat(tda.getName()).isEqualTo(newTdaName);
+ assertThat(tda.mFeatureId).isEqualTo(tdaInfo.getDisplayAreaInfo().featureId);
+ assertThat(tda.mCreatedByOrganizer).isTrue();
+ assertThat(tda.mOrganizer).isEqualTo(organizer);
+ }
+
+ @Test
+ public void testCreateTaskDisplayArea_incrementalTdaFeatureId() {
+ final String newTdaName = "testTda";
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ final DisplayAreaAppearedInfo tdaInfo1 = mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName);
+ final DisplayAreaAppearedInfo tdaInfo2 = mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName);
+
+ // New created TDA has unique feature id starting from FEATURE_RUNTIME_TASK_CONTAINER_FIRST.
+ assertThat(tdaInfo1.getDisplayAreaInfo().featureId).isEqualTo(
+ FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+ assertThat(tdaInfo2.getDisplayAreaInfo().featureId).isEqualTo(
+ FEATURE_RUNTIME_TASK_CONTAINER_FIRST + 1);
+ }
+
+
+ @Test
+ public void testCreateTaskDisplayArea_invalidDisplayAndRoot() {
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ assertThrows(IllegalArgumentException.class, () ->
+ mOrganizerController.createTaskDisplayArea(
+ organizer, SystemServicesTestRule.sNextDisplayId + 1, FEATURE_ROOT,
+ "testTda"));
+ assertThrows(IllegalArgumentException.class, () ->
+ mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_ROOT - 1, "testTda"));
+ }
+
+ @Test
+ public void testDeleteTaskDisplayArea() {
+ final String newTdaName = "testTda";
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName);
+ final int tdaFeatureId = tdaInfo.getDisplayAreaInfo().featureId;
+
+ final TaskDisplayArea newTda = mDisplayContent.getItemFromDisplayAreas(
+ da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null);
+ spyOn(newTda);
+
+ mOrganizerController.deleteTaskDisplayArea(newTda.mRemoteToken.toWindowContainerToken());
+
+ verify(newTda).remove();
+ verify(newTda).removeImmediately();
+ assertThat(newTda.mOrganizer).isNull();
+ assertThat(newTda.isRemoved()).isTrue();
+
+ final TaskDisplayArea curTda = mDisplayContent.getItemFromDisplayAreas(
+ da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null);
+
+ assertThat(curTda).isNull();
+ }
+
+ @Test
+ public void testUnregisterOrganizer_deleteNewCreatedTaskDisplayArea() {
+ final String newTdaName = "testTda";
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName);
+ final int tdaFeatureId = tdaInfo.getDisplayAreaInfo().featureId;
+
+ final TaskDisplayArea newTda = mDisplayContent.getItemFromDisplayAreas(
+ da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null);
+ spyOn(newTda);
+
+ mOrganizerController.unregisterOrganizer(organizer);
+
+ verify(newTda).remove();
+ verify(newTda).removeImmediately();
+ assertThat(newTda.mOrganizer).isNull();
+ assertThat(newTda.isRemoved()).isTrue();
+
+ final TaskDisplayArea curTda = mDisplayContent.getItemFromDisplayAreas(
+ da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null);
+
+ assertThat(curTda).isNull();
+ }
+
+ @Test
+ public void testDeleteTaskDisplayArea_invalidTaskDisplayArea() {
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ assertThrows(IllegalArgumentException.class, () ->
+ mOrganizerController.deleteTaskDisplayArea(
+ tda.mRemoteToken.toWindowContainerToken()));
+ }
+
+ @Test
public void testAppearedVanished() throws RemoteException {
- IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST);
- unregisterMockOrganizer(organizer);
+ final IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST);
+ mOrganizerController.unregisterOrganizer(organizer);
verify(organizer).onDisplayAreaVanished(any());
}
@Test
public void testChanged() throws RemoteException {
- IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST);
+ final IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST);
mDisplayContent.setBounds(new Rect(0, 0, 1000, 1000));
verify(organizer).onDisplayAreaInfoChanged(any());
@@ -137,7 +258,7 @@
assertThat(mTestDisplayArea.mOrganizer).isNotNull();
- unregisterMockOrganizer(createMockOrganizer(binder));
+ mOrganizerController.unregisterOrganizer(createMockOrganizer(binder));
assertThat(mTestDisplayArea.mOrganizer).isNull();
}