diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index bc3220673ed39..8421faa249aaf 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; import android.annotation.Nullable; import android.os.Bundle; @@ -166,32 +167,48 @@ class DisplayAreaPolicyBuilder { return this; } - /** Makes sure the setting meets the requirement. */ + /** + * Makes sure the setting meets the requirement: + * 1. {@link mRootHierarchyBuilder} must be set. + * 2. {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids. + * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids. + * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container. + * 5. There must be exactly one {@link HierarchyBuilder} that contains the default + * {@link TaskDisplayArea} with id {@link FEATURE_DEFAULT_TASK_CONTAINER}. + * 6. None of the ids is greater than {@link FEATURE_VENDOR_LAST}. + */ private void validate() { if (mRootHierarchyBuilder == null) { throw new IllegalStateException("Root must be set for the display area policy."); } - final Set rootIdSet = new ArraySet<>(); - rootIdSet.add(mRootHierarchyBuilder.mRoot.mFeatureId); + final Set uniqueIdSet = new ArraySet<>(); + final Set allIdSet = new ArraySet<>(); + validateIds(mRootHierarchyBuilder, uniqueIdSet, allIdSet); 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); - } + validateIds(hierarchyBuilder, uniqueIdSet, allIdSet); + if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) { throw new IllegalStateException( "DisplayAreaGroup must contain at least one TaskDisplayArea."); } - containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null; + if (containsImeContainer) { + if (hierarchyBuilder.mImeContainer != null) { + throw new IllegalStateException( + "Only one DisplayArea hierarchy can contain the IME container"); + } + } else { + containsImeContainer = hierarchyBuilder.mImeContainer != null; + } + if (containsDefaultTda) { if (containsDefaultTaskDisplayArea(hierarchyBuilder)) { throw new IllegalStateException("Only one TaskDisplayArea can have the feature " - + "of FEATURE_DEFAULT_TASK_CONTAINER"); + + "id of FEATURE_DEFAULT_TASK_CONTAINER"); } } else { containsDefaultTda = containsDefaultTaskDisplayArea(hierarchyBuilder); @@ -203,7 +220,8 @@ class DisplayAreaPolicyBuilder { } if (!containsDefaultTda) { - throw new IllegalStateException("There must be a default TaskDisplayArea."); + throw new IllegalStateException("There must be a default TaskDisplayArea with id of " + + "FEATURE_DEFAULT_TASK_CONTAINER."); } } @@ -218,6 +236,67 @@ class DisplayAreaPolicyBuilder { return false; } + /** + * Makes sure that ids meet requirement. + * {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids. + * {@link Feature} below the same {@link RootDisplayArea} must have unique ids, but + * {@link Feature} below different {@link RootDisplayArea} can have the same id so that we can + * organize them together. + * None of the ids is greater than {@link FEATURE_VENDOR_LAST} + * + * @param uniqueIdSet ids of {@link RootDisplayArea} and {@link TaskDisplayArea} that must be + * unique, + * @param allIdSet ids of {@link RootDisplayArea}, {@link TaskDisplayArea} and {@link Feature}. + */ + private static void validateIds(HierarchyBuilder displayAreaHierarchy, + Set uniqueIdSet, Set allIdSet) { + // Root must have unique id. + final int rootId = displayAreaHierarchy.mRoot.mFeatureId; + if (!allIdSet.add(rootId) || !uniqueIdSet.add(rootId)) { + throw new IllegalStateException( + "RootDisplayArea must have unique id, but id=" + rootId + " is not unique."); + } + if (rootId > FEATURE_VENDOR_LAST) { + throw new IllegalStateException( + "RootDisplayArea should not have an id greater than FEATURE_VENDOR_LAST."); + } + + // TDAs must have unique id. + for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) { + final int taskDisplayAreaId = displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId; + if (!allIdSet.add(taskDisplayAreaId) || !uniqueIdSet.add(taskDisplayAreaId)) { + throw new IllegalStateException("TaskDisplayArea must have unique id, but id=" + + taskDisplayAreaId + " is not unique."); + } + if (taskDisplayAreaId > FEATURE_VENDOR_LAST) { + throw new IllegalStateException("TaskDisplayArea declared in the policy should not" + + "have an id greater than FEATURE_VENDOR_LAST."); + } + } + + // Features below the same root must have unique ids. + final Set featureIdSet = new ArraySet<>(); + for (int i = 0; i < displayAreaHierarchy.mFeatures.size(); i++) { + final int featureId = displayAreaHierarchy.mFeatures.get(i).getId(); + if (uniqueIdSet.contains(featureId)) { + throw new IllegalStateException("Feature must not have same id with any " + + "RootDisplayArea or TaskDisplayArea, but id=" + featureId + " is used"); + } + if (!featureIdSet.add(featureId)) { + throw new IllegalStateException("Feature below the same root must have unique id, " + + "but id=" + featureId + " is not unique."); + } + if (featureId > FEATURE_VENDOR_LAST) { + throw new IllegalStateException( + "Feature should not have an id greater than FEATURE_VENDOR_LAST."); + } + } + + // Features below different roots can have the same id so that we can organize them + // together. + allIdSet.addAll(featureIdSet); + } + Result build(WindowManagerService wmService) { validate(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 266ce5ba0e686..811ff4acb9954 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -32,6 +32,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; @@ -114,11 +115,13 @@ public class DisplayAreaPolicyBuilderTest { final Feature bar; DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) - .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0) + .addFeature(foo = new Feature.Builder(mPolicy, "Foo", + FEATURE_VENDOR_FIRST) .upTo(TYPE_STATUS_BAR) .and(TYPE_NAVIGATION_BAR) .build()) - .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1) + .addFeature(bar = new Feature.Builder(mPolicy, "Bar", + FEATURE_VENDOR_FIRST + 1) .all() .except(TYPE_STATUS_BAR) .build()) @@ -240,12 +243,14 @@ public class DisplayAreaPolicyBuilderTest { final Feature other; DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) - .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 0) + .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", + FEATURE_VENDOR_FIRST) .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) .build()) - .addFeature(other = new Feature.Builder(mPolicy, "Other", 1) + .addFeature(other = new Feature.Builder(mPolicy, "Other", + FEATURE_VENDOR_FIRST + 1) .all() .build()) .setImeContainer(mImeContainer) @@ -313,9 +318,7 @@ public class DisplayAreaPolicyBuilderTest { builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( mGroupRoot1) .setImeContainer(mImeContainer) - .setTaskDisplayAreas(Lists.newArrayList( - new TaskDisplayArea(mDisplayContent, mWms, "testTda", - FEATURE_VENDOR_FIRST + 1)))); + .setTaskDisplayAreas(Lists.newArrayList(mTda1))); assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); @@ -340,13 +343,146 @@ public class DisplayAreaPolicyBuilderTest { .setTaskDisplayAreas(mTaskDisplayAreaList)); builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( mGroupRoot2) - .setTaskDisplayAreas(Lists.newArrayList( - new TaskDisplayArea(mDisplayContent, mWms, "testTda", - FEATURE_VENDOR_FIRST + 1)))); + .setTaskDisplayAreas(Lists.newArrayList(mTda1))); builder4.build(mWms); } + @Test + public void testBuilder_rootHasUniqueId() { + // Root must have different id from all roots. + final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); + builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + final RootDisplayArea groupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", + mRoot.mFeatureId); + builder1.addDisplayAreaGroupHierarchy( + new DisplayAreaPolicyBuilder.HierarchyBuilder(groupRoot1) + .setTaskDisplayAreas(Lists.newArrayList(mTda1))); + + assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); + + // Root must have different id from all TDAs. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + mDefaultTaskDisplayArea, + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + mRoot.mFeatureId)))); + + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + + // Root must have different id from all features. + final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); + builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "testFeature", mRoot.mFeatureId) + .all() + .build())); + + assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); + } + + @Test + public void testBuilder_taskDisplayAreaHasUniqueId() { + // TDA must have different id from all TDAs. + final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); + final List tdaList = Lists.newArrayList( + mDefaultTaskDisplayArea, + mTda1, + new TaskDisplayArea(mDisplayContent, mWms, "tda2", mTda1.mFeatureId)); + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(tdaList)); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // TDA must have different id from all features. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + mDefaultTaskDisplayArea, + mTda1)) + .addFeature(new Feature.Builder(mPolicy, "testFeature", mTda1.mFeatureId) + .all() + .build())); + + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + } + + @Test + public void testBuilder_featureHasUniqueId() { + // Feature must have different id from features below the same root. + final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); + builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10) + .all() + .build()) + .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10) + .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) + .build())); + + assertThrows(IllegalStateException.class, () -> builder.build(mWms)); + + // Features below different root can have the same id. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10) + .all() + .build())); + builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( + mGroupRoot1) + .setTaskDisplayAreas(Lists.newArrayList(mTda1)) + .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10) + .all() + .build())); + + builder2.build(mWms); + } + + @Test + public void testBuilder_idsNotGreaterThanFeatureVendorLast() { + // Root id should not be greater than FEATURE_VENDOR_LAST. + final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); + final RootDisplayArea root = new SurfacelessDisplayAreaRoot(mWms, "testRoot", + FEATURE_VENDOR_LAST + 1); + builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList)); + + assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); + + // TDA id should not be greater than FEATURE_VENDOR_LAST. + final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); + builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(Lists.newArrayList( + mDefaultTaskDisplayArea, + new TaskDisplayArea(mDisplayContent, mWms, "testTda", + FEATURE_VENDOR_LAST + 1)))); + + assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); + + // Feature id should not be greater than FEATURE_VENDOR_LAST. + final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); + builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) + .setImeContainer(mImeContainer) + .setTaskDisplayAreas(mTaskDisplayAreaList) + .addFeature(new Feature.Builder(mPolicy, "testFeature", FEATURE_VENDOR_LAST + 1) + .all() + .build())); + + assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); + } + @Test public void testBuilder_displayAreaGroup_attachDisplayAreas() { final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()