Add API to create new TaskDisplayArea at runtime

Allow DA organizer to create new TaskDisplayArea.

Fix: 173455508
Test: atest WmTests:DisplayAreaOrganizerTest
Change-Id: I7d4e88f43ae14561720c6942ca0e9f82dc28e9f6
This commit is contained in:
Chris Li
2020-11-16 20:46:13 -08:00
parent 9a654b2886
commit dfeeea1b82
10 changed files with 447 additions and 37 deletions

View File

@@ -100,6 +100,19 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
*/
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.
@@ -131,6 +144,50 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
}
}
/**
* 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

View File

@@ -19,6 +19,7 @@ package android.window;
import android.content.pm.ParceledListSlice;
import android.window.DisplayAreaAppearedInfo;
import android.window.IDisplayAreaOrganizer;
import android.window.WindowContainerToken;
/** @hide */
interface IDisplayAreaOrganizerController {
@@ -37,4 +38,28 @@ interface IDisplayAreaOrganizerController {
* 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);
}

View File

@@ -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",

View File

@@ -380,6 +380,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return result;
}
@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,

View File

@@ -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.view.SurfaceControl;
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 @@ import java.util.List;
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 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
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 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
}
}
@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 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
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();
}
}
}

View File

@@ -172,10 +172,16 @@ class DisplayAreaPolicyBuilder {
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.");

View File

@@ -57,6 +57,11 @@ class RootDisplayArea extends DisplayArea<DisplayArea> {
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;

View File

@@ -157,12 +157,24 @@ final class TaskDisplayArea extends DisplayArea<Task> {
*/
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;
}
/**
@@ -1913,6 +1925,11 @@ final class TaskDisplayArea extends DisplayArea<Task> {
return true;
}
@Override
TaskDisplayArea asTaskDisplayArea() {
return this;
}
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
pw.println(prefix + "TaskDisplayArea " + getName());

View File

@@ -1814,7 +1814,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
/**
* 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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* 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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* 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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* 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.
@@ -1910,11 +1910,31 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return reduceOnAllTaskDisplayAreas(accumulator, initValue, true /* traverseTopToBottom */);
}
/**
* 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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* {@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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
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}

View File

@@ -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.mock;
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 @@ import java.util.List;
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 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
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 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
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.
@@ -107,17 +109,136 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
any(SurfaceControl.class));
}
@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 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
assertThat(mTestDisplayArea.mOrganizer).isNotNull();
unregisterMockOrganizer(createMockOrganizer(binder));
mOrganizerController.unregisterOrganizer(createMockOrganizer(binder));
assertThat(mTestDisplayArea.mOrganizer).isNull();
}