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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user